make ctrl-rubber-band-select do something closer to the right thing
[ardour.git] / gtk2_ardour / editor_ops.cc
1 /*
2     Copyright (C) 2000-2004 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <unistd.h>
22
23 #include <cstdlib>
24 #include <cmath>
25 #include <string>
26 #include <map>
27
28 #include <pbd/error.h>
29 #include <pbd/basename.h>
30 #include <pbd/pthread_utils.h>
31 #include <pbd/memento_command.h>
32
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/choice.h>
35
36 #include <ardour/audioengine.h>
37 #include <ardour/session.h>
38 #include <ardour/audioplaylist.h>
39 #include <ardour/audioregion.h>
40 #include <ardour/audio_diskstream.h>
41 #include <ardour/utils.h>
42 #include <ardour/location.h>
43 #include <ardour/named_selection.h>
44 #include <ardour/audio_track.h>
45 #include <ardour/audioplaylist.h>
46 #include <ardour/region_factory.h>
47 #include <ardour/playlist_factory.h>
48 #include <ardour/reverse.h>
49
50 #include "ardour_ui.h"
51 #include "editor.h"
52 #include "time_axis_view.h"
53 #include "audio_time_axis.h"
54 #include "automation_time_axis.h"
55 #include "streamview.h"
56 #include "audio_region_view.h"
57 #include "rgb_macros.h"
58 #include "selection_templates.h"
59 #include "selection.h"
60 #include "editing.h"
61 #include "gtk-custom-hruler.h"
62 #include "gui_thread.h"
63
64 #include "i18n.h"
65
66 using namespace std;
67 using namespace ARDOUR;
68 using namespace PBD;
69 using namespace sigc;
70 using namespace Gtk;
71 using namespace Editing;
72
73 /***********************************************************************
74   Editor operations
75  ***********************************************************************/
76
77 void
78 Editor::undo (uint32_t n)
79 {
80         if (session) {
81                 session->undo (n);
82         }
83 }
84
85 void
86 Editor::redo (uint32_t n)
87 {
88         if (session) {
89                 session->redo (n);
90         }
91 }
92
93 int
94 Editor::ensure_cursor (nframes_t *pos)
95 {
96         *pos = edit_cursor->current_frame;
97         return 0;
98 }
99
100 void
101 Editor::split_region ()
102 {
103         split_region_at (edit_cursor->current_frame);
104 }
105
106 void
107 Editor::split_region_at (nframes_t where)
108 {
109         split_regions_at (where, selection->regions);
110 }
111
112 void
113 Editor::split_regions_at (nframes_t where, RegionSelection& regions)
114 {
115         begin_reversible_command (_("split"));
116
117         snap_to (where);
118         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
119
120                 RegionSelection::iterator tmp;
121                 
122                 tmp = a;
123                 ++tmp;
124
125                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
126
127                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
128                 if (arv)
129                         _new_regionviews_show_envelope = arv->envelope_visible();
130                 
131                 if (pl) {
132                         XMLNode &before = pl->get_state();
133                         pl->split_region ((*a)->region(), where);
134                         XMLNode &after = pl->get_state();
135                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
136                 }
137
138                 a = tmp;
139     }
140
141         commit_reversible_command ();
142         _new_regionviews_show_envelope = false;
143 }
144
145 void
146 Editor::remove_clicked_region ()
147 {
148         if (clicked_audio_trackview == 0 || clicked_regionview == 0) {
149                 return;
150         }
151
152         boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
153         
154         begin_reversible_command (_("remove region"));
155         XMLNode &before = playlist->get_state();
156         playlist->remove_region (clicked_regionview->region());
157         XMLNode &after = playlist->get_state();
158         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
159         commit_reversible_command ();
160 }
161
162 void
163 Editor::destroy_clicked_region ()
164 {
165         uint32_t selected = selection->regions.size();
166
167         if (!session || !selected) {
168                 return;
169         }
170
171         vector<string> choices;
172         string prompt;
173         
174         prompt  = string_compose (_(" This is destructive, will possibly delete audio files\n\
175 It cannot be undone\n\
176 Do you really want to destroy %1 ?"),
177                            (selected > 1 ? 
178                             _("these regions") : _("this region")));
179
180         choices.push_back (_("No, do nothing."));
181
182         if (selected > 1) {
183                 choices.push_back (_("Yes, destroy them."));
184         } else {
185                 choices.push_back (_("Yes, destroy it."));
186         }
187
188         Gtkmm2ext::Choice prompter (prompt, choices);
189         
190         if (prompter.run() == 0) { /* first choice */
191                 return;
192         }
193
194         if (selected) {
195                 list<boost::shared_ptr<Region> > r;
196
197                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
198                         r.push_back ((*i)->region());
199                 }
200
201                 session->destroy_regions (r);
202         } 
203 }
204
205 boost::shared_ptr<Region>
206 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
207 {
208         RegionView* rv;
209         boost::shared_ptr<Region> region;
210         nframes_t start = 0;
211
212         if (selection->time.start () == selection->time.end_frame ()) {
213                 
214                 /* no current selection-> is there a selected regionview? */
215
216                 if (selection->regions.empty()) {
217                         return region;
218                 }
219
220         } 
221
222         if (!selection->regions.empty()) {
223
224                 rv = *(selection->regions.begin());
225                 (*tv) = &rv->get_time_axis_view();
226                 region = rv->region();
227
228         } else if (!selection->tracks.empty()) {
229
230                 (*tv) = selection->tracks.front();
231
232                 RouteTimeAxisView* rtv;
233
234                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
235                         boost::shared_ptr<Playlist> pl;
236                         
237                         if ((pl = rtv->playlist()) == 0) {
238                                 return region;
239                         }
240                         
241                         region = pl->top_region_at (start);
242                 }
243         } 
244         
245         return region;
246 }
247         
248 void
249 Editor::extend_selection_to_end_of_region (bool next)
250 {
251         TimeAxisView *tv;
252         boost::shared_ptr<Region> region;
253         nframes_t start;
254
255         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
256                 return;
257         }
258
259         if (region && selection->time.start () == selection->time.end_frame ()) {
260                 start = region->position();
261         } else {
262                 start = selection->time.start ();
263         }
264
265         /* Try to leave the selection with the same route if possible */
266
267         if ((tv = selection->time.track) == 0) {
268                 return;
269         }
270
271         begin_reversible_command (_("extend selection"));
272         selection->set (tv, start, region->position() + region->length());
273         commit_reversible_command ();
274 }
275
276 void
277 Editor::extend_selection_to_start_of_region (bool previous)
278 {
279         TimeAxisView *tv;
280         boost::shared_ptr<Region> region;
281         nframes_t end;
282
283         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
284                 return;
285         }
286
287         if (region && selection->time.start () == selection->time.end_frame ()) {
288                 end = region->position() + region->length();
289         } else {
290                 end = selection->time.end_frame ();
291         }
292
293         /* Try to leave the selection with the same route if possible */
294         
295         if ((tv = selection->time.track) == 0) {
296                 return;
297         }
298
299         begin_reversible_command (_("extend selection"));
300         selection->set (tv, region->position(), end);
301         commit_reversible_command ();
302 }
303
304
305 void
306 Editor::nudge_forward (bool next)
307 {
308         nframes_t distance;
309         nframes_t next_distance;
310
311         if (!session) return;
312         
313         if (!selection->regions.empty()) {
314
315                 begin_reversible_command (_("nudge forward"));
316
317                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
318                         boost::shared_ptr<Region> r ((*i)->region());
319                         
320                         distance = get_nudge_distance (r->position(), next_distance);
321
322                         if (next) {
323                                 distance = next_distance;
324                         }
325
326                         XMLNode &before = r->playlist()->get_state();
327                         r->set_position (r->position() + distance, this);
328                         XMLNode &after = r->playlist()->get_state();
329                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
330                 }
331
332                 commit_reversible_command ();
333
334         } else {
335                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
336                 session->request_locate (playhead_cursor->current_frame + distance);
337         }
338 }
339                 
340 void
341 Editor::nudge_backward (bool next)
342 {
343         nframes_t distance;
344         nframes_t next_distance;
345
346         if (!session) return;
347         
348         if (!selection->regions.empty()) {
349
350                 begin_reversible_command (_("nudge forward"));
351
352                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
353                         boost::shared_ptr<Region> r ((*i)->region());
354
355                         distance = get_nudge_distance (r->position(), next_distance);
356                         
357                         if (next) {
358                                 distance = next_distance;
359                         }
360
361                         XMLNode &before = r->playlist()->get_state();
362                         
363                         if (r->position() > distance) {
364                                 r->set_position (r->position() - distance, this);
365                         } else {
366                                 r->set_position (0, this);
367                         }
368                         XMLNode &after = r->playlist()->get_state();
369                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
370                 }
371
372                 commit_reversible_command ();
373
374         } else {
375
376                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
377
378                 if (playhead_cursor->current_frame > distance) {
379                         session->request_locate (playhead_cursor->current_frame - distance);
380                 } else {
381                         session->goto_start();
382                 }
383         }
384 }
385
386 void
387 Editor::nudge_forward_capture_offset ()
388 {
389         nframes_t distance;
390
391         if (!session) return;
392         
393         if (!selection->regions.empty()) {
394
395                 begin_reversible_command (_("nudge forward"));
396
397                 distance = session->worst_output_latency();
398
399                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
400                         boost::shared_ptr<Region> r ((*i)->region());
401                         
402                         XMLNode &before = r->playlist()->get_state();
403                         r->set_position (r->position() + distance, this);
404                         XMLNode &after = r->playlist()->get_state();
405                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
406                 }
407
408                 commit_reversible_command ();
409
410         } 
411 }
412                 
413 void
414 Editor::nudge_backward_capture_offset ()
415 {
416         nframes_t distance;
417
418         if (!session) return;
419         
420         if (!selection->regions.empty()) {
421
422                 begin_reversible_command (_("nudge forward"));
423
424                 distance = session->worst_output_latency();
425
426                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
427                         boost::shared_ptr<Region> r ((*i)->region());
428
429                         XMLNode &before = r->playlist()->get_state();
430                         
431                         if (r->position() > distance) {
432                                 r->set_position (r->position() - distance, this);
433                         } else {
434                                 r->set_position (0, this);
435                         }
436                         XMLNode &after = r->playlist()->get_state();
437                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
438                 }
439
440                 commit_reversible_command ();
441         }
442 }
443
444 /* DISPLAY MOTION */
445
446 void
447 Editor::move_to_start ()
448 {
449         session->goto_start ();
450 }
451
452 void
453 Editor::move_to_end ()
454 {
455
456         session->request_locate (session->current_end_frame());
457 }
458
459 void
460 Editor::build_region_boundary_cache ()
461 {
462         nframes_t pos = 0;
463         RegionPoint point;
464         boost::shared_ptr<Region> r;
465         TrackViewList tracks;
466
467         region_boundary_cache.clear ();
468
469         if (session == 0) {
470                 return;
471         }
472         
473         switch (snap_type) {
474         case SnapToRegionStart:
475                 point = Start;
476                 break;
477         case SnapToRegionEnd:
478                 point = End;
479                 break;  
480         case SnapToRegionSync:
481                 point = SyncPoint;
482                 break;  
483         case SnapToRegionBoundary:
484                 point = Start;
485                 break;  
486         default:
487                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
488                 /*NOTREACHED*/
489                 return;
490         }
491         
492         TimeAxisView *ontrack = 0;
493
494         while (pos < session->current_end_frame()) {
495
496                 if (!selection->tracks.empty()) {
497
498                         if ((r = find_next_region (pos, point, 1, selection->tracks, &ontrack)) == 0) {
499                                 break;
500                         }
501
502                 } else if (clicked_trackview) {
503
504                         TrackViewList t;
505                         t.push_back (clicked_trackview);
506
507                         if ((r = find_next_region (pos, point, 1, t, &ontrack)) == 0) {
508                                 break;
509                         }
510
511                 } else {
512
513                         if ((r = find_next_region (pos, point, 1, track_views, &ontrack)) == 0) {
514                                 break;
515                         }
516                 }
517
518                 nframes_t rpos;
519                 
520                 switch (snap_type) {
521                 case SnapToRegionStart:
522                         rpos = r->first_frame();
523                         break;
524                 case SnapToRegionEnd:
525                         rpos = r->last_frame();
526                         break;  
527                 case SnapToRegionSync:
528                         rpos = r->adjust_to_sync (r->first_frame());
529                         break;
530
531                 case SnapToRegionBoundary:
532                         rpos = r->last_frame();
533                         break;  
534                 default:
535                         break;
536                 }
537                 
538                 float speed = 1.0f;
539                 AudioTimeAxisView *atav;
540
541                 if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
542                         if (atav->get_diskstream() != 0) {
543                                 speed = atav->get_diskstream()->speed();
544                         }
545                 }
546
547                 rpos = track_frame_to_session_frame(rpos, speed);
548
549                 if (region_boundary_cache.empty() || rpos != region_boundary_cache.back()) {
550                         if (snap_type == SnapToRegionBoundary) {
551                                 region_boundary_cache.push_back (r->first_frame());
552                         }
553                         region_boundary_cache.push_back (rpos);
554                 }
555
556                 pos = rpos + 1;
557         }
558 }
559
560 boost::shared_ptr<Region>
561 Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
562 {
563         TrackViewList::iterator i;
564         nframes_t closest = max_frames;
565         boost::shared_ptr<Region> ret;
566         nframes_t rpos = 0;
567
568         float track_speed;
569         nframes_t track_frame;
570         AudioTimeAxisView *atav;
571
572         for (i = tracks.begin(); i != tracks.end(); ++i) {
573
574                 nframes_t distance;
575                 boost::shared_ptr<Region> r;
576                 
577                 track_speed = 1.0f;
578                 if ( (atav = dynamic_cast<AudioTimeAxisView*>(*i)) != 0 ) {
579                         if (atav->get_diskstream()!=0)
580                                 track_speed = atav->get_diskstream()->speed();
581                 }
582
583                 track_frame = session_frame_to_track_frame(frame, track_speed);
584
585                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
586                         continue;
587                 }
588
589                 switch (point) {
590                 case Start:
591                         rpos = r->first_frame ();
592                         break;
593
594                 case End:
595                         rpos = r->last_frame ();
596                         break;
597
598                 case SyncPoint:
599                         rpos = r->adjust_to_sync (r->first_frame());
600                         break;
601                 }
602                 // rpos is a "track frame", converting it to "session frame"
603                 rpos = track_frame_to_session_frame(rpos, track_speed);
604
605                 if (rpos > frame) {
606                         distance = rpos - frame;
607                 } else {
608                         distance = frame - rpos;
609                 }
610
611                 if (distance < closest) {
612                         closest = distance;
613                         if (ontrack != 0)
614                                 *ontrack = (*i);
615                         ret = r;
616                 }
617         }
618
619         return ret;
620 }
621
622 void
623 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
624 {
625         boost::shared_ptr<Region> r;
626         nframes_t pos = cursor->current_frame;
627
628         if (!session) {
629                 return;
630         }
631
632         TimeAxisView *ontrack = 0;
633
634         // so we don't find the current region again..
635         if (dir>0 || pos>0)
636                 pos+=dir;
637
638         if (!selection->tracks.empty()) {
639                 
640                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
641                 
642         } else if (clicked_trackview) {
643                 
644                 TrackViewList t;
645                 t.push_back (clicked_trackview);
646                 
647                 r = find_next_region (pos, point, dir, t, &ontrack);
648                 
649         } else {
650                 
651                 r = find_next_region (pos, point, dir, track_views, &ontrack);
652         }
653
654         if (r == 0) {
655                 return;
656         }
657         
658         switch (point){
659         case Start:
660                 pos = r->first_frame ();
661                 break;
662
663         case End:
664                 pos = r->last_frame ();
665                 break;
666
667         case SyncPoint:
668                 pos = r->adjust_to_sync (r->first_frame());
669                 break;  
670         }
671         
672         float speed = 1.0f;
673         AudioTimeAxisView *atav;
674
675         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
676                 if (atav->get_diskstream() != 0) {
677                         speed = atav->get_diskstream()->speed();
678                 }
679         }
680
681         pos = track_frame_to_session_frame(pos, speed);
682         
683         if (cursor == playhead_cursor) {
684                 session->request_locate (pos);
685         } else {
686                 cursor->set_position (pos);
687         }
688 }
689
690 void
691 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
692 {
693         cursor_to_region_point (cursor, point, 1);
694 }
695
696 void
697 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
698 {
699         cursor_to_region_point (cursor, point, -1);
700 }
701
702 void
703 Editor::cursor_to_selection_start (Cursor *cursor)
704 {
705         nframes_t pos = 0;
706         switch (mouse_mode) {
707         case MouseObject:
708                 if (!selection->regions.empty()) {
709                         pos = selection->regions.start();
710                 }
711                 break;
712
713         case MouseRange:
714                 if (!selection->time.empty()) {
715                         pos = selection->time.start ();
716                 }
717                 break;
718
719         default:
720                 return;
721         }
722
723         if (cursor == playhead_cursor) {
724                 session->request_locate (pos);
725         } else {
726                 cursor->set_position (pos);
727         }
728 }
729
730 void
731 Editor::cursor_to_selection_end (Cursor *cursor)
732 {
733         nframes_t pos = 0;
734
735         switch (mouse_mode) {
736         case MouseObject:
737                 if (!selection->regions.empty()) {
738                         pos = selection->regions.end_frame();
739                 }
740                 break;
741
742         case MouseRange:
743                 if (!selection->time.empty()) {
744                         pos = selection->time.end_frame ();
745                 }
746                 break;
747
748         default:
749                 return;
750         }
751
752         if (cursor == playhead_cursor) {
753                 session->request_locate (pos);
754         } else {
755                 cursor->set_position (pos);
756         }
757 }
758
759 void
760 Editor::scroll_playhead (bool forward)
761 {
762         nframes_t pos = playhead_cursor->current_frame;
763         nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
764
765         if (forward) {
766                 if (pos == max_frames) {
767                         return;
768                 }
769
770                 if (pos < max_frames - delta) {
771                         pos += delta ;
772                 } else {
773                         pos = max_frames;
774                 } 
775
776         } else {
777
778                 if (pos == 0) {
779                         return;
780                 } 
781
782                 if (pos > delta) {
783                         pos -= delta;
784                 } else {
785                         pos = 0;
786                 }
787         }
788
789         session->request_locate (pos);
790 }
791
792 void
793 Editor::playhead_backward ()
794 {
795         nframes_t pos;
796         nframes_t cnt;
797         float prefix;
798         bool was_floating;
799
800         if (get_prefix (prefix, was_floating)) {
801                 cnt = 1;
802         } else {
803                 if (was_floating) {
804                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
805                 } else {
806                         cnt = (nframes_t) prefix;
807                 }
808         }
809
810         pos = playhead_cursor->current_frame;
811
812         if ((nframes_t) pos < cnt) {
813                 pos = 0;
814         } else {
815                 pos -= cnt;
816         }
817         
818         /* XXX this is completely insane. with the current buffering
819            design, we'll force a complete track buffer flush and
820            reload, just to move 1 sample !!!
821         */
822
823         session->request_locate (pos);
824 }
825
826 void
827 Editor::playhead_forward ()
828 {
829         nframes_t pos;
830         nframes_t cnt;
831         bool was_floating;
832         float prefix;
833
834         if (get_prefix (prefix, was_floating)) {
835                 cnt = 1;
836         } else {
837                 if (was_floating) {
838                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
839                 } else {
840                         cnt = (nframes_t) floor (prefix);
841                 }
842         }
843
844         pos = playhead_cursor->current_frame;
845         
846         /* XXX this is completely insane. with the current buffering
847            design, we'll force a complete track buffer flush and
848            reload, just to move 1 sample !!!
849         */
850
851         session->request_locate (pos+cnt);
852 }
853
854 void
855 Editor::cursor_align (bool playhead_to_edit)
856 {
857         if (playhead_to_edit) {
858                 if (session) {
859                         session->request_locate (edit_cursor->current_frame);
860                 }
861         } else {
862                 edit_cursor->set_position (playhead_cursor->current_frame);
863         }
864 }
865
866 void
867 Editor::edit_cursor_backward ()
868 {
869         nframes_t pos;
870         nframes_t cnt;
871         float prefix;
872         bool was_floating;
873
874         if (get_prefix (prefix, was_floating)) {
875                 cnt = 1;
876         } else {
877                 if (was_floating) {
878                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
879                 } else {
880                         cnt = (nframes_t) prefix;
881                 }
882         }
883
884         pos = edit_cursor->current_frame;
885
886         if ((nframes_t) pos < cnt) {
887                 pos = 0;
888         } else {
889                 pos -= cnt;
890         }
891         
892         edit_cursor->set_position (pos);
893 }
894
895 void
896 Editor::edit_cursor_forward ()
897 {
898         nframes_t pos;
899         nframes_t cnt;
900         bool was_floating;
901         float prefix;
902
903         if (get_prefix (prefix, was_floating)) {
904                 cnt = 1;
905         } else {
906                 if (was_floating) {
907                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
908                 } else {
909                         cnt = (nframes_t) floor (prefix);
910                 }
911         }
912
913         pos = edit_cursor->current_frame;
914         edit_cursor->set_position (pos+cnt);
915 }
916
917 void
918 Editor::goto_frame ()
919 {
920         float prefix;
921         bool was_floating;
922         nframes_t frame;
923
924         if (get_prefix (prefix, was_floating)) {
925                 return;
926         }
927
928         if (was_floating) {
929                 frame = (nframes_t) floor (prefix * session->frame_rate());
930         } else {
931                 frame = (nframes_t) floor (prefix);
932         }
933
934         session->request_locate (frame);
935 }
936
937 void
938 Editor::scroll_backward (float pages)
939 {
940         nframes_t frame;
941         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
942         bool was_floating;
943         float prefix;
944         nframes_t cnt;
945         
946         if (get_prefix (prefix, was_floating)) {
947                 cnt = (nframes_t) floor (pages * one_page);
948         } else {
949                 if (was_floating) {
950                         cnt = (nframes_t) floor (prefix * session->frame_rate());
951                 } else {
952                         cnt = (nframes_t) floor (prefix * one_page);
953                 }
954         }
955
956         if (leftmost_frame < cnt) {
957                 frame = 0;
958         } else {
959                 frame = leftmost_frame - cnt;
960         }
961
962         reset_x_origin (frame);
963 }
964
965 void
966 Editor::scroll_forward (float pages)
967 {
968         nframes_t frame;
969         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
970         bool was_floating;
971         float prefix;
972         nframes_t cnt;
973         
974         if (get_prefix (prefix, was_floating)) {
975                 cnt = (nframes_t) floor (pages * one_page);
976         } else {
977                 if (was_floating) {
978                         cnt = (nframes_t) floor (prefix * session->frame_rate());
979                 } else {
980                         cnt = (nframes_t) floor (prefix * one_page);
981                 }
982         }
983
984         if (max_frames - cnt < leftmost_frame) {
985                 frame = max_frames - cnt;
986         } else {
987                 frame = leftmost_frame + cnt;
988         }
989
990         reset_x_origin (frame);
991 }
992
993 void
994 Editor::scroll_tracks_down ()
995 {
996         float prefix;
997         bool was_floating;
998         int cnt;
999
1000         if (get_prefix (prefix, was_floating)) {
1001                 cnt = 1;
1002         } else {
1003                 cnt = (int) floor (prefix);
1004         }
1005
1006         double vert_value = vertical_adjustment.get_value() + (cnt *
1007                 vertical_adjustment.get_page_size());
1008         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1009                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1010         }
1011         vertical_adjustment.set_value (vert_value);
1012 }
1013
1014 void
1015 Editor::scroll_tracks_up ()
1016 {
1017         float prefix;
1018         bool was_floating;
1019         int cnt;
1020
1021         if (get_prefix (prefix, was_floating)) {
1022                 cnt = 1;
1023         } else {
1024                 cnt = (int) floor (prefix);
1025         }
1026
1027         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1028 }
1029
1030 void
1031 Editor::scroll_tracks_down_line ()
1032 {
1033
1034         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1035         double vert_value = adj->get_value() + 20;
1036
1037         if (vert_value>adj->get_upper() - canvas_height) {
1038                 vert_value = adj->get_upper() - canvas_height;
1039         }
1040         adj->set_value (vert_value);
1041 }
1042
1043 void
1044 Editor::scroll_tracks_up_line ()
1045 {
1046         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1047         adj->set_value (adj->get_value() - 20);
1048 }
1049
1050 /* ZOOM */
1051
1052 void
1053 Editor::temporal_zoom_step (bool coarser)
1054 {
1055         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1056
1057         double nfpu;
1058
1059         nfpu = frames_per_unit;
1060         
1061         if (coarser) { 
1062                 nfpu *= 1.61803399;
1063         } else { 
1064                 nfpu = max(1.0,(nfpu/1.61803399));
1065         }
1066
1067         temporal_zoom (nfpu);
1068 }       
1069
1070 void
1071 Editor::temporal_zoom (gdouble fpu)
1072 {
1073         if (!session) return;
1074         
1075         nframes_t current_page = current_page_frames();
1076         nframes_t current_leftmost = leftmost_frame;
1077         nframes_t current_rightmost;
1078         nframes_t current_center;
1079         nframes_t new_page;
1080         nframes_t leftmost_after_zoom = 0;
1081         double nfpu;
1082
1083         nfpu = fpu;
1084         
1085         new_page = (nframes_t) floor (canvas_width * nfpu);
1086
1087         switch (zoom_focus) {
1088         case ZoomFocusLeft:
1089                 leftmost_after_zoom = current_leftmost;
1090                 break;
1091                 
1092         case ZoomFocusRight:
1093                 current_rightmost = leftmost_frame + current_page;
1094                 if (current_rightmost > new_page) {
1095                         leftmost_after_zoom = current_rightmost - new_page;
1096                 } else {
1097                         leftmost_after_zoom = 0;
1098                 }
1099                 break;
1100                 
1101         case ZoomFocusCenter:
1102                 current_center = current_leftmost + (current_page/2); 
1103                 if (current_center > (new_page/2)) {
1104                         leftmost_after_zoom = current_center - (new_page / 2);
1105                 } else {
1106                         leftmost_after_zoom = 0;
1107                 }
1108                 break;
1109                 
1110         case ZoomFocusPlayhead:
1111                 /* try to keep the playhead in the center */
1112                 if (playhead_cursor->current_frame > new_page/2) {
1113                         leftmost_after_zoom = playhead_cursor->current_frame - (new_page/2);
1114                 } else {
1115                         leftmost_after_zoom = 0;
1116                 }
1117                 break;
1118
1119         case ZoomFocusEdit:
1120                 /* try to keep the edit cursor in the center */
1121                 if (edit_cursor->current_frame > new_page/2) {
1122                         leftmost_after_zoom = edit_cursor->current_frame - (new_page/2);
1123                 } else {
1124                         leftmost_after_zoom = 0;
1125                 }
1126                 break;
1127                 
1128         }
1129  
1130         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1131
1132 //      begin_reversible_command (_("zoom"));
1133 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), current_leftmost, frames_per_unit));
1134 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_after_zoom, nfpu));
1135 //      commit_reversible_command ();
1136
1137         reposition_and_zoom (leftmost_after_zoom, nfpu);
1138 }       
1139
1140 void
1141 Editor::temporal_zoom_selection ()
1142 {
1143         if (!selection) return;
1144         
1145         if (selection->time.empty()) {
1146                 return;
1147         }
1148
1149         nframes_t start = selection->time[clicked_selection].start;
1150         nframes_t end = selection->time[clicked_selection].end;
1151
1152         temporal_zoom_by_frame (start, end, "zoom to selection");
1153 }
1154
1155 void
1156 Editor::temporal_zoom_session ()
1157 {
1158         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session));
1159
1160         if (session) {
1161                 temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session");
1162         }
1163 }
1164
1165 void
1166 Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & op)
1167 {
1168         if (!session) return;
1169
1170         if ((start == 0 && end == 0) || end < start) {
1171                 return;
1172         }
1173
1174         nframes_t range = end - start;
1175
1176         double new_fpu = (double)range / (double)canvas_width;
1177 //      double p2 = 1.0;
1178
1179 //      while (p2 < new_fpu) {
1180 //              p2 *= 2.0;
1181 //      }
1182 //      new_fpu = p2;
1183         
1184         nframes_t new_page = (nframes_t) floor (canvas_width * new_fpu);
1185         nframes_t middle = (nframes_t) floor( (double)start + ((double)range / 2.0f ));
1186         nframes_t new_leftmost = (nframes_t) floor( (double)middle - ((double)new_page/2.0f));
1187
1188         if (new_leftmost > middle) new_leftmost = 0;
1189
1190 //      begin_reversible_command (op);
1191 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1192 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1193 //      commit_reversible_command ();
1194
1195         reposition_and_zoom (new_leftmost, new_fpu);
1196 }
1197
1198 void 
1199 Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame)
1200 {
1201         if (!session) return;
1202         
1203         double range_before = frame - leftmost_frame;
1204         double new_fpu;
1205         
1206         new_fpu = frames_per_unit;
1207         
1208         if (coarser) { 
1209                 new_fpu *= 1.61803399;
1210                 range_before *= 1.61803399;
1211         } else { 
1212                 new_fpu = max(1.0,(new_fpu/1.61803399));
1213                 range_before /= 1.61803399;
1214         }
1215
1216         if (new_fpu == frames_per_unit) return;
1217
1218         nframes_t new_leftmost = frame - (nframes_t)range_before;
1219
1220         if (new_leftmost > frame) new_leftmost = 0;
1221
1222 //      begin_reversible_command (_("zoom to frame"));
1223 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1224 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1225 //      commit_reversible_command ();
1226
1227         reposition_and_zoom (new_leftmost, new_fpu);
1228 }
1229
1230 void
1231 Editor::add_location_from_selection ()
1232 {
1233         string rangename;
1234
1235         if (selection->time.empty()) {
1236                 return;
1237         }
1238
1239         if (session == 0 || clicked_trackview == 0) {
1240                 return;
1241         }
1242
1243         nframes_t start = selection->time[clicked_selection].start;
1244         nframes_t end = selection->time[clicked_selection].end;
1245
1246         session->locations()->next_available_name(rangename,"selection");
1247         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1248
1249         session->begin_reversible_command (_("add marker"));
1250         XMLNode &before = session->locations()->get_state();
1251         session->locations()->add (location, true);
1252         XMLNode &after = session->locations()->get_state();
1253         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1254         session->commit_reversible_command ();
1255 }
1256
1257 void
1258 Editor::add_location_from_playhead_cursor ()
1259 {
1260         string markername;
1261
1262         nframes_t where = session->audible_frame();
1263         
1264         session->locations()->next_available_name(markername,"mark");
1265         Location *location = new Location (where, where, markername, Location::IsMark);
1266         session->begin_reversible_command (_("add marker"));
1267         XMLNode &before = session->locations()->get_state();
1268         session->locations()->add (location, true);
1269         XMLNode &after = session->locations()->get_state();
1270         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1271         session->commit_reversible_command ();
1272 }
1273
1274 void
1275 Editor::add_location_from_audio_region ()
1276 {
1277         if (selection->regions.empty()) {
1278                 return;
1279         }
1280
1281         RegionView* rv = *(selection->regions.begin());
1282         boost::shared_ptr<Region> region = rv->region();
1283         
1284         Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1285         session->begin_reversible_command (_("add marker"));
1286         XMLNode &before = session->locations()->get_state();
1287         session->locations()->add (location, true);
1288         XMLNode &after = session->locations()->get_state();
1289         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1290         session->commit_reversible_command ();
1291 }
1292
1293 void
1294 Editor::select_all_in_track (Selection::Operation op)
1295 {
1296         list<Selectable *> touched;
1297
1298         if (!clicked_trackview) {
1299                 return;
1300         }
1301         
1302         clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
1303
1304         switch (op) {
1305         case Selection::Toggle:
1306                 selection->add (touched);
1307                 break;
1308         case Selection::Set:
1309                 selection->set (touched);
1310                 break;
1311         case Selection::Extend:
1312                 /* not defined yet */
1313                 break;
1314         case Selection::Add:
1315                 selection->add (touched);
1316                 break;
1317         }
1318 }
1319
1320 void
1321 Editor::select_all (Selection::Operation op)
1322 {
1323         list<Selectable *> touched;
1324         
1325         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1326                 if ((*iter)->hidden()) {
1327                         continue;
1328                 }
1329                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
1330         }
1331         begin_reversible_command (_("select all"));
1332         switch (op) {
1333         case Selection::Add:
1334         case Selection::Toggle:
1335                 selection->add (touched);
1336                 break;
1337         case Selection::Set:
1338                 selection->set (touched);
1339                 break;
1340         case Selection::Extend:
1341                 /* not defined yet */
1342                 break;
1343         }
1344         commit_reversible_command ();
1345 }
1346
1347 void
1348 Editor::invert_selection_in_track ()
1349 {
1350         list<Selectable *> touched;
1351
1352         if (!clicked_trackview) {
1353                 return;
1354         }
1355         
1356         clicked_trackview->get_inverted_selectables (*selection, touched);
1357         selection->set (touched);
1358 }
1359
1360 void
1361 Editor::invert_selection ()
1362 {
1363         list<Selectable *> touched;
1364         
1365         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1366                 if ((*iter)->hidden()) {
1367                         continue;
1368                 }
1369                 (*iter)->get_inverted_selectables (*selection, touched);
1370         }
1371
1372         selection->set (touched);
1373 }
1374
1375 bool
1376 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, Selection::Operation op)
1377 {
1378         list<Selectable*> touched;
1379         list<Selectable*>::size_type n = 0;
1380         TrackViewList touched_tracks;
1381
1382         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1383                 if ((*iter)->hidden()) {
1384                         continue;
1385                 }
1386
1387                 n = touched.size();
1388
1389                 (*iter)->get_selectables (start, end, top, bot, touched);
1390
1391                 if (n != touched.size()) {
1392                         touched_tracks.push_back (*iter);
1393                 }
1394         }
1395
1396         if (!touched_tracks.empty()) {
1397                 switch (op) {
1398                 case Selection::Add:
1399                         selection->add (touched_tracks);
1400                         break;
1401                 case Selection::Toggle:
1402                         selection->toggle (touched_tracks);
1403                         break;
1404                 case Selection::Set:
1405                         selection->set (touched_tracks);
1406                         break;
1407                 case Selection::Extend:
1408                         /* not defined yet */
1409                         break;
1410                 }
1411         }
1412                 
1413         begin_reversible_command (_("select all within"));
1414         switch (op) {
1415         case Selection::Add:
1416                 selection->add (touched);
1417                 break;
1418         case Selection::Toggle:
1419                 selection->toggle (touched);
1420                 break;
1421         case Selection::Set:
1422                 selection->set (touched);
1423                 break;
1424         case Selection::Extend:
1425                 /* not defined yet */
1426                 break;
1427         }
1428
1429         commit_reversible_command ();
1430         return !touched.empty();
1431 }
1432
1433 void
1434 Editor::set_selection_from_audio_region ()
1435 {
1436         if (selection->regions.empty()) {
1437                 return;
1438         }
1439
1440         RegionView* rv = *(selection->regions.begin());
1441         boost::shared_ptr<Region> region = rv->region();
1442         
1443         begin_reversible_command (_("set selection from region"));
1444         selection->set (0, region->position(), region->last_frame());
1445         commit_reversible_command ();
1446
1447         set_mouse_mode (Editing::MouseRange, false);
1448 }
1449
1450 void
1451 Editor::set_selection_from_punch()
1452 {
1453         Location* location;
1454
1455         if ((location = session->locations()->auto_punch_location()) == 0)  {
1456                 return;
1457         }
1458
1459         set_selection_from_range (*location);
1460 }
1461
1462 void
1463 Editor::set_selection_from_loop()
1464 {
1465         Location* location;
1466
1467         if ((location = session->locations()->auto_loop_location()) == 0)  {
1468                 return;
1469         }
1470         set_selection_from_range (*location);
1471 }
1472
1473 void
1474 Editor::set_selection_from_range (Location& loc)
1475 {
1476         begin_reversible_command (_("set selection from range"));
1477         selection->set (0, loc.start(), loc.end());
1478         commit_reversible_command ();
1479
1480         set_mouse_mode (Editing::MouseRange, false);
1481 }
1482
1483 void
1484 Editor::select_all_selectables_using_time_selection ()
1485 {
1486         list<Selectable *> touched;
1487
1488         if (selection->time.empty()) {
1489                 return;
1490         }
1491
1492         nframes_t start = selection->time[clicked_selection].start;
1493         nframes_t end = selection->time[clicked_selection].end;
1494
1495         if (end - start < 1)  {
1496                 return;
1497         }
1498
1499         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
1500                 if ((*iter)->hidden()) {
1501                         continue;
1502                 }
1503                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1504         }
1505
1506         begin_reversible_command (_("select all from range"));
1507         selection->set (touched);
1508         commit_reversible_command ();
1509 }
1510
1511
1512 void
1513 Editor::select_all_selectables_using_punch()
1514 {
1515         Location* location = session->locations()->auto_punch_location();
1516         list<Selectable *> touched;
1517
1518         if (location == 0 || (location->end() - location->start() <= 1))  {
1519                 return;
1520         }
1521
1522         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1523                 if ((*iter)->hidden()) {
1524                         continue;
1525                 }
1526                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1527         }
1528         begin_reversible_command (_("select all from punch"));
1529         selection->set (touched);
1530         commit_reversible_command ();
1531
1532 }
1533
1534 void
1535 Editor::select_all_selectables_using_loop()
1536 {
1537         Location* location = session->locations()->auto_loop_location();
1538         list<Selectable *> touched;
1539
1540         if (location == 0 || (location->end() - location->start() <= 1))  {
1541                 return;
1542         }
1543
1544         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1545                 if ((*iter)->hidden()) {
1546                         continue;
1547                 }
1548                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1549         }
1550         begin_reversible_command (_("select all from loop"));
1551         selection->set (touched);
1552         commit_reversible_command ();
1553
1554 }
1555
1556 void
1557 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1558 {
1559         nframes_t start;
1560         nframes_t end;
1561         list<Selectable *> touched;
1562
1563         if (after) {
1564                 begin_reversible_command (_("select all after cursor"));
1565                 start = cursor->current_frame ;
1566                 end = session->current_end_frame();
1567         } else {
1568                 if (cursor->current_frame > 0) {
1569                         begin_reversible_command (_("select all before cursor"));
1570                         start = 0;
1571                         end = cursor->current_frame - 1;
1572                 } else {
1573                         return;
1574                 }
1575         }
1576
1577         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1578                 if ((*iter)->hidden()) {
1579                         continue;
1580                 }
1581                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1582         }
1583         selection->set (touched);
1584         commit_reversible_command ();
1585 }
1586
1587 void
1588 Editor::select_all_selectables_between_cursors (Cursor *cursor, Cursor *other_cursor)
1589 {
1590         nframes_t start;
1591         nframes_t end;
1592         list<Selectable *> touched;
1593         bool  other_cursor_is_first = cursor->current_frame > other_cursor->current_frame;
1594
1595         if (cursor->current_frame == other_cursor->current_frame) {
1596                 return;
1597         }
1598
1599         begin_reversible_command (_("select all between cursors"));
1600         if (other_cursor_is_first) {
1601                 start = other_cursor->current_frame;
1602                 end = cursor->current_frame - 1;
1603                 
1604         } else {
1605                 start = cursor->current_frame;
1606                 end = other_cursor->current_frame - 1;
1607         }
1608         
1609         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1610                 if ((*iter)->hidden()) {
1611                         continue;
1612                 }
1613                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1614         }
1615         selection->set (touched);
1616         commit_reversible_command ();
1617 }
1618
1619 void
1620 Editor::amplitude_zoom_step (bool in)
1621 {
1622         gdouble zoom = 1.0;
1623
1624         if (in) {
1625                 zoom *= 2.0;
1626         } else {
1627                 if (zoom > 2.0) {
1628                         zoom /= 2.0;
1629                 } else {
1630                         zoom = 1.0;
1631                 }
1632         }
1633
1634 #ifdef FIX_FOR_CANVAS
1635         /* XXX DO SOMETHING */
1636 #endif
1637 }       
1638
1639
1640 /* DELETION */
1641
1642
1643 void
1644 Editor::delete_sample_forward ()
1645 {
1646 }
1647
1648 void
1649 Editor::delete_sample_backward ()
1650 {
1651 }
1652
1653 void
1654 Editor::delete_screen ()
1655 {
1656 }
1657
1658 /* SEARCH */
1659
1660 void
1661 Editor::search_backwards ()
1662 {
1663         /* what ? */
1664 }
1665
1666 void
1667 Editor::search_forwards ()
1668 {
1669         /* what ? */
1670 }
1671
1672 /* MARKS */
1673
1674 void
1675 Editor::jump_forward_to_mark ()
1676 {
1677         if (!session) {
1678                 return;
1679         }
1680         
1681         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1682
1683         if (location) {
1684                 session->request_locate (location->start(), session->transport_rolling());
1685         } else {
1686                 session->request_locate (session->current_end_frame());
1687         }
1688 }
1689
1690 void
1691 Editor::jump_backward_to_mark ()
1692 {
1693         if (!session) {
1694                 return;
1695         }
1696
1697         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1698         
1699         if (location) {
1700                 session->request_locate (location->start(), session->transport_rolling());
1701         } else {
1702                 session->goto_start ();
1703         }
1704 }
1705
1706 void
1707 Editor::set_mark ()
1708 {
1709         nframes_t pos;
1710         float prefix;
1711         bool was_floating;
1712         string markername;
1713
1714         if (get_prefix (prefix, was_floating)) {
1715                 pos = session->audible_frame ();
1716         } else {
1717                 if (was_floating) {
1718                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1719                 } else {
1720                         pos = (nframes_t) floor (prefix);
1721                 }
1722         }
1723
1724         session->locations()->next_available_name(markername,"mark");
1725         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1726 }
1727
1728 void
1729 Editor::clear_markers ()
1730 {
1731         if (session) {
1732                 session->begin_reversible_command (_("clear markers"));
1733                 XMLNode &before = session->locations()->get_state();
1734                 session->locations()->clear_markers ();
1735                 XMLNode &after = session->locations()->get_state();
1736                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1737                 session->commit_reversible_command ();
1738         }
1739 }
1740
1741 void
1742 Editor::clear_ranges ()
1743 {
1744         if (session) {
1745                 session->begin_reversible_command (_("clear ranges"));
1746                 XMLNode &before = session->locations()->get_state();
1747                 
1748                 Location * looploc = session->locations()->auto_loop_location();
1749                 Location * punchloc = session->locations()->auto_punch_location();
1750                 
1751                 session->locations()->clear_ranges ();
1752                 // re-add these
1753                 if (looploc) session->locations()->add (looploc);
1754                 if (punchloc) session->locations()->add (punchloc);
1755                 
1756                 XMLNode &after = session->locations()->get_state();
1757                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1758                 session->commit_reversible_command ();
1759         }
1760 }
1761
1762 void
1763 Editor::clear_locations ()
1764 {
1765         session->begin_reversible_command (_("clear locations"));
1766         XMLNode &before = session->locations()->get_state();
1767         session->locations()->clear ();
1768         XMLNode &after = session->locations()->get_state();
1769         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1770         session->commit_reversible_command ();
1771         session->locations()->clear ();
1772 }
1773
1774 void
1775 Editor::unhide_markers ()
1776 {
1777         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1778                 Location *l = (*i).first;
1779                 if (l->is_hidden() && l->is_mark()) {
1780                         l->set_hidden(false, this);
1781                 }
1782         }
1783 }
1784
1785 void
1786 Editor::unhide_ranges ()
1787 {
1788         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1789                 Location *l = (*i).first;
1790                 if (l->is_hidden() && l->is_range_marker()) { 
1791                         l->set_hidden(false, this);
1792                 }
1793         }
1794 }
1795
1796 /* INSERT/REPLACE */
1797
1798 void
1799 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
1800 {
1801         double wx, wy;
1802         double cx, cy;
1803         TimeAxisView *tv;
1804         nframes_t where;
1805         AudioTimeAxisView *atv = 0;
1806         boost::shared_ptr<Playlist> playlist;
1807         
1808         track_canvas.window_to_world (x, y, wx, wy);
1809         wx += horizontal_adjustment.get_value();
1810         wy += vertical_adjustment.get_value();
1811
1812         GdkEvent event;
1813         event.type = GDK_BUTTON_RELEASE;
1814         event.button.x = wx;
1815         event.button.y = wy;
1816         
1817         where = event_frame (&event, &cx, &cy);
1818
1819         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1820                 /* clearly outside canvas area */
1821                 return;
1822         }
1823         
1824         if ((tv = trackview_by_y_position (cy)) == 0) {
1825                 return;
1826         }
1827         
1828         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
1829                 return;
1830         }
1831
1832         if ((playlist = atv->playlist()) == 0) {
1833                 return;
1834         }
1835         
1836         cerr << "drop target playlist, UC  = " << playlist.use_count() << endl;
1837
1838         snap_to (where);
1839         
1840         begin_reversible_command (_("insert dragged region"));
1841         XMLNode &before = playlist->get_state();
1842         cerr << "pre add target playlist, UC  = " << playlist.use_count() << endl;
1843         playlist->add_region (RegionFactory::create (region), where, 1.0);
1844         cerr << "post add target playlist, UC  = " << playlist.use_count() << endl;
1845         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1846         commit_reversible_command ();
1847
1848         cerr << "post drop target playlist, UC  = " << playlist.use_count() << endl;
1849 }
1850
1851 void
1852 Editor::insert_region_list_selection (float times)
1853 {
1854         RouteTimeAxisView *tv = 0;
1855         boost::shared_ptr<Playlist> playlist;
1856
1857         if (clicked_audio_trackview != 0) {
1858                 tv = clicked_audio_trackview;
1859         } else if (!selection->tracks.empty()) {
1860                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
1861                         return;
1862                 }
1863         } else {
1864                 return;
1865         }
1866
1867         if ((playlist = tv->playlist()) == 0) {
1868                 return;
1869         }
1870         
1871         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
1872         
1873         if (selected->count_selected_rows() != 1) {
1874                 return;
1875         }
1876         
1877         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
1878
1879         /* only one row selected, so rows.begin() is it */
1880
1881         TreeIter iter;
1882
1883         if ((iter = region_list_model->get_iter (*rows.begin()))) {
1884
1885                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
1886                 
1887                 begin_reversible_command (_("insert region"));
1888                 XMLNode &before = playlist->get_state();
1889                 playlist->add_region ((RegionFactory::create (region)), edit_cursor->current_frame, times);
1890                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1891                 commit_reversible_command ();
1892         } 
1893 }
1894
1895 /* BUILT-IN EFFECTS */
1896
1897 void
1898 Editor::reverse_selection ()
1899 {
1900
1901 }
1902
1903 /* GAIN ENVELOPE EDITING */
1904
1905 void
1906 Editor::edit_envelope ()
1907 {
1908 }
1909
1910 /* PLAYBACK */
1911
1912 void
1913 Editor::toggle_playback (bool with_abort)
1914 {
1915         if (!session) {
1916                 return;
1917         }
1918
1919         switch (Config->get_slave_source()) {
1920         case None:
1921         case JACK:
1922                 break;
1923         default:
1924                 /* transport controlled by the master */
1925                 return;
1926         }
1927
1928         if (session->is_auditioning()) {
1929                 session->cancel_audition ();
1930                 return;
1931         }
1932         
1933         if (session->transport_rolling()) {
1934                 session->request_stop (with_abort);
1935                 if (session->get_play_loop()) {
1936                         session->request_play_loop (false);
1937                 }
1938         } else {
1939                 session->request_transport_speed (1.0f);
1940         }
1941 }
1942
1943 void
1944 Editor::play_from_start ()
1945 {
1946         session->request_locate (session->current_start_frame(), true);
1947 }
1948
1949 void
1950 Editor::play_from_edit_cursor ()
1951 {
1952        session->request_locate (edit_cursor->current_frame, true);
1953 }
1954
1955 void
1956 Editor::play_selection ()
1957 {
1958         if (selection->time.empty()) {
1959                 return;
1960         }
1961
1962         session->request_play_range (true);
1963 }
1964
1965 void
1966 Editor::play_selected_region ()
1967 {
1968         if (!selection->regions.empty()) {
1969                 RegionView *rv = *(selection->regions.begin());
1970
1971                 session->request_bounded_roll (rv->region()->position(), rv->region()->last_frame());   
1972         }
1973 }
1974
1975 void
1976 Editor::loop_selected_region ()
1977 {
1978         if (!selection->regions.empty()) {
1979                 RegionView *rv = *(selection->regions.begin());
1980                 Location* tll;
1981
1982                 if ((tll = transport_loop_location()) != 0)  {
1983
1984                         tll->set (rv->region()->position(), rv->region()->last_frame());
1985                         
1986                         // enable looping, reposition and start rolling
1987
1988                         session->request_play_loop (true);
1989                         session->request_locate (tll->start(), false);
1990                         session->request_transport_speed (1.0f);
1991                 }
1992         }
1993 }
1994
1995 void
1996 Editor::play_location (Location& location)
1997 {
1998         if (location.start() <= location.end()) {
1999                 return;
2000         }
2001
2002         session->request_bounded_roll (location.start(), location.end());
2003 }
2004
2005 void
2006 Editor::loop_location (Location& location)
2007 {
2008         if (location.start() <= location.end()) {
2009                 return;
2010         }
2011
2012         Location* tll;
2013
2014         if ((tll = transport_loop_location()) != 0) {
2015                 tll->set (location.start(), location.end());
2016
2017                 // enable looping, reposition and start rolling
2018                 session->request_play_loop (true);
2019                 session->request_locate (tll->start(), true);
2020         }
2021 }
2022
2023 void
2024 Editor::raise_region ()
2025 {
2026         selection->foreach_region (&Region::raise);
2027 }
2028
2029 void
2030 Editor::raise_region_to_top ()
2031 {
2032         selection->foreach_region (&Region::raise_to_top);
2033 }
2034
2035 void
2036 Editor::lower_region ()
2037 {
2038         selection->foreach_region (&Region::lower);
2039 }
2040
2041 void
2042 Editor::lower_region_to_bottom ()
2043 {
2044         selection->foreach_region (&Region::lower_to_bottom);
2045 }
2046
2047 void
2048 Editor::edit_region ()
2049 {
2050         if (clicked_regionview == 0) {
2051                 return;
2052         }
2053         
2054         clicked_regionview->show_region_editor ();
2055 }
2056
2057 void
2058 Editor::rename_region ()
2059 {
2060         Dialog dialog;
2061         Entry  entry;
2062         Button ok_button (_("OK"));
2063         Button cancel_button (_("Cancel"));
2064
2065         if (selection->regions.empty()) {
2066                 return;
2067         }
2068
2069         dialog.set_title (_("ardour: rename region"));
2070         dialog.set_name ("RegionRenameWindow");
2071         dialog.set_size_request (300, -1);
2072         dialog.set_position (Gtk::WIN_POS_MOUSE);
2073         dialog.set_modal (true);
2074
2075         dialog.get_vbox()->set_border_width (10);
2076         dialog.get_vbox()->pack_start (entry);
2077         dialog.get_action_area()->pack_start (ok_button);
2078         dialog.get_action_area()->pack_start (cancel_button);
2079
2080         entry.set_name ("RegionNameDisplay");
2081         ok_button.set_name ("EditorGTKButton");
2082         cancel_button.set_name ("EditorGTKButton");
2083
2084         region_renamed = false;
2085
2086         entry.signal_activate().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2087         ok_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2088         cancel_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), false));
2089
2090         /* recurse */
2091
2092         dialog.show_all ();
2093         Main::run ();
2094
2095         if (region_renamed) {
2096                 (*selection->regions.begin())->region()->set_name (entry.get_text());
2097                 redisplay_regions ();
2098         }
2099 }
2100
2101 void
2102 Editor::rename_region_finished (bool status)
2103
2104 {
2105         region_renamed = status;
2106         Main::quit ();
2107 }
2108
2109 void
2110 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2111 {
2112         if (session->is_auditioning()) {
2113                 session->cancel_audition ();
2114         } 
2115
2116         // note: some potential for creativity here, because region doesn't
2117         // have to belong to the playlist that Route is handling
2118
2119         // bool was_soloed = route.soloed();
2120
2121         route.set_solo (true, this);
2122         
2123         session->request_bounded_roll (region->position(), region->position() + region->length());
2124         
2125         /* XXX how to unset the solo state ? */
2126 }
2127
2128 void
2129 Editor::audition_selected_region ()
2130 {
2131         if (!selection->regions.empty()) {
2132                 RegionView* rv = *(selection->regions.begin());
2133                 session->audition_region (rv->region());
2134         }
2135 }
2136
2137 void
2138 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2139 {
2140         session->audition_region (region);
2141 }
2142
2143 void
2144 Editor::build_interthread_progress_window ()
2145 {
2146         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2147
2148         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2149         
2150         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2151         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2152
2153         // GTK2FIX: this button needs a modifiable label
2154
2155         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2156         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2157
2158         interthread_cancel_button.add (interthread_cancel_label);
2159
2160         interthread_progress_window->set_default_size (200, 100);
2161 }
2162
2163 void
2164 Editor::interthread_cancel_clicked ()
2165 {
2166         if (current_interthread_info) {
2167                 current_interthread_info->cancel = true;
2168         }
2169 }
2170
2171 void
2172 Editor::region_from_selection ()
2173 {
2174         if (clicked_trackview == 0) {
2175                 return;
2176         }
2177
2178         if (selection->time.empty()) {
2179                 return;
2180         }
2181
2182         nframes_t start = selection->time[clicked_selection].start;
2183         nframes_t end = selection->time[clicked_selection].end;
2184
2185         nframes_t selection_cnt = end - start + 1;
2186         
2187         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2188                 boost::shared_ptr<AudioRegion> current;
2189                 boost::shared_ptr<Region> current_r;
2190                 boost::shared_ptr<Playlist> pl;
2191
2192                 nframes_t internal_start;
2193                 string new_name;
2194
2195                 if ((pl = (*i)->playlist()) == 0) {
2196                         continue;
2197                 }
2198
2199                 if ((current_r = pl->top_region_at (start)) == 0) {
2200                         continue;
2201                 }
2202
2203                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2204                 // FIXME: audio only
2205                 if (current != 0) {
2206                         internal_start = start - current->position();
2207                         session->region_name (new_name, current->name(), true);
2208                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2209                 }
2210         }
2211 }       
2212
2213 void
2214 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2215 {
2216         if (selection->time.empty() || selection->tracks.empty()) {
2217                 return;
2218         }
2219
2220         nframes_t start = selection->time[clicked_selection].start;
2221         nframes_t end = selection->time[clicked_selection].end;
2222         
2223         sort_track_selection ();
2224
2225         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2226
2227                 boost::shared_ptr<AudioRegion> current;
2228                 boost::shared_ptr<Region> current_r;
2229                 boost::shared_ptr<Playlist> playlist;
2230                 nframes_t internal_start;
2231                 string new_name;
2232
2233                 if ((playlist = (*i)->playlist()) == 0) {
2234                         continue;
2235                 }
2236
2237                 if ((current_r = playlist->top_region_at(start)) == 0) {
2238                         continue;
2239                 }
2240
2241                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2242                         continue;
2243                 }
2244         
2245                 internal_start = start - current->position();
2246                 session->region_name (new_name, current->name(), true);
2247                 
2248                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2249         }
2250 }
2251
2252 void
2253 Editor::split_multichannel_region ()
2254 {
2255         vector<AudioRegion*> v;
2256
2257         AudioRegionView* clicked_arv = dynamic_cast<AudioRegionView*>(clicked_regionview);
2258         
2259         if (!clicked_arv || clicked_arv->audio_region()->n_channels() < 2) {
2260                 return;
2261         }
2262
2263         clicked_arv->audio_region()->separate_by_channel (*session, v);
2264
2265         /* nothing else to do, really */
2266 }
2267
2268 void
2269 Editor::new_region_from_selection ()
2270 {
2271         region_from_selection ();
2272         cancel_selection ();
2273 }
2274
2275 void
2276 Editor::separate_region_from_selection ()
2277 {
2278         bool doing_undo = false;
2279
2280         if (selection->time.empty()) {
2281                 return;
2282         }
2283
2284         boost::shared_ptr<Playlist> playlist;
2285                 
2286         sort_track_selection ();
2287
2288         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2289
2290                 AudioTimeAxisView* atv;
2291
2292                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2293
2294                         if (atv->is_audio_track()) {
2295                                         
2296                                 if ((playlist = atv->playlist()) != 0) {
2297                                         if (!doing_undo) {
2298                                                 begin_reversible_command (_("separate"));
2299                                                 doing_undo = true;
2300                                         }
2301                                         XMLNode *before;
2302                                         if (doing_undo) 
2303                                             before = &(playlist->get_state());
2304                         
2305                                         /* XXX need to consider musical time selections here at some point */
2306
2307                                         double speed = atv->get_diskstream()->speed();
2308
2309                                         for (list<AudioRange>::iterator t = selection->time.begin(); t != selection->time.end(); ++t) {
2310                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2311                                         }
2312
2313                                         if (doing_undo) 
2314                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2315                                 }
2316                         }
2317                 }
2318         }
2319
2320         if (doing_undo) commit_reversible_command ();
2321 }
2322
2323 void
2324 Editor::separate_regions_using_location (Location& loc)
2325 {
2326         bool doing_undo = false;
2327
2328         if (loc.is_mark()) {
2329                 return;
2330         }
2331
2332         boost::shared_ptr<Playlist> playlist;
2333
2334         /* XXX i'm unsure as to whether this should operate on selected tracks only 
2335            or the entire enchillada. uncomment the below line to correct the behaviour 
2336            (currently set for all tracks)
2337         */
2338
2339         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
2340         //for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2341
2342                 AudioTimeAxisView* atv;
2343
2344                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2345
2346                         if (atv->is_audio_track()) {
2347                                         
2348                                 if ((playlist = atv->playlist()) != 0) {
2349                                         XMLNode *before;
2350                                         if (!doing_undo) {
2351                                                 begin_reversible_command (_("separate"));
2352                                                 doing_undo = true;
2353                                         }
2354                                         if (doing_undo) 
2355                                             before = &(playlist->get_state());
2356                                             
2357                         
2358                                         /* XXX need to consider musical time selections here at some point */
2359
2360                                         double speed = atv->get_diskstream()->speed();
2361
2362
2363                                         playlist->partition ((nframes_t)(loc.start() * speed), (nframes_t)(loc.end() * speed), true);
2364                                         if (doing_undo) 
2365                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2366                                 }
2367                         }
2368                 }
2369         }
2370
2371         if (doing_undo) commit_reversible_command ();
2372 }
2373
2374 void
2375 Editor::crop_region_to_selection ()
2376 {
2377         if (selection->time.empty()) {
2378                 return;
2379         }
2380
2381         vector<boost::shared_ptr<Playlist> > playlists;
2382         boost::shared_ptr<Playlist> playlist;
2383
2384         if (clicked_trackview != 0) {
2385
2386                 if ((playlist = clicked_trackview->playlist()) == 0) {
2387                         return;
2388                 }
2389
2390                 playlists.push_back (playlist);
2391
2392         } else {
2393                 
2394                 sort_track_selection ();
2395
2396                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2397
2398                         AudioTimeAxisView* atv;
2399
2400                         if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2401
2402                                 if (atv->is_audio_track()) {
2403                                         
2404                                         if ((playlist = atv->playlist()) != 0) {
2405                                                 playlists.push_back (playlist);
2406                                         }
2407                                 }
2408                         }
2409                 }
2410         }
2411
2412         if (!playlists.empty()) {
2413
2414                 nframes_t start;
2415                 nframes_t end;
2416                 nframes_t cnt;
2417
2418                 begin_reversible_command (_("trim to selection"));
2419
2420                 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2421                         
2422                         boost::shared_ptr<Region> region;
2423                         
2424                         start = selection->time.start();
2425
2426                         if ((region = (*i)->top_region_at(start)) == 0) {
2427                                 continue;
2428                         }
2429                         
2430                         /* now adjust lengths to that we do the right thing
2431                            if the selection extends beyond the region
2432                         */
2433                         
2434                         start = max (start, region->position());
2435                         end = min (selection->time.end_frame(), start + region->length() - 1);
2436                         cnt = end - start + 1;
2437
2438                         XMLNode &before = (*i)->get_state();
2439                         region->trim_to (start, cnt, this);
2440                         XMLNode &after = (*i)->get_state();
2441                         session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2442                 }
2443
2444                 commit_reversible_command ();
2445         }
2446 }               
2447
2448 void
2449 Editor::region_fill_track ()
2450 {
2451         nframes_t end;
2452
2453         if (!session || selection->regions.empty()) {
2454                 return;
2455         }
2456
2457         end = session->current_end_frame ();
2458
2459         begin_reversible_command (_("region fill"));
2460
2461         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2462
2463                 boost::shared_ptr<Region> region ((*i)->region());
2464                 
2465                 // FIXME
2466                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2467                 if (!ar)
2468                         continue;
2469
2470                 boost::shared_ptr<Playlist> pl = region->playlist();
2471
2472                 if (end <= region->last_frame()) {
2473                         return;
2474                 }
2475
2476                 double times = (double) (end - region->last_frame()) / (double) region->length();
2477
2478                 if (times == 0) {
2479                         return;
2480                 }
2481
2482                 XMLNode &before = pl->get_state();
2483                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2484                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2485         }
2486
2487         commit_reversible_command ();
2488 }
2489
2490 void
2491 Editor::region_fill_selection ()
2492 {
2493         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2494                 return;
2495         }
2496
2497         if (selection->time.empty()) {
2498                 return;
2499         }
2500
2501
2502         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2503
2504         if (selected->count_selected_rows() != 1) {
2505                 return;
2506         }
2507
2508         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2509         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2510
2511         nframes_t start = selection->time[clicked_selection].start;
2512         nframes_t end = selection->time[clicked_selection].end;
2513
2514         boost::shared_ptr<Playlist> playlist; 
2515
2516         if (selection->tracks.empty()) {
2517                 return;
2518         }
2519
2520         nframes_t selection_length = end - start;
2521         float times = (float)selection_length / region->length();
2522         
2523         begin_reversible_command (_("fill selection"));
2524         
2525         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2526
2527                 if ((playlist = (*i)->playlist()) == 0) {
2528                         continue;
2529                 }               
2530                 
2531                 XMLNode &before = playlist->get_state();
2532                 playlist->add_region (RegionFactory::create (region), start, times);
2533                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2534         }
2535         
2536         commit_reversible_command ();                   
2537 }
2538
2539 void
2540 Editor::set_a_regions_sync_position (boost::shared_ptr<Region> region, nframes_t position)
2541 {
2542
2543         if (!region->covers (position)) {
2544           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2545                 return;
2546         }
2547         begin_reversible_command (_("set region sync position"));
2548         XMLNode &before = region->playlist()->get_state();
2549         region->set_sync_position (position);
2550         XMLNode &after = region->playlist()->get_state();
2551         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2552         commit_reversible_command ();
2553 }
2554
2555 void
2556 Editor::set_region_sync_from_edit_cursor ()
2557 {
2558         if (clicked_regionview == 0) {
2559                 return;
2560         }
2561
2562         if (!clicked_regionview->region()->covers (edit_cursor->current_frame)) {
2563                 error << _("Place the edit cursor at the desired sync point") << endmsg;
2564                 return;
2565         }
2566
2567         boost::shared_ptr<Region> region (clicked_regionview->region());
2568         begin_reversible_command (_("set sync from edit cursor"));
2569         XMLNode &before = region->playlist()->get_state();
2570         region->set_sync_position (edit_cursor->current_frame);
2571         XMLNode &after = region->playlist()->get_state();
2572         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2573         commit_reversible_command ();
2574 }
2575
2576 void
2577 Editor::remove_region_sync ()
2578 {
2579         if (clicked_regionview) {
2580                 boost::shared_ptr<Region> region (clicked_regionview->region());
2581                 begin_reversible_command (_("remove sync"));
2582                 XMLNode &before = region->playlist()->get_state();
2583                 region->clear_sync_position ();
2584                 XMLNode &after = region->playlist()->get_state();
2585                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2586                 commit_reversible_command ();
2587         }
2588 }
2589
2590 void
2591 Editor::naturalize ()
2592 {
2593         if (selection->regions.empty()) {
2594                 return;
2595         }
2596         begin_reversible_command (_("naturalize"));
2597         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2598                 XMLNode &before = (*i)->region()->get_state();
2599                 (*i)->region()->move_to_natural_position (this);
2600                 XMLNode &after = (*i)->region()->get_state();
2601                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2602         }
2603         commit_reversible_command ();
2604 }
2605
2606 void
2607 Editor::align (RegionPoint what)
2608 {
2609         align_selection (what, edit_cursor->current_frame);
2610 }
2611
2612 void
2613 Editor::align_relative (RegionPoint what)
2614 {
2615         align_selection_relative (what, edit_cursor->current_frame);
2616 }
2617
2618 struct RegionSortByTime {
2619     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2620             return a->region()->position() < b->region()->position();
2621     }
2622 };
2623
2624 void
2625 Editor::align_selection_relative (RegionPoint point, nframes_t position)
2626 {
2627         if (selection->regions.empty()) {
2628                 return;
2629         }
2630
2631         nframes_t distance;
2632         nframes_t pos = 0;
2633         int dir;
2634
2635         list<RegionView*> sorted;
2636         selection->regions.by_position (sorted);
2637         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2638
2639         switch (point) {
2640         case Start:
2641                 pos = r->first_frame ();
2642                 break;
2643
2644         case End:
2645                 pos = r->last_frame();
2646                 break;
2647
2648         case SyncPoint:
2649                 pos = r->adjust_to_sync (r->first_frame());
2650                 break;  
2651         }
2652
2653         if (pos > position) {
2654                 distance = pos - position;
2655                 dir = -1;
2656         } else {
2657                 distance = position - pos;
2658                 dir = 1;
2659         }
2660
2661         begin_reversible_command (_("align selection (relative)"));
2662
2663         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2664
2665                 boost::shared_ptr<Region> region ((*i)->region());
2666
2667                 XMLNode &before = region->playlist()->get_state();
2668                 
2669                 if (dir > 0) {
2670                         region->set_position (region->position() + distance, this);
2671                 } else {
2672                         region->set_position (region->position() - distance, this);
2673                 }
2674
2675                 XMLNode &after = region->playlist()->get_state();
2676                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2677
2678         }
2679
2680         commit_reversible_command ();
2681 }
2682
2683 void
2684 Editor::align_selection (RegionPoint point, nframes_t position)
2685 {
2686         if (selection->regions.empty()) {
2687                 return;
2688         }
2689
2690         begin_reversible_command (_("align selection"));
2691
2692         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2693                 align_region_internal ((*i)->region(), point, position);
2694         }
2695
2696         commit_reversible_command ();
2697 }
2698
2699 void
2700 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2701 {
2702         begin_reversible_command (_("align region"));
2703         align_region_internal (region, point, position);
2704         commit_reversible_command ();
2705 }
2706
2707 void
2708 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2709 {
2710         XMLNode &before = region->playlist()->get_state();
2711
2712         switch (point) {
2713         case SyncPoint:
2714                 region->set_position (region->adjust_to_sync (position), this);
2715                 break;
2716
2717         case End:
2718                 if (position > region->length()) {
2719                         region->set_position (position - region->length(), this);
2720                 }
2721                 break;
2722
2723         case Start:
2724                 region->set_position (position, this);
2725                 break;
2726         }
2727
2728         XMLNode &after = region->playlist()->get_state();
2729         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2730 }       
2731
2732 void
2733 Editor::trim_region_to_edit_cursor ()
2734 {
2735         if (clicked_regionview == 0) {
2736                 return;
2737         }
2738
2739         boost::shared_ptr<Region> region (clicked_regionview->region());
2740
2741         float speed = 1.0f;
2742         AudioTimeAxisView *atav;
2743
2744         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2745                 if (atav->get_diskstream() != 0) {
2746                         speed = atav->get_diskstream()->speed();
2747                 }
2748         }
2749
2750         begin_reversible_command (_("trim to edit"));
2751         XMLNode &before = region->playlist()->get_state();
2752         region->trim_end( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2753         XMLNode &after = region->playlist()->get_state();
2754         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2755         commit_reversible_command ();
2756 }
2757
2758 void
2759 Editor::trim_region_from_edit_cursor ()
2760 {
2761         if (clicked_regionview == 0) {
2762                 return;
2763         }
2764
2765         boost::shared_ptr<Region> region (clicked_regionview->region());
2766
2767         float speed = 1.0f;
2768         AudioTimeAxisView *atav;
2769
2770         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2771                 if (atav->get_diskstream() != 0) {
2772                         speed = atav->get_diskstream()->speed();
2773                 }
2774         }
2775
2776         begin_reversible_command (_("trim to edit"));
2777         XMLNode &before = region->playlist()->get_state();
2778         region->trim_front ( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2779         XMLNode &after = region->playlist()->get_state();
2780         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2781         commit_reversible_command ();
2782 }
2783
2784 void
2785 Editor::unfreeze_route ()
2786 {
2787         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2788                 return;
2789         }
2790         
2791         clicked_audio_trackview->audio_track()->unfreeze ();
2792 }
2793
2794 void*
2795 Editor::_freeze_thread (void* arg)
2796 {
2797         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2798         return static_cast<Editor*>(arg)->freeze_thread ();
2799 }
2800
2801 void*
2802 Editor::freeze_thread ()
2803 {
2804         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2805         return 0;
2806 }
2807
2808 gint
2809 Editor::freeze_progress_timeout (void *arg)
2810 {
2811         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2812         return !(current_interthread_info->done || current_interthread_info->cancel);
2813 }
2814
2815 void
2816 Editor::freeze_route ()
2817 {
2818         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2819                 return;
2820         }
2821         
2822         InterThreadInfo itt;
2823
2824         if (interthread_progress_window == 0) {
2825                 build_interthread_progress_window ();
2826         }
2827         
2828         interthread_progress_window->set_title (_("ardour: freeze"));
2829         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2830         interthread_progress_window->show_all ();
2831         interthread_progress_bar.set_fraction (0.0f);
2832         interthread_progress_label.set_text ("");
2833         interthread_cancel_label.set_text (_("Cancel Freeze"));
2834         current_interthread_info = &itt;
2835
2836         interthread_progress_connection = 
2837           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2838
2839         itt.done = false;
2840         itt.cancel = false;
2841         itt.progress = 0.0f;
2842         
2843         pthread_attr_t attr;
2844         pthread_attr_init(&attr);
2845         pthread_attr_setstacksize(&attr, 500000);
2846
2847         pthread_create (&itt.thread, &attr, _freeze_thread, this);
2848
2849         pthread_attr_destroy(&attr);
2850
2851         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2852
2853         while (!itt.done && !itt.cancel) {
2854                 gtk_main_iteration ();
2855         }
2856
2857         interthread_progress_connection.disconnect ();
2858         interthread_progress_window->hide_all ();
2859         current_interthread_info = 0;
2860         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2861 }
2862
2863 void
2864 Editor::bounce_range_selection ()
2865 {
2866         if (selection->time.empty()) {
2867                 return;
2868         }
2869
2870         TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
2871
2872         nframes_t start = selection->time[clicked_selection].start;
2873         nframes_t end = selection->time[clicked_selection].end;
2874         nframes_t cnt = end - start + 1;
2875         
2876         begin_reversible_command (_("bounce range"));
2877
2878         for (TrackViewList::iterator i = views->begin(); i != views->end(); ++i) {
2879
2880                 AudioTimeAxisView* atv;
2881
2882                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2883                         continue;
2884                 }
2885                 
2886                 boost::shared_ptr<Playlist> playlist;
2887                 
2888                 if ((playlist = atv->playlist()) == 0) {
2889                         return;
2890                 }
2891
2892                 InterThreadInfo itt;
2893                 
2894                 itt.done = false;
2895                 itt.cancel = false;
2896                 itt.progress = false;
2897                 
2898                 XMLNode &before = playlist->get_state();
2899                 atv->audio_track()->bounce_range (start, cnt, itt);
2900                 XMLNode &after = playlist->get_state();
2901                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
2902         }
2903         
2904         commit_reversible_command ();
2905         
2906         delete views;
2907 }
2908
2909 void
2910 Editor::cut ()
2911 {
2912         cut_copy (Cut);
2913 }
2914
2915 void
2916 Editor::copy ()
2917 {
2918         cut_copy (Copy);
2919 }
2920
2921 void 
2922 Editor::cut_copy (CutCopyOp op)
2923 {
2924         /* only cancel selection if cut/copy is successful.*/
2925
2926         string opname;
2927
2928         switch (op) {
2929         case Cut:
2930                 opname = _("cut");
2931                 break;
2932         case Copy:
2933                 opname = _("copy");
2934                 break;
2935         case Clear:
2936                 opname = _("clear");
2937                 break;
2938         }
2939         
2940         cut_buffer->clear ();
2941
2942         switch (current_mouse_mode()) {
2943         case MouseObject: 
2944                 if (!selection->regions.empty() || !selection->points.empty()) {
2945
2946                         begin_reversible_command (opname + _(" objects"));
2947
2948                         if (!selection->regions.empty()) {
2949                                 
2950                                 cut_copy_regions (op);
2951                                 
2952                                 if (op == Cut) {
2953                                         selection->clear_regions ();
2954                                 }
2955                         }
2956
2957                         if (!selection->points.empty()) {
2958                                 cut_copy_points (op);
2959
2960                                 if (op == Cut) {
2961                                         selection->clear_points ();
2962                                 }
2963                         }
2964
2965                         commit_reversible_command ();   
2966                 }
2967                 break;
2968                 
2969         case MouseRange:
2970                 if (!selection->time.empty()) {
2971
2972                         begin_reversible_command (opname + _(" range"));
2973                         cut_copy_ranges (op);
2974                         commit_reversible_command ();
2975
2976                         if (op == Cut) {
2977                                 selection->clear_time ();
2978                         }
2979                         
2980                 }
2981                 break;
2982                 
2983         default:
2984                 break;
2985         }
2986 }
2987
2988 void
2989 Editor::cut_copy_points (CutCopyOp op)
2990 {
2991         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
2992
2993                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
2994
2995                 if (atv) {
2996                         atv->cut_copy_clear_objects (selection->points, op);
2997                 } 
2998         }
2999 }
3000
3001 struct PlaylistState {
3002     boost::shared_ptr<Playlist> playlist;
3003     XMLNode*  before;
3004 };
3005
3006 struct lt_playlist {
3007     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3008             return a.playlist < b.playlist;
3009     }
3010 };
3011         
3012 struct PlaylistMapping { 
3013     TimeAxisView* tv;
3014     boost::shared_ptr<AudioPlaylist> pl;
3015
3016     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3017 };
3018
3019 void
3020 Editor::cut_copy_regions (CutCopyOp op)
3021 {
3022         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3023            a map when we want ordered access to both elements. i think.
3024         */
3025
3026         vector<PlaylistMapping> pmap;
3027
3028         nframes_t first_position = max_frames;
3029         
3030         set<PlaylistState, lt_playlist> freezelist;
3031         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3032         
3033         /* get ordering correct before we cut/copy */
3034         
3035         selection->regions.sort_by_position_and_track ();
3036
3037         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3038
3039                 first_position = min ((*x)->region()->position(), first_position);
3040
3041                 if (op == Cut || op == Clear) {
3042                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3043
3044                         if (pl) {
3045
3046                                 PlaylistState before;
3047                                 before.playlist = pl;
3048                                 before.before = &pl->get_state();
3049                                 
3050                                 insert_result = freezelist.insert (before);
3051                                 
3052                                 if (insert_result.second) {
3053                                         pl->freeze ();
3054                                 }
3055                         }
3056                 }
3057
3058                 TimeAxisView* tv = &(*x)->get_trackview();
3059                 vector<PlaylistMapping>::iterator z;
3060
3061                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3062                         if ((*z).tv == tv) {
3063                                 break;
3064                         }
3065                 }
3066                 
3067                 if (z == pmap.end()) {
3068                         pmap.push_back (PlaylistMapping (tv));
3069                 }
3070         }
3071
3072         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3073
3074                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3075                 
3076                 if (!pl) {
3077                         /* impossible, but this handles it for the future */
3078                         continue;
3079                 }
3080
3081                 TimeAxisView& tv = (*x)->get_trackview();
3082                 boost::shared_ptr<AudioPlaylist> npl;
3083                 RegionSelection::iterator tmp;
3084                 
3085                 tmp = x;
3086                 ++tmp;
3087
3088                 vector<PlaylistMapping>::iterator z;
3089                 
3090                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3091                         if ((*z).tv == &tv) {
3092                                 break;
3093                         }
3094                 }
3095                 
3096                 assert (z != pmap.end());
3097                 
3098                 if (!(*z).pl) {
3099                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3100                         npl->freeze();
3101                         (*z).pl = npl;
3102                 } else {
3103                         npl = (*z).pl;
3104                 }
3105                 
3106                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3107                 
3108                 switch (op) {
3109                 case Cut:
3110                         if (!ar) break;
3111                         
3112                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3113                         pl->remove_region (((*x)->region()));
3114                         break;
3115                         
3116                 case Copy:
3117                         if (!ar) break;
3118                         
3119                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3120                         break;
3121                         
3122                 case Clear:
3123                         pl->remove_region (((*x)->region()));
3124                         break;
3125                 }
3126
3127                 x = tmp;
3128         }
3129         
3130         list<boost::shared_ptr<Playlist> > foo;
3131         
3132         /* the pmap is in the same order as the tracks in which selected regions occured */
3133         
3134         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3135                 (*i).pl->thaw();
3136                 foo.push_back ((*i).pl);
3137         }
3138         
3139         if (!foo.empty()) {
3140                 cut_buffer->set (foo);
3141         }
3142         
3143         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3144                 (*pl).playlist->thaw ();
3145                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3146         }
3147 }
3148
3149 void
3150 Editor::cut_copy_ranges (CutCopyOp op)
3151 {
3152         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3153                 (*i)->cut_copy_clear (*selection, op);
3154         }
3155 }
3156
3157 void
3158 Editor::paste (float times)
3159 {
3160         paste_internal (edit_cursor->current_frame, times);
3161 }
3162
3163 void
3164 Editor::mouse_paste ()
3165 {
3166         int x, y;
3167         double wx, wy;
3168
3169         track_canvas.get_pointer (x, y);
3170         track_canvas.window_to_world (x, y, wx, wy);
3171         wx += horizontal_adjustment.get_value();
3172         wy += vertical_adjustment.get_value();
3173
3174         GdkEvent event;
3175         event.type = GDK_BUTTON_RELEASE;
3176         event.button.x = wx;
3177         event.button.y = wy;
3178         
3179         nframes_t where = event_frame (&event, 0, 0);
3180         snap_to (where);
3181         paste_internal (where, 1);
3182 }
3183
3184 void
3185 Editor::paste_internal (nframes_t position, float times)
3186 {
3187         bool commit = false;
3188
3189         if (cut_buffer->empty() || selection->tracks.empty()) {
3190                 return;
3191         }
3192
3193         if (position == max_frames) {
3194                 position = edit_cursor->current_frame;
3195         }
3196
3197         begin_reversible_command (_("paste"));
3198
3199         TrackSelection::iterator i;
3200         size_t nth;
3201
3202         /* get everything in the correct order */
3203
3204         sort_track_selection ();
3205
3206         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3207
3208                 /* undo/redo is handled by individual tracks */
3209
3210                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3211                         commit = true;
3212                 }
3213         }
3214         
3215         if (commit) {
3216                 commit_reversible_command ();
3217         }
3218 }
3219
3220 void
3221 Editor::paste_named_selection (float times)
3222 {
3223         TrackSelection::iterator t;
3224
3225         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3226
3227         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3228                 return;
3229         }
3230
3231         TreeModel::iterator i = selected->get_selected();
3232         NamedSelection* ns = (*i)[named_selection_columns.selection];
3233
3234         list<boost::shared_ptr<Playlist> >::iterator chunk;
3235         list<boost::shared_ptr<Playlist> >::iterator tmp;
3236
3237         chunk = ns->playlists.begin();
3238                 
3239         begin_reversible_command (_("paste chunk"));
3240         
3241         sort_track_selection ();
3242
3243         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3244                 
3245                 AudioTimeAxisView* atv;
3246                 boost::shared_ptr<Playlist> pl;
3247                 boost::shared_ptr<AudioPlaylist> apl;
3248
3249                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3250                         continue;
3251                 }
3252
3253                 if ((pl = atv->playlist()) == 0) {
3254                         continue;
3255                 }
3256                 
3257                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3258                         continue;
3259                 }
3260
3261                 tmp = chunk;
3262                 ++tmp;
3263
3264                 XMLNode &before = apl->get_state();
3265                 apl->paste (*chunk, edit_cursor->current_frame, times);
3266                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3267
3268                 if (tmp != ns->playlists.end()) {
3269                         chunk = tmp;
3270                 }
3271         }
3272
3273         commit_reversible_command();
3274 }
3275
3276 void
3277 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3278 {
3279         boost::shared_ptr<Playlist> playlist; 
3280         RegionSelection sel = regions; // clear (below) will clear the argument list
3281                 
3282         begin_reversible_command (_("duplicate region"));
3283
3284         selection->clear_regions ();
3285
3286         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3287
3288                 boost::shared_ptr<Region> r ((*i)->region());
3289
3290                 TimeAxisView& tv = (*i)->get_time_axis_view();
3291                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3292                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3293                 
3294                 playlist = (*i)->region()->playlist();
3295                 XMLNode &before = playlist->get_state();
3296                 playlist->duplicate (r, r->last_frame(), times);
3297                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3298
3299                 c.disconnect ();
3300
3301                 if (latest_regionview) {
3302                         selection->add (latest_regionview);
3303                 }
3304         }
3305                 
3306
3307         commit_reversible_command ();
3308 }
3309
3310 void
3311 Editor::duplicate_selection (float times)
3312 {
3313         if (selection->time.empty() || selection->tracks.empty()) {
3314                 return;
3315         }
3316
3317         boost::shared_ptr<Playlist> playlist; 
3318         vector<boost::shared_ptr<AudioRegion> > new_regions;
3319         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3320                 
3321         create_region_from_selection (new_regions);
3322
3323         if (new_regions.empty()) {
3324                 return;
3325         }
3326         
3327         begin_reversible_command (_("duplicate selection"));
3328
3329         ri = new_regions.begin();
3330
3331         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3332                 if ((playlist = (*i)->playlist()) == 0) {
3333                         continue;
3334                 }
3335                 XMLNode &before = playlist->get_state();
3336                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3337                 XMLNode &after = playlist->get_state();
3338                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3339
3340                 ++ri;
3341                 if (ri == new_regions.end()) {
3342                         --ri;
3343                 }
3344         }
3345
3346         commit_reversible_command ();
3347 }
3348
3349 void
3350 Editor::reset_point_selection ()
3351 {
3352         /* reset all selected points to the relevant default value */
3353
3354         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3355                 
3356                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3357                 
3358                 if (atv) {
3359                         atv->reset_objects (selection->points);
3360                 } 
3361         }
3362 }
3363
3364 void
3365 Editor::center_playhead ()
3366 {
3367         float page = canvas_width * frames_per_unit;
3368
3369         center_screen_internal (playhead_cursor->current_frame, page);
3370 }
3371
3372 void
3373 Editor::center_edit_cursor ()
3374 {
3375         float page = canvas_width * frames_per_unit;
3376
3377         center_screen_internal (edit_cursor->current_frame, page);
3378 }
3379
3380 void
3381 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3382 {
3383         begin_reversible_command (_("clear playlist"));
3384         XMLNode &before = playlist->get_state();
3385         playlist->clear ();
3386         XMLNode &after = playlist->get_state();
3387         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3388         commit_reversible_command ();
3389 }
3390
3391 void
3392 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3393 {
3394         boost::shared_ptr<Playlist> playlist; 
3395         nframes_t distance;
3396         nframes_t next_distance;
3397         nframes_t start;
3398
3399         if (use_edit_cursor) {
3400                 start = edit_cursor->current_frame;
3401         } else {
3402                 start = 0;
3403         }
3404
3405         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3406                 return;
3407         }
3408         
3409         if (selection->tracks.empty()) {
3410                 return;
3411         }
3412         
3413         begin_reversible_command (_("nudge track"));
3414         
3415         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3416
3417                 if ((playlist = (*i)->playlist()) == 0) {
3418                         continue;
3419                 }               
3420                 
3421                 XMLNode &before = playlist->get_state();
3422                 playlist->nudge_after (start, distance, forwards);
3423                 XMLNode &after = playlist->get_state();
3424                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3425         }
3426         
3427         commit_reversible_command ();                   
3428 }
3429
3430 void
3431 Editor::remove_last_capture ()
3432 {
3433         vector<string> choices;
3434         string prompt;
3435         
3436         if (!session) {
3437                 return;
3438         }
3439
3440         if (Config->get_verify_remove_last_capture()) {
3441                 prompt  = _("Do you really want to destroy the last capture?"
3442                             "\n(This is destructive and cannot be undone)");
3443
3444                 choices.push_back (_("No, do nothing."));
3445                 choices.push_back (_("Yes, destroy it."));
3446                 
3447                 Gtkmm2ext::Choice prompter (prompt, choices);
3448                 
3449                 if (prompter.run () == 1) {
3450                         session->remove_last_capture ();
3451                 }
3452
3453         } else {
3454                 session->remove_last_capture();
3455         }
3456 }
3457
3458 void
3459 Editor::normalize_region ()
3460 {
3461         if (!session) {
3462                 return;
3463         }
3464
3465         if (selection->regions.empty()) {
3466                 return;
3467         }
3468
3469         begin_reversible_command (_("normalize"));
3470
3471         track_canvas.get_window()->set_cursor (*wait_cursor);
3472         gdk_flush ();
3473
3474         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3475                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3476                 if (!arv)
3477                         continue;
3478                 XMLNode &before = arv->region()->get_state();
3479                 arv->audio_region()->normalize_to (0.0f);
3480                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3481         }
3482
3483         commit_reversible_command ();
3484         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3485 }
3486
3487
3488 void
3489 Editor::denormalize_region ()
3490 {
3491         if (!session) {
3492                 return;
3493         }
3494
3495         if (selection->regions.empty()) {
3496                 return;
3497         }
3498
3499         begin_reversible_command ("denormalize");
3500
3501         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3502                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3503                 if (!arv)
3504                         continue;
3505                 XMLNode &before = arv->region()->get_state();
3506                 arv->audio_region()->set_scale_amplitude (1.0f);
3507                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3508         }
3509
3510         commit_reversible_command ();
3511 }
3512
3513
3514 void
3515 Editor::reverse_region ()
3516 {
3517         if (!session) {
3518                 return;
3519         }
3520
3521         Reverse rev (*session);
3522         apply_filter (rev, _("reverse regions"));
3523 }
3524
3525 void
3526 Editor::apply_filter (AudioFilter& filter, string command)
3527 {
3528         if (selection->regions.empty()) {
3529                 return;
3530         }
3531
3532         begin_reversible_command (command);
3533
3534         track_canvas.get_window()->set_cursor (*wait_cursor);
3535         gdk_flush ();
3536
3537         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3538                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3539                 if (!arv)
3540                         continue;
3541
3542                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3543
3544                 RegionSelection::iterator tmp;
3545                 
3546                 tmp = r;
3547                 ++tmp;
3548
3549                 if (arv->audio_region()->apply (filter) == 0) {
3550
3551                         XMLNode &before = playlist->get_state();
3552                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3553                         XMLNode &after = playlist->get_state();
3554                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3555                 } else {
3556                         goto out;
3557                 }
3558
3559                 r = tmp;
3560         }
3561
3562         commit_reversible_command ();
3563         selection->regions.clear ();
3564
3565   out:
3566         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3567 }
3568
3569 void
3570 Editor::region_selection_op (void (Region::*pmf)(void))
3571 {
3572         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3573                 Region* region = (*i)->region().get();
3574                 (region->*pmf)();
3575         }
3576 }
3577
3578
3579 void
3580 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3581 {
3582         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3583                 Region* region = (*i)->region().get();
3584                 (region->*pmf)(arg);
3585         }
3586 }
3587
3588 void
3589 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3590 {
3591         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3592                 Region* region = (*i)->region().get();
3593                 (region->*pmf)(yn);
3594         }
3595 }
3596
3597 void
3598 Editor::external_edit_region ()
3599 {
3600         if (!clicked_regionview) {
3601                 return;
3602         }
3603
3604         /* more to come */
3605 }
3606
3607 void
3608 Editor::brush (nframes_t pos)
3609 {
3610         RegionSelection sel;
3611         snap_to (pos);
3612
3613         if (selection->regions.empty()) {
3614                 /* XXX get selection from region list */
3615         } else { 
3616                 sel = selection->regions;
3617         }
3618
3619         if (sel.empty()) {
3620                 return;
3621         }
3622
3623         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3624                 mouse_brush_insert_region ((*i), pos);
3625         }
3626 }
3627
3628 void
3629 Editor::reset_region_gain_envelopes ()
3630 {
3631         if (!session || selection->regions.empty()) {
3632                 return;
3633         }
3634
3635         session->begin_reversible_command (_("reset region gain"));
3636
3637         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3638                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3639                 if (arv) {
3640                         AutomationList& alist (arv->audio_region()->envelope());
3641                         XMLNode& before (alist.get_state());
3642
3643                         arv->audio_region()->set_default_envelope ();
3644                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3645                 }
3646         }
3647
3648         session->commit_reversible_command ();
3649 }
3650
3651 void
3652 Editor::toggle_gain_envelope_visibility ()
3653 {
3654         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3655                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3656                 if (arv) {
3657                         bool x = region_envelope_visible_item->get_active();
3658                         if (x != arv->envelope_visible()) {
3659                                 arv->set_envelope_visible (x);
3660                         }
3661                 }
3662         }
3663 }
3664
3665 void
3666 Editor::toggle_gain_envelope_active ()
3667 {
3668         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3669                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3670                 if (arv) {
3671                         bool x = region_envelope_active_item->get_active();
3672                         if (x != arv->audio_region()->envelope_active()) {
3673                                 arv->audio_region()->set_envelope_active (x);
3674                         }
3675                 }
3676         }
3677 }
3678
3679 void
3680 Editor::toggle_region_lock ()
3681 {
3682         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3683                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3684                 if (arv) {
3685                         bool x = region_lock_item->get_active();
3686                         if (x != arv->audio_region()->locked()) {
3687                                 arv->audio_region()->set_locked (x);
3688                         }
3689                 }
3690         }
3691 }
3692
3693 void
3694 Editor::toggle_region_mute ()
3695 {
3696         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3697                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3698                 if (arv) {
3699                         bool x = region_mute_item->get_active();
3700                         if (x != arv->audio_region()->muted()) {
3701                                 arv->audio_region()->set_muted (x);
3702                         }
3703                 }
3704         }
3705 }
3706
3707 void
3708 Editor::toggle_region_opaque ()
3709 {
3710         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3711                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3712                 if (arv) {
3713                         bool x = region_opaque_item->get_active();
3714                         if (x != arv->audio_region()->opaque()) {
3715                                 arv->audio_region()->set_opaque (x);
3716                         }
3717                 }
3718         }
3719 }
3720
3721 void
3722 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
3723 {
3724         begin_reversible_command (_("set fade in shape"));
3725
3726         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3727                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3728
3729                 if (!tmp) {
3730                         return;
3731                 }
3732
3733                 AutomationList& alist = tmp->audio_region()->fade_in();
3734                 XMLNode &before = alist.get_state();
3735
3736                 tmp->audio_region()->set_fade_in_shape (shape);
3737                 
3738                 XMLNode &after = alist.get_state();
3739                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3740         }
3741
3742         commit_reversible_command ();
3743 }
3744
3745 void
3746 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
3747 {
3748         begin_reversible_command (_("set fade out shape"));
3749
3750         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3751                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3752
3753                 if (!tmp) {
3754                         return;
3755                 }
3756
3757                 AutomationList& alist = tmp->audio_region()->fade_out();
3758                 XMLNode &before = alist.get_state();
3759
3760                 tmp->audio_region()->set_fade_out_shape (shape);
3761                 
3762                 XMLNode &after = alist.get_state();
3763                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3764         }
3765
3766         commit_reversible_command ();
3767 }
3768
3769 void
3770 Editor::set_fade_in_active (bool yn)
3771 {
3772         begin_reversible_command (_("set fade in active"));
3773
3774         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3775                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3776
3777                 if (!tmp) {
3778                         return;
3779                 }
3780
3781
3782                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3783
3784                 XMLNode &before = ar->get_state();
3785
3786                 ar->set_fade_in_active (yn);
3787                 
3788                 XMLNode &after = ar->get_state();
3789                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3790         }
3791 }
3792
3793 void
3794 Editor::set_fade_out_active (bool yn)
3795 {
3796         begin_reversible_command (_("set fade out active"));
3797
3798         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3799                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3800
3801                 if (!tmp) {
3802                         return;
3803                 }
3804
3805                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3806
3807                 XMLNode &before = ar->get_state();
3808
3809                 ar->set_fade_out_active (yn);
3810                 
3811                 XMLNode &after = ar->get_state();
3812                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3813         }
3814 }
3815