a variety of bits and pieces for selection ops
[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                 /* meaningless, because we're selecting everything */
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                 selection->add (touched);
1335                 break;
1336         case Selection::Toggle:
1337                 selection->add (touched);
1338                 break;
1339         case Selection::Set:
1340                 selection->set (touched);
1341                 break;
1342         case Selection::Extend:
1343                 /* meaningless, because we're selecting everything */
1344                 break;
1345         }
1346         commit_reversible_command ();
1347 }
1348
1349 void
1350 Editor::invert_selection_in_track ()
1351 {
1352         list<Selectable *> touched;
1353
1354         if (!clicked_trackview) {
1355                 return;
1356         }
1357         
1358         clicked_trackview->get_inverted_selectables (*selection, touched);
1359         selection->set (touched);
1360 }
1361
1362 void
1363 Editor::invert_selection ()
1364 {
1365         list<Selectable *> touched;
1366         
1367         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1368                 if ((*iter)->hidden()) {
1369                         continue;
1370                 }
1371                 (*iter)->get_inverted_selectables (*selection, touched);
1372         }
1373
1374         selection->set (touched);
1375 }
1376
1377 bool
1378 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, Selection::Operation op)
1379 {
1380         list<Selectable*> touched;
1381         list<Selectable*>::size_type n = 0;
1382         TrackViewList touched_tracks;
1383
1384         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1385                 if ((*iter)->hidden()) {
1386                         continue;
1387                 }
1388
1389                 n = touched.size();
1390
1391                 (*iter)->get_selectables (start, end, top, bot, touched);
1392
1393                 if (n != touched.size()) {
1394                         touched_tracks.push_back (*iter);
1395                 }
1396         }
1397
1398         if (!touched_tracks.empty()) {
1399                 switch (op) {
1400                 case Selection::Add:
1401                         selection->add (touched_tracks);
1402                         break;
1403                 case Selection::Toggle:
1404                         selection->toggle (touched_tracks);
1405                         break;
1406                 case Selection::Set:
1407                         selection->set (touched_tracks);
1408                         break;
1409                 case Selection::Extend:
1410                         /* not defined yet */
1411                         break;
1412                 }
1413         }
1414                 
1415         begin_reversible_command (_("select all within"));
1416         switch (op) {
1417         case Selection::Add:
1418                 selection->add (touched);
1419                 break;
1420         case Selection::Toggle:
1421                 selection->toggle (touched);
1422                 break;
1423         case Selection::Set:
1424                 selection->set (touched);
1425                 break;
1426         case Selection::Extend:
1427                 /* not defined yet */
1428                 break;
1429         }
1430
1431         commit_reversible_command ();
1432         return !touched.empty();
1433 }
1434
1435 void
1436 Editor::set_selection_from_audio_region ()
1437 {
1438         if (selection->regions.empty()) {
1439                 return;
1440         }
1441
1442         RegionView* rv = *(selection->regions.begin());
1443         boost::shared_ptr<Region> region = rv->region();
1444         
1445         begin_reversible_command (_("set selection from region"));
1446         selection->set (0, region->position(), region->last_frame());
1447         commit_reversible_command ();
1448
1449         set_mouse_mode (Editing::MouseRange, false);
1450 }
1451
1452 void
1453 Editor::set_selection_from_punch()
1454 {
1455         Location* location;
1456
1457         if ((location = session->locations()->auto_punch_location()) == 0)  {
1458                 return;
1459         }
1460
1461         set_selection_from_range (*location);
1462 }
1463
1464 void
1465 Editor::set_selection_from_loop()
1466 {
1467         Location* location;
1468
1469         if ((location = session->locations()->auto_loop_location()) == 0)  {
1470                 return;
1471         }
1472         set_selection_from_range (*location);
1473 }
1474
1475 void
1476 Editor::set_selection_from_range (Location& loc)
1477 {
1478         begin_reversible_command (_("set selection from range"));
1479         selection->set (0, loc.start(), loc.end());
1480         commit_reversible_command ();
1481
1482         set_mouse_mode (Editing::MouseRange, false);
1483 }
1484
1485 void
1486 Editor::select_all_selectables_using_time_selection ()
1487 {
1488         list<Selectable *> touched;
1489
1490         if (selection->time.empty()) {
1491                 return;
1492         }
1493
1494         nframes_t start = selection->time[clicked_selection].start;
1495         nframes_t end = selection->time[clicked_selection].end;
1496
1497         if (end - start < 1)  {
1498                 return;
1499         }
1500
1501         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
1502                 if ((*iter)->hidden()) {
1503                         continue;
1504                 }
1505                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1506         }
1507
1508         begin_reversible_command (_("select all from range"));
1509         selection->set (touched);
1510         commit_reversible_command ();
1511 }
1512
1513
1514 void
1515 Editor::select_all_selectables_using_punch()
1516 {
1517         Location* location = session->locations()->auto_punch_location();
1518         list<Selectable *> touched;
1519
1520         if (location == 0 || (location->end() - location->start() <= 1))  {
1521                 return;
1522         }
1523
1524         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1525                 if ((*iter)->hidden()) {
1526                         continue;
1527                 }
1528                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1529         }
1530         begin_reversible_command (_("select all from punch"));
1531         selection->set (touched);
1532         commit_reversible_command ();
1533
1534 }
1535
1536 void
1537 Editor::select_all_selectables_using_loop()
1538 {
1539         Location* location = session->locations()->auto_loop_location();
1540         list<Selectable *> touched;
1541
1542         if (location == 0 || (location->end() - location->start() <= 1))  {
1543                 return;
1544         }
1545
1546         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1547                 if ((*iter)->hidden()) {
1548                         continue;
1549                 }
1550                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1551         }
1552         begin_reversible_command (_("select all from loop"));
1553         selection->set (touched);
1554         commit_reversible_command ();
1555
1556 }
1557
1558 void
1559 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1560 {
1561         nframes_t start;
1562         nframes_t end;
1563         list<Selectable *> touched;
1564
1565         if (after) {
1566                 begin_reversible_command (_("select all after cursor"));
1567                 start = cursor->current_frame ;
1568                 end = session->current_end_frame();
1569         } else {
1570                 if (cursor->current_frame > 0) {
1571                         begin_reversible_command (_("select all before cursor"));
1572                         start = 0;
1573                         end = cursor->current_frame - 1;
1574                 } else {
1575                         return;
1576                 }
1577         }
1578
1579         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1580                 if ((*iter)->hidden()) {
1581                         continue;
1582                 }
1583                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1584         }
1585         selection->set (touched);
1586         commit_reversible_command ();
1587 }
1588
1589 void
1590 Editor::select_all_selectables_between_cursors (Cursor *cursor, Cursor *other_cursor)
1591 {
1592         nframes_t start;
1593         nframes_t end;
1594         list<Selectable *> touched;
1595         bool  other_cursor_is_first = cursor->current_frame > other_cursor->current_frame;
1596
1597         if (cursor->current_frame == other_cursor->current_frame) {
1598                 return;
1599         }
1600
1601         begin_reversible_command (_("select all between cursors"));
1602         if (other_cursor_is_first) {
1603                 start = other_cursor->current_frame;
1604                 end = cursor->current_frame - 1;
1605                 
1606         } else {
1607                 start = cursor->current_frame;
1608                 end = other_cursor->current_frame - 1;
1609         }
1610         
1611         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1612                 if ((*iter)->hidden()) {
1613                         continue;
1614                 }
1615                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1616         }
1617         selection->set (touched);
1618         commit_reversible_command ();
1619 }
1620
1621 void
1622 Editor::amplitude_zoom_step (bool in)
1623 {
1624         gdouble zoom = 1.0;
1625
1626         if (in) {
1627                 zoom *= 2.0;
1628         } else {
1629                 if (zoom > 2.0) {
1630                         zoom /= 2.0;
1631                 } else {
1632                         zoom = 1.0;
1633                 }
1634         }
1635
1636 #ifdef FIX_FOR_CANVAS
1637         /* XXX DO SOMETHING */
1638 #endif
1639 }       
1640
1641
1642 /* DELETION */
1643
1644
1645 void
1646 Editor::delete_sample_forward ()
1647 {
1648 }
1649
1650 void
1651 Editor::delete_sample_backward ()
1652 {
1653 }
1654
1655 void
1656 Editor::delete_screen ()
1657 {
1658 }
1659
1660 /* SEARCH */
1661
1662 void
1663 Editor::search_backwards ()
1664 {
1665         /* what ? */
1666 }
1667
1668 void
1669 Editor::search_forwards ()
1670 {
1671         /* what ? */
1672 }
1673
1674 /* MARKS */
1675
1676 void
1677 Editor::jump_forward_to_mark ()
1678 {
1679         if (!session) {
1680                 return;
1681         }
1682         
1683         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1684
1685         if (location) {
1686                 session->request_locate (location->start(), session->transport_rolling());
1687         } else {
1688                 session->request_locate (session->current_end_frame());
1689         }
1690 }
1691
1692 void
1693 Editor::jump_backward_to_mark ()
1694 {
1695         if (!session) {
1696                 return;
1697         }
1698
1699         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1700         
1701         if (location) {
1702                 session->request_locate (location->start(), session->transport_rolling());
1703         } else {
1704                 session->goto_start ();
1705         }
1706 }
1707
1708 void
1709 Editor::set_mark ()
1710 {
1711         nframes_t pos;
1712         float prefix;
1713         bool was_floating;
1714         string markername;
1715
1716         if (get_prefix (prefix, was_floating)) {
1717                 pos = session->audible_frame ();
1718         } else {
1719                 if (was_floating) {
1720                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1721                 } else {
1722                         pos = (nframes_t) floor (prefix);
1723                 }
1724         }
1725
1726         session->locations()->next_available_name(markername,"mark");
1727         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1728 }
1729
1730 void
1731 Editor::clear_markers ()
1732 {
1733         if (session) {
1734                 session->begin_reversible_command (_("clear markers"));
1735                 XMLNode &before = session->locations()->get_state();
1736                 session->locations()->clear_markers ();
1737                 XMLNode &after = session->locations()->get_state();
1738                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1739                 session->commit_reversible_command ();
1740         }
1741 }
1742
1743 void
1744 Editor::clear_ranges ()
1745 {
1746         if (session) {
1747                 session->begin_reversible_command (_("clear ranges"));
1748                 XMLNode &before = session->locations()->get_state();
1749                 
1750                 Location * looploc = session->locations()->auto_loop_location();
1751                 Location * punchloc = session->locations()->auto_punch_location();
1752                 
1753                 session->locations()->clear_ranges ();
1754                 // re-add these
1755                 if (looploc) session->locations()->add (looploc);
1756                 if (punchloc) session->locations()->add (punchloc);
1757                 
1758                 XMLNode &after = session->locations()->get_state();
1759                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1760                 session->commit_reversible_command ();
1761         }
1762 }
1763
1764 void
1765 Editor::clear_locations ()
1766 {
1767         session->begin_reversible_command (_("clear locations"));
1768         XMLNode &before = session->locations()->get_state();
1769         session->locations()->clear ();
1770         XMLNode &after = session->locations()->get_state();
1771         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1772         session->commit_reversible_command ();
1773         session->locations()->clear ();
1774 }
1775
1776 void
1777 Editor::unhide_markers ()
1778 {
1779         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1780                 Location *l = (*i).first;
1781                 if (l->is_hidden() && l->is_mark()) {
1782                         l->set_hidden(false, this);
1783                 }
1784         }
1785 }
1786
1787 void
1788 Editor::unhide_ranges ()
1789 {
1790         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1791                 Location *l = (*i).first;
1792                 if (l->is_hidden() && l->is_range_marker()) { 
1793                         l->set_hidden(false, this);
1794                 }
1795         }
1796 }
1797
1798 /* INSERT/REPLACE */
1799
1800 void
1801 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
1802 {
1803         double wx, wy;
1804         double cx, cy;
1805         TimeAxisView *tv;
1806         nframes_t where;
1807         AudioTimeAxisView *atv = 0;
1808         boost::shared_ptr<Playlist> playlist;
1809         
1810         track_canvas.window_to_world (x, y, wx, wy);
1811         wx += horizontal_adjustment.get_value();
1812         wy += vertical_adjustment.get_value();
1813
1814         GdkEvent event;
1815         event.type = GDK_BUTTON_RELEASE;
1816         event.button.x = wx;
1817         event.button.y = wy;
1818         
1819         where = event_frame (&event, &cx, &cy);
1820
1821         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1822                 /* clearly outside canvas area */
1823                 return;
1824         }
1825         
1826         if ((tv = trackview_by_y_position (cy)) == 0) {
1827                 return;
1828         }
1829         
1830         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
1831                 return;
1832         }
1833
1834         if ((playlist = atv->playlist()) == 0) {
1835                 return;
1836         }
1837         
1838         cerr << "drop target playlist, UC  = " << playlist.use_count() << endl;
1839
1840         snap_to (where);
1841         
1842         begin_reversible_command (_("insert dragged region"));
1843         XMLNode &before = playlist->get_state();
1844         cerr << "pre add target playlist, UC  = " << playlist.use_count() << endl;
1845         playlist->add_region (RegionFactory::create (region), where, 1.0);
1846         cerr << "post add target playlist, UC  = " << playlist.use_count() << endl;
1847         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1848         commit_reversible_command ();
1849
1850         cerr << "post drop target playlist, UC  = " << playlist.use_count() << endl;
1851 }
1852
1853 void
1854 Editor::insert_region_list_selection (float times)
1855 {
1856         RouteTimeAxisView *tv = 0;
1857         boost::shared_ptr<Playlist> playlist;
1858
1859         if (clicked_audio_trackview != 0) {
1860                 tv = clicked_audio_trackview;
1861         } else if (!selection->tracks.empty()) {
1862                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
1863                         return;
1864                 }
1865         } else {
1866                 return;
1867         }
1868
1869         if ((playlist = tv->playlist()) == 0) {
1870                 return;
1871         }
1872         
1873         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
1874         
1875         if (selected->count_selected_rows() != 1) {
1876                 return;
1877         }
1878         
1879         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
1880
1881         /* only one row selected, so rows.begin() is it */
1882
1883         TreeIter iter;
1884
1885         if ((iter = region_list_model->get_iter (*rows.begin()))) {
1886
1887                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
1888                 
1889                 begin_reversible_command (_("insert region"));
1890                 XMLNode &before = playlist->get_state();
1891                 playlist->add_region ((RegionFactory::create (region)), edit_cursor->current_frame, times);
1892                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1893                 commit_reversible_command ();
1894         } 
1895 }
1896
1897 /* BUILT-IN EFFECTS */
1898
1899 void
1900 Editor::reverse_selection ()
1901 {
1902
1903 }
1904
1905 /* GAIN ENVELOPE EDITING */
1906
1907 void
1908 Editor::edit_envelope ()
1909 {
1910 }
1911
1912 /* PLAYBACK */
1913
1914 void
1915 Editor::toggle_playback (bool with_abort)
1916 {
1917         if (!session) {
1918                 return;
1919         }
1920
1921         switch (Config->get_slave_source()) {
1922         case None:
1923         case JACK:
1924                 break;
1925         default:
1926                 /* transport controlled by the master */
1927                 return;
1928         }
1929
1930         if (session->is_auditioning()) {
1931                 session->cancel_audition ();
1932                 return;
1933         }
1934         
1935         if (session->transport_rolling()) {
1936                 session->request_stop (with_abort);
1937                 if (session->get_play_loop()) {
1938                         session->request_play_loop (false);
1939                 }
1940         } else {
1941                 session->request_transport_speed (1.0f);
1942         }
1943 }
1944
1945 void
1946 Editor::play_from_start ()
1947 {
1948         session->request_locate (session->current_start_frame(), true);
1949 }
1950
1951 void
1952 Editor::play_from_edit_cursor ()
1953 {
1954        session->request_locate (edit_cursor->current_frame, true);
1955 }
1956
1957 void
1958 Editor::play_selection ()
1959 {
1960         if (selection->time.empty()) {
1961                 return;
1962         }
1963
1964         session->request_play_range (true);
1965 }
1966
1967 void
1968 Editor::play_selected_region ()
1969 {
1970         if (!selection->regions.empty()) {
1971                 RegionView *rv = *(selection->regions.begin());
1972
1973                 session->request_bounded_roll (rv->region()->position(), rv->region()->last_frame());   
1974         }
1975 }
1976
1977 void
1978 Editor::loop_selected_region ()
1979 {
1980         if (!selection->regions.empty()) {
1981                 RegionView *rv = *(selection->regions.begin());
1982                 Location* tll;
1983
1984                 if ((tll = transport_loop_location()) != 0)  {
1985
1986                         tll->set (rv->region()->position(), rv->region()->last_frame());
1987                         
1988                         // enable looping, reposition and start rolling
1989
1990                         session->request_play_loop (true);
1991                         session->request_locate (tll->start(), false);
1992                         session->request_transport_speed (1.0f);
1993                 }
1994         }
1995 }
1996
1997 void
1998 Editor::play_location (Location& location)
1999 {
2000         if (location.start() <= location.end()) {
2001                 return;
2002         }
2003
2004         session->request_bounded_roll (location.start(), location.end());
2005 }
2006
2007 void
2008 Editor::loop_location (Location& location)
2009 {
2010         if (location.start() <= location.end()) {
2011                 return;
2012         }
2013
2014         Location* tll;
2015
2016         if ((tll = transport_loop_location()) != 0) {
2017                 tll->set (location.start(), location.end());
2018
2019                 // enable looping, reposition and start rolling
2020                 session->request_play_loop (true);
2021                 session->request_locate (tll->start(), true);
2022         }
2023 }
2024
2025 void
2026 Editor::raise_region ()
2027 {
2028         selection->foreach_region (&Region::raise);
2029 }
2030
2031 void
2032 Editor::raise_region_to_top ()
2033 {
2034         selection->foreach_region (&Region::raise_to_top);
2035 }
2036
2037 void
2038 Editor::lower_region ()
2039 {
2040         selection->foreach_region (&Region::lower);
2041 }
2042
2043 void
2044 Editor::lower_region_to_bottom ()
2045 {
2046         selection->foreach_region (&Region::lower_to_bottom);
2047 }
2048
2049 void
2050 Editor::edit_region ()
2051 {
2052         if (clicked_regionview == 0) {
2053                 return;
2054         }
2055         
2056         clicked_regionview->show_region_editor ();
2057 }
2058
2059 void
2060 Editor::rename_region ()
2061 {
2062         Dialog dialog;
2063         Entry  entry;
2064         Button ok_button (_("OK"));
2065         Button cancel_button (_("Cancel"));
2066
2067         if (selection->regions.empty()) {
2068                 return;
2069         }
2070
2071         dialog.set_title (_("ardour: rename region"));
2072         dialog.set_name ("RegionRenameWindow");
2073         dialog.set_size_request (300, -1);
2074         dialog.set_position (Gtk::WIN_POS_MOUSE);
2075         dialog.set_modal (true);
2076
2077         dialog.get_vbox()->set_border_width (10);
2078         dialog.get_vbox()->pack_start (entry);
2079         dialog.get_action_area()->pack_start (ok_button);
2080         dialog.get_action_area()->pack_start (cancel_button);
2081
2082         entry.set_name ("RegionNameDisplay");
2083         ok_button.set_name ("EditorGTKButton");
2084         cancel_button.set_name ("EditorGTKButton");
2085
2086         region_renamed = false;
2087
2088         entry.signal_activate().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2089         ok_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2090         cancel_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), false));
2091
2092         /* recurse */
2093
2094         dialog.show_all ();
2095         Main::run ();
2096
2097         if (region_renamed) {
2098                 (*selection->regions.begin())->region()->set_name (entry.get_text());
2099                 redisplay_regions ();
2100         }
2101 }
2102
2103 void
2104 Editor::rename_region_finished (bool status)
2105
2106 {
2107         region_renamed = status;
2108         Main::quit ();
2109 }
2110
2111 void
2112 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2113 {
2114         if (session->is_auditioning()) {
2115                 session->cancel_audition ();
2116         } 
2117
2118         // note: some potential for creativity here, because region doesn't
2119         // have to belong to the playlist that Route is handling
2120
2121         // bool was_soloed = route.soloed();
2122
2123         route.set_solo (true, this);
2124         
2125         session->request_bounded_roll (region->position(), region->position() + region->length());
2126         
2127         /* XXX how to unset the solo state ? */
2128 }
2129
2130 void
2131 Editor::audition_selected_region ()
2132 {
2133         if (!selection->regions.empty()) {
2134                 RegionView* rv = *(selection->regions.begin());
2135                 session->audition_region (rv->region());
2136         }
2137 }
2138
2139 void
2140 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2141 {
2142         session->audition_region (region);
2143 }
2144
2145 void
2146 Editor::build_interthread_progress_window ()
2147 {
2148         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2149
2150         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2151         
2152         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2153         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2154
2155         // GTK2FIX: this button needs a modifiable label
2156
2157         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2158         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2159
2160         interthread_cancel_button.add (interthread_cancel_label);
2161
2162         interthread_progress_window->set_default_size (200, 100);
2163 }
2164
2165 void
2166 Editor::interthread_cancel_clicked ()
2167 {
2168         if (current_interthread_info) {
2169                 current_interthread_info->cancel = true;
2170         }
2171 }
2172
2173 void
2174 Editor::region_from_selection ()
2175 {
2176         if (clicked_trackview == 0) {
2177                 return;
2178         }
2179
2180         if (selection->time.empty()) {
2181                 return;
2182         }
2183
2184         nframes_t start = selection->time[clicked_selection].start;
2185         nframes_t end = selection->time[clicked_selection].end;
2186
2187         nframes_t selection_cnt = end - start + 1;
2188         
2189         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2190                 boost::shared_ptr<AudioRegion> current;
2191                 boost::shared_ptr<Region> current_r;
2192                 boost::shared_ptr<Playlist> pl;
2193
2194                 nframes_t internal_start;
2195                 string new_name;
2196
2197                 if ((pl = (*i)->playlist()) == 0) {
2198                         continue;
2199                 }
2200
2201                 if ((current_r = pl->top_region_at (start)) == 0) {
2202                         continue;
2203                 }
2204
2205                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2206                 // FIXME: audio only
2207                 if (current != 0) {
2208                         internal_start = start - current->position();
2209                         session->region_name (new_name, current->name(), true);
2210                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2211                 }
2212         }
2213 }       
2214
2215 void
2216 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2217 {
2218         if (selection->time.empty() || selection->tracks.empty()) {
2219                 return;
2220         }
2221
2222         nframes_t start = selection->time[clicked_selection].start;
2223         nframes_t end = selection->time[clicked_selection].end;
2224         
2225         sort_track_selection ();
2226
2227         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2228
2229                 boost::shared_ptr<AudioRegion> current;
2230                 boost::shared_ptr<Region> current_r;
2231                 boost::shared_ptr<Playlist> playlist;
2232                 nframes_t internal_start;
2233                 string new_name;
2234
2235                 if ((playlist = (*i)->playlist()) == 0) {
2236                         continue;
2237                 }
2238
2239                 if ((current_r = playlist->top_region_at(start)) == 0) {
2240                         continue;
2241                 }
2242
2243                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2244                         continue;
2245                 }
2246         
2247                 internal_start = start - current->position();
2248                 session->region_name (new_name, current->name(), true);
2249                 
2250                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2251         }
2252 }
2253
2254 void
2255 Editor::split_multichannel_region ()
2256 {
2257         vector<AudioRegion*> v;
2258
2259         AudioRegionView* clicked_arv = dynamic_cast<AudioRegionView*>(clicked_regionview);
2260         
2261         if (!clicked_arv || clicked_arv->audio_region()->n_channels() < 2) {
2262                 return;
2263         }
2264
2265         clicked_arv->audio_region()->separate_by_channel (*session, v);
2266
2267         /* nothing else to do, really */
2268 }
2269
2270 void
2271 Editor::new_region_from_selection ()
2272 {
2273         region_from_selection ();
2274         cancel_selection ();
2275 }
2276
2277 void
2278 Editor::separate_region_from_selection ()
2279 {
2280         bool doing_undo = false;
2281
2282         if (selection->time.empty()) {
2283                 return;
2284         }
2285
2286         boost::shared_ptr<Playlist> playlist;
2287                 
2288         sort_track_selection ();
2289
2290         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2291
2292                 AudioTimeAxisView* atv;
2293
2294                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2295
2296                         if (atv->is_audio_track()) {
2297                                         
2298                                 if ((playlist = atv->playlist()) != 0) {
2299                                         if (!doing_undo) {
2300                                                 begin_reversible_command (_("separate"));
2301                                                 doing_undo = true;
2302                                         }
2303                                         XMLNode *before;
2304                                         if (doing_undo) 
2305                                             before = &(playlist->get_state());
2306                         
2307                                         /* XXX need to consider musical time selections here at some point */
2308
2309                                         double speed = atv->get_diskstream()->speed();
2310
2311                                         for (list<AudioRange>::iterator t = selection->time.begin(); t != selection->time.end(); ++t) {
2312                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2313                                         }
2314
2315                                         if (doing_undo) 
2316                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2317                                 }
2318                         }
2319                 }
2320         }
2321
2322         if (doing_undo) commit_reversible_command ();
2323 }
2324
2325 void
2326 Editor::separate_regions_using_location (Location& loc)
2327 {
2328         bool doing_undo = false;
2329
2330         if (loc.is_mark()) {
2331                 return;
2332         }
2333
2334         boost::shared_ptr<Playlist> playlist;
2335
2336         /* XXX i'm unsure as to whether this should operate on selected tracks only 
2337            or the entire enchillada. uncomment the below line to correct the behaviour 
2338            (currently set for all tracks)
2339         */
2340
2341         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
2342         //for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2343
2344                 AudioTimeAxisView* atv;
2345
2346                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2347
2348                         if (atv->is_audio_track()) {
2349                                         
2350                                 if ((playlist = atv->playlist()) != 0) {
2351                                         XMLNode *before;
2352                                         if (!doing_undo) {
2353                                                 begin_reversible_command (_("separate"));
2354                                                 doing_undo = true;
2355                                         }
2356                                         if (doing_undo) 
2357                                             before = &(playlist->get_state());
2358                                             
2359                         
2360                                         /* XXX need to consider musical time selections here at some point */
2361
2362                                         double speed = atv->get_diskstream()->speed();
2363
2364
2365                                         playlist->partition ((nframes_t)(loc.start() * speed), (nframes_t)(loc.end() * speed), true);
2366                                         if (doing_undo) 
2367                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2368                                 }
2369                         }
2370                 }
2371         }
2372
2373         if (doing_undo) commit_reversible_command ();
2374 }
2375
2376 void
2377 Editor::crop_region_to_selection ()
2378 {
2379         if (selection->time.empty()) {
2380                 return;
2381         }
2382
2383         vector<boost::shared_ptr<Playlist> > playlists;
2384         boost::shared_ptr<Playlist> playlist;
2385
2386         if (clicked_trackview != 0) {
2387
2388                 if ((playlist = clicked_trackview->playlist()) == 0) {
2389                         return;
2390                 }
2391
2392                 playlists.push_back (playlist);
2393
2394         } else {
2395                 
2396                 sort_track_selection ();
2397
2398                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2399
2400                         AudioTimeAxisView* atv;
2401
2402                         if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2403
2404                                 if (atv->is_audio_track()) {
2405                                         
2406                                         if ((playlist = atv->playlist()) != 0) {
2407                                                 playlists.push_back (playlist);
2408                                         }
2409                                 }
2410                         }
2411                 }
2412         }
2413
2414         if (!playlists.empty()) {
2415
2416                 nframes_t start;
2417                 nframes_t end;
2418                 nframes_t cnt;
2419
2420                 begin_reversible_command (_("trim to selection"));
2421
2422                 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2423                         
2424                         boost::shared_ptr<Region> region;
2425                         
2426                         start = selection->time.start();
2427
2428                         if ((region = (*i)->top_region_at(start)) == 0) {
2429                                 continue;
2430                         }
2431                         
2432                         /* now adjust lengths to that we do the right thing
2433                            if the selection extends beyond the region
2434                         */
2435                         
2436                         start = max (start, region->position());
2437                         end = min (selection->time.end_frame(), start + region->length() - 1);
2438                         cnt = end - start + 1;
2439
2440                         XMLNode &before = (*i)->get_state();
2441                         region->trim_to (start, cnt, this);
2442                         XMLNode &after = (*i)->get_state();
2443                         session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2444                 }
2445
2446                 commit_reversible_command ();
2447         }
2448 }               
2449
2450 void
2451 Editor::region_fill_track ()
2452 {
2453         nframes_t end;
2454
2455         if (!session || selection->regions.empty()) {
2456                 return;
2457         }
2458
2459         end = session->current_end_frame ();
2460
2461         begin_reversible_command (_("region fill"));
2462
2463         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2464
2465                 boost::shared_ptr<Region> region ((*i)->region());
2466                 
2467                 // FIXME
2468                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2469                 if (!ar)
2470                         continue;
2471
2472                 boost::shared_ptr<Playlist> pl = region->playlist();
2473
2474                 if (end <= region->last_frame()) {
2475                         return;
2476                 }
2477
2478                 double times = (double) (end - region->last_frame()) / (double) region->length();
2479
2480                 if (times == 0) {
2481                         return;
2482                 }
2483
2484                 XMLNode &before = pl->get_state();
2485                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2486                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2487         }
2488
2489         commit_reversible_command ();
2490 }
2491
2492 void
2493 Editor::region_fill_selection ()
2494 {
2495         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2496                 return;
2497         }
2498
2499         if (selection->time.empty()) {
2500                 return;
2501         }
2502
2503
2504         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2505
2506         if (selected->count_selected_rows() != 1) {
2507                 return;
2508         }
2509
2510         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2511         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2512
2513         nframes_t start = selection->time[clicked_selection].start;
2514         nframes_t end = selection->time[clicked_selection].end;
2515
2516         boost::shared_ptr<Playlist> playlist; 
2517
2518         if (selection->tracks.empty()) {
2519                 return;
2520         }
2521
2522         nframes_t selection_length = end - start;
2523         float times = (float)selection_length / region->length();
2524         
2525         begin_reversible_command (_("fill selection"));
2526         
2527         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2528
2529                 if ((playlist = (*i)->playlist()) == 0) {
2530                         continue;
2531                 }               
2532                 
2533                 XMLNode &before = playlist->get_state();
2534                 playlist->add_region (RegionFactory::create (region), start, times);
2535                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2536         }
2537         
2538         commit_reversible_command ();                   
2539 }
2540
2541 void
2542 Editor::set_a_regions_sync_position (boost::shared_ptr<Region> region, nframes_t position)
2543 {
2544
2545         if (!region->covers (position)) {
2546           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2547                 return;
2548         }
2549         begin_reversible_command (_("set region sync position"));
2550         XMLNode &before = region->playlist()->get_state();
2551         region->set_sync_position (position);
2552         XMLNode &after = region->playlist()->get_state();
2553         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2554         commit_reversible_command ();
2555 }
2556
2557 void
2558 Editor::set_region_sync_from_edit_cursor ()
2559 {
2560         if (clicked_regionview == 0) {
2561                 return;
2562         }
2563
2564         if (!clicked_regionview->region()->covers (edit_cursor->current_frame)) {
2565                 error << _("Place the edit cursor at the desired sync point") << endmsg;
2566                 return;
2567         }
2568
2569         boost::shared_ptr<Region> region (clicked_regionview->region());
2570         begin_reversible_command (_("set sync from edit cursor"));
2571         XMLNode &before = region->playlist()->get_state();
2572         region->set_sync_position (edit_cursor->current_frame);
2573         XMLNode &after = region->playlist()->get_state();
2574         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2575         commit_reversible_command ();
2576 }
2577
2578 void
2579 Editor::remove_region_sync ()
2580 {
2581         if (clicked_regionview) {
2582                 boost::shared_ptr<Region> region (clicked_regionview->region());
2583                 begin_reversible_command (_("remove sync"));
2584                 XMLNode &before = region->playlist()->get_state();
2585                 region->clear_sync_position ();
2586                 XMLNode &after = region->playlist()->get_state();
2587                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2588                 commit_reversible_command ();
2589         }
2590 }
2591
2592 void
2593 Editor::naturalize ()
2594 {
2595         if (selection->regions.empty()) {
2596                 return;
2597         }
2598         begin_reversible_command (_("naturalize"));
2599         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2600                 XMLNode &before = (*i)->region()->get_state();
2601                 (*i)->region()->move_to_natural_position (this);
2602                 XMLNode &after = (*i)->region()->get_state();
2603                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2604         }
2605         commit_reversible_command ();
2606 }
2607
2608 void
2609 Editor::align (RegionPoint what)
2610 {
2611         align_selection (what, edit_cursor->current_frame);
2612 }
2613
2614 void
2615 Editor::align_relative (RegionPoint what)
2616 {
2617         align_selection_relative (what, edit_cursor->current_frame);
2618 }
2619
2620 struct RegionSortByTime {
2621     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2622             return a->region()->position() < b->region()->position();
2623     }
2624 };
2625
2626 void
2627 Editor::align_selection_relative (RegionPoint point, nframes_t position)
2628 {
2629         if (selection->regions.empty()) {
2630                 return;
2631         }
2632
2633         nframes_t distance;
2634         nframes_t pos = 0;
2635         int dir;
2636
2637         list<RegionView*> sorted;
2638         selection->regions.by_position (sorted);
2639         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2640
2641         switch (point) {
2642         case Start:
2643                 pos = r->first_frame ();
2644                 break;
2645
2646         case End:
2647                 pos = r->last_frame();
2648                 break;
2649
2650         case SyncPoint:
2651                 pos = r->adjust_to_sync (r->first_frame());
2652                 break;  
2653         }
2654
2655         if (pos > position) {
2656                 distance = pos - position;
2657                 dir = -1;
2658         } else {
2659                 distance = position - pos;
2660                 dir = 1;
2661         }
2662
2663         begin_reversible_command (_("align selection (relative)"));
2664
2665         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2666
2667                 boost::shared_ptr<Region> region ((*i)->region());
2668
2669                 XMLNode &before = region->playlist()->get_state();
2670                 
2671                 if (dir > 0) {
2672                         region->set_position (region->position() + distance, this);
2673                 } else {
2674                         region->set_position (region->position() - distance, this);
2675                 }
2676
2677                 XMLNode &after = region->playlist()->get_state();
2678                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2679
2680         }
2681
2682         commit_reversible_command ();
2683 }
2684
2685 void
2686 Editor::align_selection (RegionPoint point, nframes_t position)
2687 {
2688         if (selection->regions.empty()) {
2689                 return;
2690         }
2691
2692         begin_reversible_command (_("align selection"));
2693
2694         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2695                 align_region_internal ((*i)->region(), point, position);
2696         }
2697
2698         commit_reversible_command ();
2699 }
2700
2701 void
2702 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2703 {
2704         begin_reversible_command (_("align region"));
2705         align_region_internal (region, point, position);
2706         commit_reversible_command ();
2707 }
2708
2709 void
2710 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2711 {
2712         XMLNode &before = region->playlist()->get_state();
2713
2714         switch (point) {
2715         case SyncPoint:
2716                 region->set_position (region->adjust_to_sync (position), this);
2717                 break;
2718
2719         case End:
2720                 if (position > region->length()) {
2721                         region->set_position (position - region->length(), this);
2722                 }
2723                 break;
2724
2725         case Start:
2726                 region->set_position (position, this);
2727                 break;
2728         }
2729
2730         XMLNode &after = region->playlist()->get_state();
2731         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2732 }       
2733
2734 void
2735 Editor::trim_region_to_edit_cursor ()
2736 {
2737         if (clicked_regionview == 0) {
2738                 return;
2739         }
2740
2741         boost::shared_ptr<Region> region (clicked_regionview->region());
2742
2743         float speed = 1.0f;
2744         AudioTimeAxisView *atav;
2745
2746         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2747                 if (atav->get_diskstream() != 0) {
2748                         speed = atav->get_diskstream()->speed();
2749                 }
2750         }
2751
2752         begin_reversible_command (_("trim to edit"));
2753         XMLNode &before = region->playlist()->get_state();
2754         region->trim_end( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2755         XMLNode &after = region->playlist()->get_state();
2756         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2757         commit_reversible_command ();
2758 }
2759
2760 void
2761 Editor::trim_region_from_edit_cursor ()
2762 {
2763         if (clicked_regionview == 0) {
2764                 return;
2765         }
2766
2767         boost::shared_ptr<Region> region (clicked_regionview->region());
2768
2769         float speed = 1.0f;
2770         AudioTimeAxisView *atav;
2771
2772         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2773                 if (atav->get_diskstream() != 0) {
2774                         speed = atav->get_diskstream()->speed();
2775                 }
2776         }
2777
2778         begin_reversible_command (_("trim to edit"));
2779         XMLNode &before = region->playlist()->get_state();
2780         region->trim_front ( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2781         XMLNode &after = region->playlist()->get_state();
2782         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2783         commit_reversible_command ();
2784 }
2785
2786 void
2787 Editor::unfreeze_route ()
2788 {
2789         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2790                 return;
2791         }
2792         
2793         clicked_audio_trackview->audio_track()->unfreeze ();
2794 }
2795
2796 void*
2797 Editor::_freeze_thread (void* arg)
2798 {
2799         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2800         return static_cast<Editor*>(arg)->freeze_thread ();
2801 }
2802
2803 void*
2804 Editor::freeze_thread ()
2805 {
2806         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2807         return 0;
2808 }
2809
2810 gint
2811 Editor::freeze_progress_timeout (void *arg)
2812 {
2813         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2814         return !(current_interthread_info->done || current_interthread_info->cancel);
2815 }
2816
2817 void
2818 Editor::freeze_route ()
2819 {
2820         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2821                 return;
2822         }
2823         
2824         InterThreadInfo itt;
2825
2826         if (interthread_progress_window == 0) {
2827                 build_interthread_progress_window ();
2828         }
2829         
2830         interthread_progress_window->set_title (_("ardour: freeze"));
2831         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2832         interthread_progress_window->show_all ();
2833         interthread_progress_bar.set_fraction (0.0f);
2834         interthread_progress_label.set_text ("");
2835         interthread_cancel_label.set_text (_("Cancel Freeze"));
2836         current_interthread_info = &itt;
2837
2838         interthread_progress_connection = 
2839           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2840
2841         itt.done = false;
2842         itt.cancel = false;
2843         itt.progress = 0.0f;
2844         
2845         pthread_attr_t attr;
2846         pthread_attr_init(&attr);
2847         pthread_attr_setstacksize(&attr, 500000);
2848
2849         pthread_create (&itt.thread, &attr, _freeze_thread, this);
2850
2851         pthread_attr_destroy(&attr);
2852
2853         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2854
2855         while (!itt.done && !itt.cancel) {
2856                 gtk_main_iteration ();
2857         }
2858
2859         interthread_progress_connection.disconnect ();
2860         interthread_progress_window->hide_all ();
2861         current_interthread_info = 0;
2862         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2863 }
2864
2865 void
2866 Editor::bounce_range_selection ()
2867 {
2868         if (selection->time.empty()) {
2869                 return;
2870         }
2871
2872         TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
2873
2874         nframes_t start = selection->time[clicked_selection].start;
2875         nframes_t end = selection->time[clicked_selection].end;
2876         nframes_t cnt = end - start + 1;
2877         
2878         begin_reversible_command (_("bounce range"));
2879
2880         for (TrackViewList::iterator i = views->begin(); i != views->end(); ++i) {
2881
2882                 AudioTimeAxisView* atv;
2883
2884                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2885                         continue;
2886                 }
2887                 
2888                 boost::shared_ptr<Playlist> playlist;
2889                 
2890                 if ((playlist = atv->playlist()) == 0) {
2891                         return;
2892                 }
2893
2894                 InterThreadInfo itt;
2895                 
2896                 itt.done = false;
2897                 itt.cancel = false;
2898                 itt.progress = false;
2899                 
2900                 XMLNode &before = playlist->get_state();
2901                 atv->audio_track()->bounce_range (start, cnt, itt);
2902                 XMLNode &after = playlist->get_state();
2903                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
2904         }
2905         
2906         commit_reversible_command ();
2907         
2908         delete views;
2909 }
2910
2911 void
2912 Editor::cut ()
2913 {
2914         cut_copy (Cut);
2915 }
2916
2917 void
2918 Editor::copy ()
2919 {
2920         cut_copy (Copy);
2921 }
2922
2923 void 
2924 Editor::cut_copy (CutCopyOp op)
2925 {
2926         /* only cancel selection if cut/copy is successful.*/
2927
2928         string opname;
2929
2930         switch (op) {
2931         case Cut:
2932                 opname = _("cut");
2933                 break;
2934         case Copy:
2935                 opname = _("copy");
2936                 break;
2937         case Clear:
2938                 opname = _("clear");
2939                 break;
2940         }
2941         
2942         cut_buffer->clear ();
2943
2944         switch (current_mouse_mode()) {
2945         case MouseObject: 
2946                 if (!selection->regions.empty() || !selection->points.empty()) {
2947
2948                         begin_reversible_command (opname + _(" objects"));
2949
2950                         if (!selection->regions.empty()) {
2951                                 
2952                                 cut_copy_regions (op);
2953                                 
2954                                 if (op == Cut) {
2955                                         selection->clear_regions ();
2956                                 }
2957                         }
2958
2959                         if (!selection->points.empty()) {
2960                                 cut_copy_points (op);
2961
2962                                 if (op == Cut) {
2963                                         selection->clear_points ();
2964                                 }
2965                         }
2966
2967                         commit_reversible_command ();   
2968                 }
2969                 break;
2970                 
2971         case MouseRange:
2972                 if (!selection->time.empty()) {
2973
2974                         begin_reversible_command (opname + _(" range"));
2975                         cut_copy_ranges (op);
2976                         commit_reversible_command ();
2977
2978                         if (op == Cut) {
2979                                 selection->clear_time ();
2980                         }
2981                         
2982                 }
2983                 break;
2984                 
2985         default:
2986                 break;
2987         }
2988 }
2989
2990 void
2991 Editor::cut_copy_points (CutCopyOp op)
2992 {
2993         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
2994
2995                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
2996
2997                 if (atv) {
2998                         atv->cut_copy_clear_objects (selection->points, op);
2999                 } 
3000         }
3001 }
3002
3003 struct PlaylistState {
3004     boost::shared_ptr<Playlist> playlist;
3005     XMLNode*  before;
3006 };
3007
3008 struct lt_playlist {
3009     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3010             return a.playlist < b.playlist;
3011     }
3012 };
3013         
3014 struct PlaylistMapping { 
3015     TimeAxisView* tv;
3016     boost::shared_ptr<AudioPlaylist> pl;
3017
3018     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3019 };
3020
3021 void
3022 Editor::cut_copy_regions (CutCopyOp op)
3023 {
3024         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3025            a map when we want ordered access to both elements. i think.
3026         */
3027
3028         vector<PlaylistMapping> pmap;
3029
3030         nframes_t first_position = max_frames;
3031         
3032         set<PlaylistState, lt_playlist> freezelist;
3033         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3034         
3035         /* get ordering correct before we cut/copy */
3036         
3037         selection->regions.sort_by_position_and_track ();
3038
3039         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3040
3041                 first_position = min ((*x)->region()->position(), first_position);
3042
3043                 if (op == Cut || op == Clear) {
3044                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3045
3046                         if (pl) {
3047
3048                                 PlaylistState before;
3049                                 before.playlist = pl;
3050                                 before.before = &pl->get_state();
3051                                 
3052                                 insert_result = freezelist.insert (before);
3053                                 
3054                                 if (insert_result.second) {
3055                                         pl->freeze ();
3056                                 }
3057                         }
3058                 }
3059
3060                 TimeAxisView* tv = &(*x)->get_trackview();
3061                 vector<PlaylistMapping>::iterator z;
3062
3063                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3064                         if ((*z).tv == tv) {
3065                                 break;
3066                         }
3067                 }
3068                 
3069                 if (z == pmap.end()) {
3070                         pmap.push_back (PlaylistMapping (tv));
3071                 }
3072         }
3073
3074         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3075
3076                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3077                 
3078                 if (!pl) {
3079                         /* impossible, but this handles it for the future */
3080                         continue;
3081                 }
3082
3083                 TimeAxisView& tv = (*x)->get_trackview();
3084                 boost::shared_ptr<AudioPlaylist> npl;
3085                 RegionSelection::iterator tmp;
3086                 
3087                 tmp = x;
3088                 ++tmp;
3089
3090                 vector<PlaylistMapping>::iterator z;
3091                 
3092                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3093                         if ((*z).tv == &tv) {
3094                                 break;
3095                         }
3096                 }
3097                 
3098                 assert (z != pmap.end());
3099                 
3100                 if (!(*z).pl) {
3101                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3102                         npl->freeze();
3103                         (*z).pl = npl;
3104                 } else {
3105                         npl = (*z).pl;
3106                 }
3107                 
3108                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3109                 
3110                 switch (op) {
3111                 case Cut:
3112                         if (!ar) break;
3113                         
3114                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3115                         pl->remove_region (((*x)->region()));
3116                         break;
3117                         
3118                 case Copy:
3119                         if (!ar) break;
3120                         
3121                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3122                         break;
3123                         
3124                 case Clear:
3125                         pl->remove_region (((*x)->region()));
3126                         break;
3127                 }
3128
3129                 x = tmp;
3130         }
3131         
3132         list<boost::shared_ptr<Playlist> > foo;
3133         
3134         /* the pmap is in the same order as the tracks in which selected regions occured */
3135         
3136         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3137                 (*i).pl->thaw();
3138                 foo.push_back ((*i).pl);
3139         }
3140         
3141         if (!foo.empty()) {
3142                 cut_buffer->set (foo);
3143         }
3144         
3145         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3146                 (*pl).playlist->thaw ();
3147                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3148         }
3149 }
3150
3151 void
3152 Editor::cut_copy_ranges (CutCopyOp op)
3153 {
3154         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3155                 (*i)->cut_copy_clear (*selection, op);
3156         }
3157 }
3158
3159 void
3160 Editor::paste (float times)
3161 {
3162         paste_internal (edit_cursor->current_frame, times);
3163 }
3164
3165 void
3166 Editor::mouse_paste ()
3167 {
3168         int x, y;
3169         double wx, wy;
3170
3171         track_canvas.get_pointer (x, y);
3172         track_canvas.window_to_world (x, y, wx, wy);
3173         wx += horizontal_adjustment.get_value();
3174         wy += vertical_adjustment.get_value();
3175
3176         GdkEvent event;
3177         event.type = GDK_BUTTON_RELEASE;
3178         event.button.x = wx;
3179         event.button.y = wy;
3180         
3181         nframes_t where = event_frame (&event, 0, 0);
3182         snap_to (where);
3183         paste_internal (where, 1);
3184 }
3185
3186 void
3187 Editor::paste_internal (nframes_t position, float times)
3188 {
3189         bool commit = false;
3190
3191         if (cut_buffer->empty() || selection->tracks.empty()) {
3192                 return;
3193         }
3194
3195         if (position == max_frames) {
3196                 position = edit_cursor->current_frame;
3197         }
3198
3199         begin_reversible_command (_("paste"));
3200
3201         TrackSelection::iterator i;
3202         size_t nth;
3203
3204         /* get everything in the correct order */
3205
3206         sort_track_selection ();
3207
3208         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3209
3210                 /* undo/redo is handled by individual tracks */
3211
3212                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3213                         commit = true;
3214                 }
3215         }
3216         
3217         if (commit) {
3218                 commit_reversible_command ();
3219         }
3220 }
3221
3222 void
3223 Editor::paste_named_selection (float times)
3224 {
3225         TrackSelection::iterator t;
3226
3227         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3228
3229         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3230                 return;
3231         }
3232
3233         TreeModel::iterator i = selected->get_selected();
3234         NamedSelection* ns = (*i)[named_selection_columns.selection];
3235
3236         list<boost::shared_ptr<Playlist> >::iterator chunk;
3237         list<boost::shared_ptr<Playlist> >::iterator tmp;
3238
3239         chunk = ns->playlists.begin();
3240                 
3241         begin_reversible_command (_("paste chunk"));
3242         
3243         sort_track_selection ();
3244
3245         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3246                 
3247                 AudioTimeAxisView* atv;
3248                 boost::shared_ptr<Playlist> pl;
3249                 boost::shared_ptr<AudioPlaylist> apl;
3250
3251                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3252                         continue;
3253                 }
3254
3255                 if ((pl = atv->playlist()) == 0) {
3256                         continue;
3257                 }
3258                 
3259                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3260                         continue;
3261                 }
3262
3263                 tmp = chunk;
3264                 ++tmp;
3265
3266                 XMLNode &before = apl->get_state();
3267                 apl->paste (*chunk, edit_cursor->current_frame, times);
3268                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3269
3270                 if (tmp != ns->playlists.end()) {
3271                         chunk = tmp;
3272                 }
3273         }
3274
3275         commit_reversible_command();
3276 }
3277
3278 void
3279 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3280 {
3281         boost::shared_ptr<Playlist> playlist; 
3282         RegionSelection sel = regions; // clear (below) will clear the argument list
3283                 
3284         begin_reversible_command (_("duplicate region"));
3285
3286         selection->clear_regions ();
3287
3288         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3289
3290                 boost::shared_ptr<Region> r ((*i)->region());
3291
3292                 TimeAxisView& tv = (*i)->get_time_axis_view();
3293                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3294                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3295                 
3296                 playlist = (*i)->region()->playlist();
3297                 XMLNode &before = playlist->get_state();
3298                 playlist->duplicate (r, r->last_frame(), times);
3299                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3300
3301                 c.disconnect ();
3302
3303                 if (latest_regionview) {
3304                         selection->add (latest_regionview);
3305                 }
3306         }
3307                 
3308
3309         commit_reversible_command ();
3310 }
3311
3312 void
3313 Editor::duplicate_selection (float times)
3314 {
3315         if (selection->time.empty() || selection->tracks.empty()) {
3316                 return;
3317         }
3318
3319         boost::shared_ptr<Playlist> playlist; 
3320         vector<boost::shared_ptr<AudioRegion> > new_regions;
3321         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3322                 
3323         create_region_from_selection (new_regions);
3324
3325         if (new_regions.empty()) {
3326                 return;
3327         }
3328         
3329         begin_reversible_command (_("duplicate selection"));
3330
3331         ri = new_regions.begin();
3332
3333         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3334                 if ((playlist = (*i)->playlist()) == 0) {
3335                         continue;
3336                 }
3337                 XMLNode &before = playlist->get_state();
3338                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3339                 XMLNode &after = playlist->get_state();
3340                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3341
3342                 ++ri;
3343                 if (ri == new_regions.end()) {
3344                         --ri;
3345                 }
3346         }
3347
3348         commit_reversible_command ();
3349 }
3350
3351 void
3352 Editor::reset_point_selection ()
3353 {
3354         /* reset all selected points to the relevant default value */
3355
3356         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3357                 
3358                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3359                 
3360                 if (atv) {
3361                         atv->reset_objects (selection->points);
3362                 } 
3363         }
3364 }
3365
3366 void
3367 Editor::center_playhead ()
3368 {
3369         float page = canvas_width * frames_per_unit;
3370
3371         center_screen_internal (playhead_cursor->current_frame, page);
3372 }
3373
3374 void
3375 Editor::center_edit_cursor ()
3376 {
3377         float page = canvas_width * frames_per_unit;
3378
3379         center_screen_internal (edit_cursor->current_frame, page);
3380 }
3381
3382 void
3383 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3384 {
3385         begin_reversible_command (_("clear playlist"));
3386         XMLNode &before = playlist->get_state();
3387         playlist->clear ();
3388         XMLNode &after = playlist->get_state();
3389         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3390         commit_reversible_command ();
3391 }
3392
3393 void
3394 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3395 {
3396         boost::shared_ptr<Playlist> playlist; 
3397         nframes_t distance;
3398         nframes_t next_distance;
3399         nframes_t start;
3400
3401         if (use_edit_cursor) {
3402                 start = edit_cursor->current_frame;
3403         } else {
3404                 start = 0;
3405         }
3406
3407         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3408                 return;
3409         }
3410         
3411         if (selection->tracks.empty()) {
3412                 return;
3413         }
3414         
3415         begin_reversible_command (_("nudge track"));
3416         
3417         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3418
3419                 if ((playlist = (*i)->playlist()) == 0) {
3420                         continue;
3421                 }               
3422                 
3423                 XMLNode &before = playlist->get_state();
3424                 playlist->nudge_after (start, distance, forwards);
3425                 XMLNode &after = playlist->get_state();
3426                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3427         }
3428         
3429         commit_reversible_command ();                   
3430 }
3431
3432 void
3433 Editor::remove_last_capture ()
3434 {
3435         vector<string> choices;
3436         string prompt;
3437         
3438         if (!session) {
3439                 return;
3440         }
3441
3442         if (Config->get_verify_remove_last_capture()) {
3443                 prompt  = _("Do you really want to destroy the last capture?"
3444                             "\n(This is destructive and cannot be undone)");
3445
3446                 choices.push_back (_("No, do nothing."));
3447                 choices.push_back (_("Yes, destroy it."));
3448                 
3449                 Gtkmm2ext::Choice prompter (prompt, choices);
3450                 
3451                 if (prompter.run () == 1) {
3452                         session->remove_last_capture ();
3453                 }
3454
3455         } else {
3456                 session->remove_last_capture();
3457         }
3458 }
3459
3460 void
3461 Editor::normalize_region ()
3462 {
3463         if (!session) {
3464                 return;
3465         }
3466
3467         if (selection->regions.empty()) {
3468                 return;
3469         }
3470
3471         begin_reversible_command (_("normalize"));
3472
3473         track_canvas.get_window()->set_cursor (*wait_cursor);
3474         gdk_flush ();
3475
3476         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3477                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3478                 if (!arv)
3479                         continue;
3480                 XMLNode &before = arv->region()->get_state();
3481                 arv->audio_region()->normalize_to (0.0f);
3482                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3483         }
3484
3485         commit_reversible_command ();
3486         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3487 }
3488
3489
3490 void
3491 Editor::denormalize_region ()
3492 {
3493         if (!session) {
3494                 return;
3495         }
3496
3497         if (selection->regions.empty()) {
3498                 return;
3499         }
3500
3501         begin_reversible_command ("denormalize");
3502
3503         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3504                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3505                 if (!arv)
3506                         continue;
3507                 XMLNode &before = arv->region()->get_state();
3508                 arv->audio_region()->set_scale_amplitude (1.0f);
3509                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3510         }
3511
3512         commit_reversible_command ();
3513 }
3514
3515
3516 void
3517 Editor::reverse_region ()
3518 {
3519         if (!session) {
3520                 return;
3521         }
3522
3523         Reverse rev (*session);
3524         apply_filter (rev, _("reverse regions"));
3525 }
3526
3527 void
3528 Editor::apply_filter (AudioFilter& filter, string command)
3529 {
3530         if (selection->regions.empty()) {
3531                 return;
3532         }
3533
3534         begin_reversible_command (command);
3535
3536         track_canvas.get_window()->set_cursor (*wait_cursor);
3537         gdk_flush ();
3538
3539         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3540                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3541                 if (!arv)
3542                         continue;
3543
3544                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3545
3546                 RegionSelection::iterator tmp;
3547                 
3548                 tmp = r;
3549                 ++tmp;
3550
3551                 if (arv->audio_region()->apply (filter) == 0) {
3552
3553                         XMLNode &before = playlist->get_state();
3554                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3555                         XMLNode &after = playlist->get_state();
3556                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3557                 } else {
3558                         goto out;
3559                 }
3560
3561                 r = tmp;
3562         }
3563
3564         commit_reversible_command ();
3565         selection->regions.clear ();
3566
3567   out:
3568         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3569 }
3570
3571 void
3572 Editor::region_selection_op (void (Region::*pmf)(void))
3573 {
3574         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3575                 Region* region = (*i)->region().get();
3576                 (region->*pmf)();
3577         }
3578 }
3579
3580
3581 void
3582 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3583 {
3584         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3585                 Region* region = (*i)->region().get();
3586                 (region->*pmf)(arg);
3587         }
3588 }
3589
3590 void
3591 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3592 {
3593         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3594                 Region* region = (*i)->region().get();
3595                 (region->*pmf)(yn);
3596         }
3597 }
3598
3599 void
3600 Editor::external_edit_region ()
3601 {
3602         if (!clicked_regionview) {
3603                 return;
3604         }
3605
3606         /* more to come */
3607 }
3608
3609 void
3610 Editor::brush (nframes_t pos)
3611 {
3612         RegionSelection sel;
3613         snap_to (pos);
3614
3615         if (selection->regions.empty()) {
3616                 /* XXX get selection from region list */
3617         } else { 
3618                 sel = selection->regions;
3619         }
3620
3621         if (sel.empty()) {
3622                 return;
3623         }
3624
3625         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3626                 mouse_brush_insert_region ((*i), pos);
3627         }
3628 }
3629
3630 void
3631 Editor::reset_region_gain_envelopes ()
3632 {
3633         if (!session || selection->regions.empty()) {
3634                 return;
3635         }
3636
3637         session->begin_reversible_command (_("reset region gain"));
3638
3639         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3640                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3641                 if (arv) {
3642                         AutomationList& alist (arv->audio_region()->envelope());
3643                         XMLNode& before (alist.get_state());
3644
3645                         arv->audio_region()->set_default_envelope ();
3646                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3647                 }
3648         }
3649
3650         session->commit_reversible_command ();
3651 }
3652
3653 void
3654 Editor::toggle_gain_envelope_visibility ()
3655 {
3656         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3657                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3658                 if (arv) {
3659                         bool x = region_envelope_visible_item->get_active();
3660                         if (x != arv->envelope_visible()) {
3661                                 arv->set_envelope_visible (x);
3662                         }
3663                 }
3664         }
3665 }
3666
3667 void
3668 Editor::toggle_gain_envelope_active ()
3669 {
3670         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3671                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3672                 if (arv) {
3673                         bool x = region_envelope_active_item->get_active();
3674                         if (x != arv->audio_region()->envelope_active()) {
3675                                 arv->audio_region()->set_envelope_active (x);
3676                         }
3677                 }
3678         }
3679 }
3680
3681 void
3682 Editor::toggle_region_lock ()
3683 {
3684         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3685                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3686                 if (arv) {
3687                         bool x = region_lock_item->get_active();
3688                         if (x != arv->audio_region()->locked()) {
3689                                 arv->audio_region()->set_locked (x);
3690                         }
3691                 }
3692         }
3693 }
3694
3695 void
3696 Editor::toggle_region_mute ()
3697 {
3698         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3699                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3700                 if (arv) {
3701                         bool x = region_mute_item->get_active();
3702                         if (x != arv->audio_region()->muted()) {
3703                                 arv->audio_region()->set_muted (x);
3704                         }
3705                 }
3706         }
3707 }
3708
3709 void
3710 Editor::toggle_region_opaque ()
3711 {
3712         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3713                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3714                 if (arv) {
3715                         bool x = region_opaque_item->get_active();
3716                         if (x != arv->audio_region()->opaque()) {
3717                                 arv->audio_region()->set_opaque (x);
3718                         }
3719                 }
3720         }
3721 }
3722
3723 void
3724 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
3725 {
3726         begin_reversible_command (_("set fade in shape"));
3727
3728         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3729                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3730
3731                 if (!tmp) {
3732                         return;
3733                 }
3734
3735                 AutomationList& alist = tmp->audio_region()->fade_in();
3736                 XMLNode &before = alist.get_state();
3737
3738                 tmp->audio_region()->set_fade_in_shape (shape);
3739                 
3740                 XMLNode &after = alist.get_state();
3741                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3742         }
3743
3744         commit_reversible_command ();
3745 }
3746
3747 void
3748 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
3749 {
3750         begin_reversible_command (_("set fade out shape"));
3751
3752         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3753                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3754
3755                 if (!tmp) {
3756                         return;
3757                 }
3758
3759                 AutomationList& alist = tmp->audio_region()->fade_out();
3760                 XMLNode &before = alist.get_state();
3761
3762                 tmp->audio_region()->set_fade_out_shape (shape);
3763                 
3764                 XMLNode &after = alist.get_state();
3765                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3766         }
3767
3768         commit_reversible_command ();
3769 }
3770
3771 void
3772 Editor::set_fade_in_active (bool yn)
3773 {
3774         begin_reversible_command (_("set fade in active"));
3775
3776         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3777                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3778
3779                 if (!tmp) {
3780                         return;
3781                 }
3782
3783
3784                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3785
3786                 XMLNode &before = ar->get_state();
3787
3788                 ar->set_fade_in_active (yn);
3789                 
3790                 XMLNode &after = ar->get_state();
3791                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3792         }
3793 }
3794
3795 void
3796 Editor::set_fade_out_active (bool yn)
3797 {
3798         begin_reversible_command (_("set fade out active"));
3799
3800         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3801                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3802
3803                 if (!tmp) {
3804                         return;
3805                 }
3806
3807                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3808
3809                 XMLNode &before = ar->get_state();
3810
3811                 ar->set_fade_out_active (yn);
3812                 
3813                 XMLNode &after = ar->get_state();
3814                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3815         }
3816 }
3817