Merge with 2.0-ongoing R2883.
[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 */
19
20 /* Note: public Editor methods are documented in public_editor.h */
21
22 #include <unistd.h>
23
24 #include <cstdlib>
25 #include <cmath>
26 #include <string>
27 #include <map>
28
29 #include <pbd/error.h>
30 #include <pbd/basename.h>
31 #include <pbd/pthread_utils.h>
32 #include <pbd/memento_command.h>
33 #include <pbd/whitespace.h>
34
35 #include <gtkmm2ext/utils.h>
36 #include <gtkmm2ext/choice.h>
37 #include <gtkmm2ext/window_title.h>
38
39 #include <ardour/audioengine.h>
40 #include <ardour/session.h>
41 #include <ardour/audioplaylist.h>
42 #include <ardour/audioregion.h>
43 #include <ardour/audio_diskstream.h>
44 #include <ardour/utils.h>
45 #include <ardour/location.h>
46 #include <ardour/named_selection.h>
47 #include <ardour/audio_track.h>
48 #include <ardour/audioplaylist.h>
49 #include <ardour/region_factory.h>
50 #include <ardour/playlist_factory.h>
51 #include <ardour/reverse.h>
52 #include <ardour/quantize.h>
53
54 #include "ardour_ui.h"
55 #include "editor.h"
56 #include "time_axis_view.h"
57 #include "audio_time_axis.h"
58 #include "automation_time_axis.h"
59 #include "streamview.h"
60 #include "audio_region_view.h"
61 #include "midi_region_view.h"
62 #include "rgb_macros.h"
63 #include "selection_templates.h"
64 #include "selection.h"
65 #include "editing.h"
66 #include "gtk-custom-hruler.h"
67 #include "gui_thread.h"
68
69 #include "i18n.h"
70
71 using namespace std;
72 using namespace ARDOUR;
73 using namespace PBD;
74 using namespace sigc;
75 using namespace Gtk;
76 using namespace Gtkmm2ext;
77 using namespace Editing;
78
79 /***********************************************************************
80   Editor operations
81  ***********************************************************************/
82
83 void
84 Editor::undo (uint32_t n)
85 {
86         if (session) {
87                 session->undo (n);
88         }
89 }
90
91 void
92 Editor::redo (uint32_t n)
93 {
94         if (session) {
95                 session->redo (n);
96         }
97 }
98
99 void
100 Editor::split_region ()
101 {
102         split_region_at (get_preferred_edit_position());
103 }
104
105 void
106 Editor::split_region_at (nframes_t where)
107 {
108         split_regions_at (where, selection->regions);
109 }
110
111 void
112 Editor::split_regions_at (nframes_t where, RegionSelection& regions)
113 {
114         list <boost::shared_ptr<Playlist > > used_playlists;
115
116         if (regions.empty()) {
117                 return;
118         }
119
120         begin_reversible_command (_("split"));
121
122         // if splitting a single region, and snap-to is using
123         // region boundaries, don't pay attention to them
124
125         if (regions.size() == 1) {
126                 switch (snap_type) {
127                 case SnapToRegionStart:
128                 case SnapToRegionSync:
129                 case SnapToRegionEnd:
130                         break;
131                 default:
132                         snap_to (where);
133                 }
134         } else {
135                 snap_to (where);
136         }
137
138         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
139
140                 RegionSelection::iterator tmp;
141
142                 /* XXX this test needs to be more complicated, to make sure we really
143                    have something to split.
144                 */
145                 
146                 if (!(*a)->region()->covers (where)) {
147                         ++a;
148                         continue;
149                 }
150
151                 tmp = a;
152                 ++tmp;
153
154                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
155
156                 if (! pl->frozen()) {
157                         /* we haven't seen this playlist before */
158
159                         /* remember used playlists so we can thaw them later */ 
160                         used_playlists.push_back(pl);
161                         pl->freeze();
162                 }
163
164                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
165
166                 if (arv) {
167                         _new_regionviews_show_envelope = arv->envelope_visible();
168                 }
169                 
170                 if (pl) {
171                         XMLNode &before = pl->get_state();
172                         pl->split_region ((*a)->region(), where);
173                         XMLNode &after = pl->get_state();
174                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
175                 }
176
177                 a = tmp;
178         }
179         while (used_playlists.size() > 0) {
180
181                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
182                 (*i)->thaw();
183                 used_playlists.pop_front();
184         }
185         commit_reversible_command ();
186         _new_regionviews_show_envelope = false;
187 }
188
189
190 /** Remove `clicked_regionview' */
191 void
192 Editor::remove_clicked_region ()
193 {
194         if (clicked_routeview == 0 || clicked_regionview == 0) {
195                 return;
196         }
197
198         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
199         
200         begin_reversible_command (_("remove region"));
201         XMLNode &before = playlist->get_state();
202         playlist->remove_region (clicked_regionview->region());
203         XMLNode &after = playlist->get_state();
204         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
205         commit_reversible_command ();
206 }
207
208
209 /** Remove the selected regions */
210 void
211 Editor::remove_selected_regions ()
212 {
213         if (selection->regions.empty()) {
214                 return;
215         }
216
217         /* XXX: should be called remove regions if we're removing more than one */
218         begin_reversible_command (_("remove region"));
219         
220
221         while (!selection->regions.empty()) {
222                 boost::shared_ptr<Region> region = selection->regions.front()->region ();
223                 boost::shared_ptr<Playlist> playlist = region->playlist ();
224
225                 XMLNode &before = playlist->get_state();
226                 playlist->remove_region (region);
227                 XMLNode &after = playlist->get_state();
228                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
229         }
230
231         commit_reversible_command ();
232 }
233
234 boost::shared_ptr<Region>
235 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
236 {
237         RegionView* rv;
238         boost::shared_ptr<Region> region;
239         nframes_t start = 0;
240
241         if (selection->time.start () == selection->time.end_frame ()) {
242                 
243                 /* no current selection-> is there a selected regionview? */
244
245                 if (selection->regions.empty()) {
246                         return region;
247                 }
248
249         } 
250
251         if (!selection->regions.empty()) {
252
253                 rv = *(selection->regions.begin());
254                 (*tv) = &rv->get_time_axis_view();
255                 region = rv->region();
256
257         } else if (!selection->tracks.empty()) {
258
259                 (*tv) = selection->tracks.front();
260
261                 RouteTimeAxisView* rtv;
262
263                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
264                         boost::shared_ptr<Playlist> pl;
265                         
266                         if ((pl = rtv->playlist()) == 0) {
267                                 return region;
268                         }
269                         
270                         region = pl->top_region_at (start);
271                 }
272         } 
273         
274         return region;
275 }
276         
277 void
278 Editor::extend_selection_to_end_of_region (bool next)
279 {
280         TimeAxisView *tv;
281         boost::shared_ptr<Region> region;
282         nframes_t start;
283
284         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
285                 return;
286         }
287
288         if (region && selection->time.start () == selection->time.end_frame ()) {
289                 start = region->position();
290         } else {
291                 start = selection->time.start ();
292         }
293
294         /* Try to leave the selection with the same route if possible */
295
296         if ((tv = selection->time.track) == 0) {
297                 return;
298         }
299
300         begin_reversible_command (_("extend selection"));
301         selection->set (tv, start, region->position() + region->length());
302         commit_reversible_command ();
303 }
304
305 void
306 Editor::extend_selection_to_start_of_region (bool previous)
307 {
308         TimeAxisView *tv;
309         boost::shared_ptr<Region> region;
310         nframes_t end;
311
312         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
313                 return;
314         }
315
316         if (region && selection->time.start () == selection->time.end_frame ()) {
317                 end = region->position() + region->length();
318         } else {
319                 end = selection->time.end_frame ();
320         }
321
322         /* Try to leave the selection with the same route if possible */
323         
324         if ((tv = selection->time.track) == 0) {
325                 return;
326         }
327
328         begin_reversible_command (_("extend selection"));
329         selection->set (tv, region->position(), end);
330         commit_reversible_command ();
331 }
332
333
334 void
335 Editor::nudge_forward (bool next)
336 {
337         nframes_t distance;
338         nframes_t next_distance;
339
340         if (!session) return;
341         
342         if (!selection->regions.empty()) {
343
344                 begin_reversible_command (_("nudge regions forward"));
345
346                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
347                         boost::shared_ptr<Region> r ((*i)->region());
348                         
349                         distance = get_nudge_distance (r->position(), next_distance);
350
351                         if (next) {
352                                 distance = next_distance;
353                         }
354
355                         XMLNode &before = r->playlist()->get_state();
356                         r->set_position (r->position() + distance, this);
357                         XMLNode &after = r->playlist()->get_state();
358                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
359                 }
360
361                 commit_reversible_command ();
362
363                 
364         } else if (!selection->markers.empty()) {
365
366                 bool is_start;
367                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
368
369                 if (loc) {
370
371                         begin_reversible_command (_("nudge location forward"));
372
373                         XMLNode& before (loc->get_state());
374
375                         if (is_start) {
376                                 distance = get_nudge_distance (loc->start(), next_distance);
377                                 if (next) {
378                                         distance = next_distance;
379                                 }
380                                 if (max_frames - distance > loc->start() + loc->length()) {
381                                         loc->set_start (loc->start() + distance);
382                                 } else {
383                                         loc->set_start (max_frames - loc->length());
384                                 }
385                         } else {
386                                 distance = get_nudge_distance (loc->end(), next_distance);
387                                 if (next) {
388                                         distance = next_distance;
389                                 }
390                                 if (max_frames - distance > loc->end()) {
391                                         loc->set_end (loc->end() + distance);
392                                 } else {
393                                         loc->set_end (max_frames);
394                                 }
395                         }
396                         XMLNode& after (loc->get_state());
397                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
398                         commit_reversible_command ();
399                 }
400                 
401         } else {
402                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
403                 session->request_locate (playhead_cursor->current_frame + distance);
404         }
405 }
406                 
407 void
408 Editor::nudge_backward (bool next)
409 {
410         nframes_t distance;
411         nframes_t next_distance;
412
413         if (!session) return;
414         
415         if (!selection->regions.empty()) {
416
417                 begin_reversible_command (_("nudge regions backward"));
418
419                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
420                         boost::shared_ptr<Region> r ((*i)->region());
421
422                         distance = get_nudge_distance (r->position(), next_distance);
423                         
424                         if (next) {
425                                 distance = next_distance;
426                         }
427
428                         XMLNode &before = r->playlist()->get_state();
429                         
430                         if (r->position() > distance) {
431                                 r->set_position (r->position() - distance, this);
432                         } else {
433                                 r->set_position (0, this);
434                         }
435                         XMLNode &after = r->playlist()->get_state();
436                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
437                 }
438
439                 commit_reversible_command ();
440
441         } else if (!selection->markers.empty()) {
442
443                 bool is_start;
444                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
445
446                 if (loc) {
447
448                         begin_reversible_command (_("nudge location forward"));
449                         XMLNode& before (loc->get_state());
450
451                         if (is_start) {
452                                 distance = get_nudge_distance (loc->start(), next_distance);
453                                 if (next) {
454                                         distance = next_distance;
455                                 }
456                                 if (distance < loc->start()) {
457                                         loc->set_start (loc->start() - distance);
458                                 } else {
459                                         loc->set_start (0);
460                                 }
461                         } else {
462                                 distance = get_nudge_distance (loc->end(), next_distance);
463
464                                 if (next) {
465                                         distance = next_distance;
466                                 }
467
468                                 if (distance < loc->end() - loc->length()) {
469                                         loc->set_end (loc->end() - distance);
470                                 } else {
471                                         loc->set_end (loc->length());
472                                 }
473                         }
474
475                         XMLNode& after (loc->get_state());
476                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
477                 }
478                 
479         } else {
480
481                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
482
483                 if (playhead_cursor->current_frame > distance) {
484                         session->request_locate (playhead_cursor->current_frame - distance);
485                 } else {
486                         session->goto_start();
487                 }
488         }
489 }
490
491 void
492 Editor::nudge_forward_capture_offset ()
493 {
494         nframes_t distance;
495
496         if (!session) return;
497         
498         if (!selection->regions.empty()) {
499
500                 begin_reversible_command (_("nudge forward"));
501
502                 distance = session->worst_output_latency();
503
504                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
505                         boost::shared_ptr<Region> r ((*i)->region());
506                         
507                         XMLNode &before = r->playlist()->get_state();
508                         r->set_position (r->position() + distance, this);
509                         XMLNode &after = r->playlist()->get_state();
510                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
511                 }
512
513                 commit_reversible_command ();
514
515         } 
516 }
517                 
518 void
519 Editor::nudge_backward_capture_offset ()
520 {
521         nframes_t distance;
522
523         if (!session) return;
524         
525         if (!selection->regions.empty()) {
526
527                 begin_reversible_command (_("nudge forward"));
528
529                 distance = session->worst_output_latency();
530
531                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
532                         boost::shared_ptr<Region> r ((*i)->region());
533
534                         XMLNode &before = r->playlist()->get_state();
535                         
536                         if (r->position() > distance) {
537                                 r->set_position (r->position() - distance, this);
538                         } else {
539                                 r->set_position (0, this);
540                         }
541                         XMLNode &after = r->playlist()->get_state();
542                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
543                 }
544
545                 commit_reversible_command ();
546         }
547 }
548
549 /* DISPLAY MOTION */
550
551 void
552 Editor::move_to_start ()
553 {
554         session->goto_start ();
555 }
556
557 void
558 Editor::move_to_end ()
559 {
560
561         session->request_locate (session->current_end_frame());
562 }
563
564 void
565 Editor::build_region_boundary_cache ()
566 {
567         nframes_t pos = 0;
568         vector<RegionPoint> interesting_points;
569         boost::shared_ptr<Region> r;
570         TrackViewList tracks;
571         bool at_end = false;
572
573         region_boundary_cache.clear ();
574
575         if (session == 0) {
576                 return;
577         }
578         
579         switch (snap_type) {
580         case SnapToRegionStart:
581                 interesting_points.push_back (Start);
582                 break;
583         case SnapToRegionEnd:
584                 interesting_points.push_back (End);
585                 break;  
586         case SnapToRegionSync:
587                 interesting_points.push_back (SyncPoint);
588                 break;  
589         case SnapToRegionBoundary:
590                 interesting_points.push_back (Start);
591                 interesting_points.push_back (End);
592                 break;  
593         default:
594                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
595                 /*NOTREACHED*/
596                 return;
597         }
598         
599         TimeAxisView *ontrack = 0;
600         TrackViewList tlist;
601
602         if (!selection->tracks.empty()) {
603                 tlist = selection->tracks;
604         } else {
605                 tlist = track_views;
606         }
607
608         while (pos < session->current_end_frame() && !at_end) {
609
610                 nframes_t rpos;
611                 nframes_t lpos = max_frames;
612
613                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
614
615                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
616                                 if (*p == interesting_points.back()) {
617                                         at_end = true;
618                                 }
619                                 /* move to next point type */
620                                 continue;
621                         }
622
623                         switch (*p) {
624                         case Start:
625                                 rpos = r->first_frame();
626                                 break;
627
628                         case End:
629                                 rpos = r->last_frame();
630                                 break;  
631
632                         case SyncPoint:
633                                 rpos = r->adjust_to_sync (r->first_frame());
634                                 break;
635
636                         default:
637                                 break;
638                         }
639                         
640                         float speed = 1.0f;
641                         RouteTimeAxisView *rtav;
642                         
643                         if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
644                                 if (rtav->get_diskstream() != 0) {
645                                         speed = rtav->get_diskstream()->speed();
646                                 }
647                         }
648                         
649                         rpos = track_frame_to_session_frame (rpos, speed);
650
651                         if (rpos < lpos) {
652                                 lpos = rpos;
653                         }
654
655                         /* prevent duplicates, but we don't use set<> because we want to be able
656                            to sort later.
657                         */
658
659                         vector<nframes_t>::iterator ri; 
660                         
661                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
662                                 if (*ri == rpos) {
663                                         break;
664                                 }
665                         }
666
667                         if (ri == region_boundary_cache.end()) {
668                                 region_boundary_cache.push_back (rpos);
669                         }
670                 }
671
672                 pos = lpos + 1;
673         }
674
675         /* finally sort to be sure that the order is correct */
676
677         sort (region_boundary_cache.begin(), region_boundary_cache.end());
678 }
679
680 boost::shared_ptr<Region>
681 Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
682 {
683         TrackViewList::iterator i;
684         nframes_t closest = max_frames;
685         boost::shared_ptr<Region> ret;
686         nframes_t rpos = 0;
687
688         float track_speed;
689         nframes_t track_frame;
690         RouteTimeAxisView *rtav;
691
692         for (i = tracks.begin(); i != tracks.end(); ++i) {
693
694                 nframes_t distance;
695                 boost::shared_ptr<Region> r;
696                 
697                 track_speed = 1.0f;
698                 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
699                         if (rtav->get_diskstream()!=0)
700                                 track_speed = rtav->get_diskstream()->speed();
701                 }
702
703                 track_frame = session_frame_to_track_frame(frame, track_speed);
704
705                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
706                         continue;
707                 }
708
709                 switch (point) {
710                 case Start:
711                         rpos = r->first_frame ();
712                         break;
713
714                 case End:
715                         rpos = r->last_frame ();
716                         break;
717
718                 case SyncPoint:
719                         rpos = r->adjust_to_sync (r->first_frame());
720                         break;
721                 }
722
723                 // rpos is a "track frame", converting it to "session frame"
724                 rpos = track_frame_to_session_frame(rpos, track_speed);
725
726                 if (rpos > frame) {
727                         distance = rpos - frame;
728                 } else {
729                         distance = frame - rpos;
730                 }
731
732                 if (distance < closest) {
733                         closest = distance;
734                         if (ontrack != 0)
735                                 *ontrack = (*i);
736                         ret = r;
737                 }
738         }
739
740         return ret;
741 }
742
743 nframes64_t
744 Editor::find_next_region_boundary (nframes64_t pos, int32_t dir, const TrackViewList& tracks)
745 {
746         nframes64_t distance = max_frames;
747         nframes64_t current_nearest = -1;
748
749         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
750                 nframes64_t contender;
751                 nframes64_t d;
752
753                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
754
755                 if (!rtv) {
756                         continue;
757                 }
758
759                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
760                         continue;
761                 }
762
763                 d = ::llabs (pos - contender);
764
765                 if (d < distance) {
766                         current_nearest = contender;
767                         distance = d;
768                 }
769         }
770         
771         return current_nearest;
772 }
773
774 void
775 Editor::cursor_to_region_boundary (Cursor* cursor, int32_t dir)
776 {
777         nframes64_t pos = cursor->current_frame;
778         nframes64_t target;
779
780         if (!session) {
781                 return;
782         }
783
784         // so we don't find the current region again..
785         if (dir > 0 || pos > 0) {
786                 pos += dir;
787         }
788
789         if (!selection->tracks.empty()) {
790                 
791                 target = find_next_region_boundary (pos, dir, selection->tracks);
792                 
793         } else {
794                 
795                 target = find_next_region_boundary (pos, dir, track_views);
796         }
797         
798         if (target < 0) {
799                 return;
800         }
801
802
803         if (cursor == playhead_cursor) {
804                 session->request_locate (target);
805         } else {
806                 cursor->set_position (target);
807         }
808 }
809
810 void
811 Editor::cursor_to_next_region_boundary (Cursor* cursor)
812 {
813         cursor_to_region_boundary (cursor, 1);
814 }
815
816 void
817 Editor::cursor_to_previous_region_boundary (Cursor* cursor)
818 {
819         cursor_to_region_boundary (cursor, -1);
820 }
821
822 void
823 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
824 {
825         boost::shared_ptr<Region> r;
826         nframes_t pos = cursor->current_frame;
827
828         if (!session) {
829                 return;
830         }
831
832         TimeAxisView *ontrack = 0;
833
834         // so we don't find the current region again..
835         if (dir>0 || pos>0)
836                 pos+=dir;
837
838         if (!selection->tracks.empty()) {
839                 
840                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
841                 
842         } else if (clicked_axisview) {
843                 
844                 TrackViewList t;
845                 t.push_back (clicked_axisview);
846                 
847                 r = find_next_region (pos, point, dir, t, &ontrack);
848                 
849         } else {
850                 
851                 r = find_next_region (pos, point, dir, track_views, &ontrack);
852         }
853
854         if (r == 0) {
855                 return;
856         }
857         
858         switch (point){
859         case Start:
860                 pos = r->first_frame ();
861                 break;
862
863         case End:
864                 pos = r->last_frame ();
865                 break;
866
867         case SyncPoint:
868                 pos = r->adjust_to_sync (r->first_frame());
869                 break;  
870         }
871         
872         float speed = 1.0f;
873         RouteTimeAxisView *rtav;
874
875         if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
876                 if (rtav->get_diskstream() != 0) {
877                         speed = rtav->get_diskstream()->speed();
878                 }
879         }
880
881         pos = track_frame_to_session_frame(pos, speed);
882         
883         if (cursor == playhead_cursor) {
884                 session->request_locate (pos);
885         } else {
886                 cursor->set_position (pos);
887         }
888 }
889
890 void
891 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
892 {
893         cursor_to_region_point (cursor, point, 1);
894 }
895
896 void
897 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
898 {
899         cursor_to_region_point (cursor, point, -1);
900 }
901
902 void
903 Editor::cursor_to_selection_start (Cursor *cursor)
904 {
905         nframes_t pos = 0;
906         switch (mouse_mode) {
907         case MouseObject:
908                 if (!selection->regions.empty()) {
909                         pos = selection->regions.start();
910                 }
911                 break;
912
913         case MouseRange:
914                 if (!selection->time.empty()) {
915                         pos = selection->time.start ();
916                 }
917                 break;
918
919         default:
920                 return;
921         }
922
923         if (cursor == playhead_cursor) {
924                 session->request_locate (pos);
925         } else {
926                 cursor->set_position (pos);
927         }
928 }
929
930 void
931 Editor::cursor_to_selection_end (Cursor *cursor)
932 {
933         nframes_t pos = 0;
934
935         switch (mouse_mode) {
936         case MouseObject:
937                 if (!selection->regions.empty()) {
938                         pos = selection->regions.end_frame();
939                 }
940                 break;
941
942         case MouseRange:
943                 if (!selection->time.empty()) {
944                         pos = selection->time.end_frame ();
945                 }
946                 break;
947
948         default:
949                 return;
950         }
951
952         if (cursor == playhead_cursor) {
953                 session->request_locate (pos);
954         } else {
955                 cursor->set_position (pos);
956         }
957 }
958
959 void
960 Editor::selected_marker_to_region_boundary (int32_t dir)
961 {
962         nframes64_t target;
963         Location* loc;
964         bool ignored;
965
966         if (!session) {
967                 return;
968         }
969
970         if (selection->markers.empty()) {
971                 nframes64_t mouse;
972                 bool ignored;
973
974                 if (!mouse_frame (mouse, ignored)) {
975                         return;
976                 }
977                 
978                 add_location_mark (mouse);
979         }
980
981         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
982                 return;
983         }
984
985         nframes64_t pos = loc->start();
986
987         // so we don't find the current region again..
988         if (dir > 0 || pos > 0) {
989                 pos += dir;
990         }
991
992         if (!selection->tracks.empty()) {
993                 
994                 target = find_next_region_boundary (pos, dir, selection->tracks);
995                 
996         } else {
997                 
998                 target = find_next_region_boundary (pos, dir, track_views);
999         }
1000         
1001         if (target < 0) {
1002                 return;
1003         }
1004
1005         loc->move_to (target);
1006 }
1007
1008 void
1009 Editor::selected_marker_to_next_region_boundary ()
1010 {
1011         selected_marker_to_region_boundary (1);
1012 }
1013
1014 void
1015 Editor::selected_marker_to_previous_region_boundary ()
1016 {
1017         selected_marker_to_region_boundary (-1);
1018 }
1019
1020 void
1021 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1022 {
1023         boost::shared_ptr<Region> r;
1024         nframes_t pos;
1025         Location* loc;
1026         bool ignored;
1027
1028         if (!session || selection->markers.empty()) {
1029                 return;
1030         }
1031
1032         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1033                 return;
1034         }
1035
1036         TimeAxisView *ontrack = 0;
1037
1038         pos = loc->start();
1039
1040         // so we don't find the current region again..
1041         if (dir>0 || pos>0)
1042                 pos+=dir;
1043
1044         if (!selection->tracks.empty()) {
1045                 
1046                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1047                 
1048         } else {
1049                 
1050                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1051         }
1052
1053         if (r == 0) {
1054                 return;
1055         }
1056         
1057         switch (point){
1058         case Start:
1059                 pos = r->first_frame ();
1060                 break;
1061
1062         case End:
1063                 pos = r->last_frame ();
1064                 break;
1065
1066         case SyncPoint:
1067                 pos = r->adjust_to_sync (r->first_frame());
1068                 break;  
1069         }
1070         
1071         float speed = 1.0f;
1072         AudioTimeAxisView *atav;
1073
1074         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
1075                 if (atav->get_diskstream() != 0) {
1076                         speed = atav->get_diskstream()->speed();
1077                 }
1078         }
1079
1080         pos = track_frame_to_session_frame(pos, speed);
1081
1082         loc->move_to (pos);
1083 }
1084
1085 void
1086 Editor::selected_marker_to_next_region_point (RegionPoint point)
1087 {
1088         selected_marker_to_region_point (point, 1);
1089 }
1090
1091 void
1092 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1093 {
1094         selected_marker_to_region_point (point, -1);
1095 }
1096
1097 void
1098 Editor::selected_marker_to_selection_start ()
1099 {
1100         nframes_t pos = 0;
1101         Location* loc;
1102         bool ignored;
1103
1104         if (!session || selection->markers.empty()) {
1105                 return;
1106         }
1107
1108         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1109                 return;
1110         }
1111
1112         switch (mouse_mode) {
1113         case MouseObject:
1114                 if (!selection->regions.empty()) {
1115                         pos = selection->regions.start();
1116                 }
1117                 break;
1118
1119         case MouseRange:
1120                 if (!selection->time.empty()) {
1121                         pos = selection->time.start ();
1122                 }
1123                 break;
1124
1125         default:
1126                 return;
1127         }
1128
1129         loc->move_to (pos);
1130 }
1131
1132 void
1133 Editor::selected_marker_to_selection_end ()
1134 {
1135         nframes_t pos = 0;
1136         Location* loc;
1137         bool ignored;
1138
1139         if (!session || selection->markers.empty()) {
1140                 return;
1141         }
1142
1143         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1144                 return;
1145         }
1146
1147         switch (mouse_mode) {
1148         case MouseObject:
1149                 if (!selection->regions.empty()) {
1150                         pos = selection->regions.end_frame();
1151                 }
1152                 break;
1153
1154         case MouseRange:
1155                 if (!selection->time.empty()) {
1156                         pos = selection->time.end_frame ();
1157                 }
1158                 break;
1159
1160         default:
1161                 return;
1162         }
1163
1164         loc->move_to (pos);
1165 }
1166
1167 void
1168 Editor::scroll_playhead (bool forward)
1169 {
1170         nframes_t pos = playhead_cursor->current_frame;
1171         nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
1172
1173         if (forward) {
1174                 if (pos == max_frames) {
1175                         return;
1176                 }
1177
1178                 if (pos < max_frames - delta) {
1179                         pos += delta ;
1180                 } else {
1181                         pos = max_frames;
1182                 } 
1183
1184         } else {
1185
1186                 if (pos == 0) {
1187                         return;
1188                 } 
1189
1190                 if (pos > delta) {
1191                         pos -= delta;
1192                 } else {
1193                         pos = 0;
1194                 }
1195         }
1196
1197         session->request_locate (pos);
1198 }
1199
1200 void
1201 Editor::playhead_backward ()
1202 {
1203         nframes_t pos;
1204         nframes_t cnt;
1205         float prefix;
1206         bool was_floating;
1207
1208         if (get_prefix (prefix, was_floating)) {
1209                 cnt = 1;
1210         } else {
1211                 if (was_floating) {
1212                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1213                 } else {
1214                         cnt = (nframes_t) prefix;
1215                 }
1216         }
1217
1218         pos = playhead_cursor->current_frame;
1219
1220         if ((nframes_t) pos < cnt) {
1221                 pos = 0;
1222         } else {
1223                 pos -= cnt;
1224         }
1225         
1226         /* XXX this is completely insane. with the current buffering
1227            design, we'll force a complete track buffer flush and
1228            reload, just to move 1 sample !!!
1229         */
1230
1231         session->request_locate (pos);
1232 }
1233
1234 void
1235 Editor::playhead_forward ()
1236 {
1237         nframes_t pos;
1238         nframes_t cnt;
1239         bool was_floating;
1240         float prefix;
1241
1242         if (get_prefix (prefix, was_floating)) {
1243                 cnt = 1;
1244         } else {
1245                 if (was_floating) {
1246                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1247                 } else {
1248                         cnt = (nframes_t) floor (prefix);
1249                 }
1250         }
1251
1252         pos = playhead_cursor->current_frame;
1253         
1254         /* XXX this is completely insane. with the current buffering
1255            design, we'll force a complete track buffer flush and
1256            reload, just to move 1 sample !!!
1257         */
1258
1259         session->request_locate (pos+cnt);
1260 }
1261
1262 void
1263 Editor::cursor_align (bool playhead_to_edit)
1264 {
1265         if (!session) {
1266                 return;
1267         }
1268
1269         if (playhead_to_edit) {
1270
1271                 if (selection->markers.empty()) {
1272                         return;
1273                 }
1274                 
1275                 session->request_locate (selection->markers.front()->position(), session->transport_rolling());
1276         
1277         } else {
1278                 /* move selected markers to playhead */
1279                 
1280                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1281                         bool ignored;
1282                         
1283                         Location* loc = find_location_from_marker (*i, ignored);
1284                         
1285                         if (loc->is_mark()) {
1286                                 loc->set_start (playhead_cursor->current_frame);
1287                         } else {
1288                                 loc->set (playhead_cursor->current_frame,
1289                                           playhead_cursor->current_frame + loc->length());
1290                         }
1291                 }
1292         }
1293 }
1294
1295 void
1296 Editor::edit_cursor_backward ()
1297 {
1298         nframes64_t pos;
1299         nframes64_t cnt;
1300         float prefix;
1301         bool was_floating;
1302
1303         if (get_prefix (prefix, was_floating)) {
1304                 cnt = 1;
1305         } else {
1306                 if (was_floating) {
1307                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1308                 } else {
1309                         cnt = (nframes_t) prefix;
1310                 }
1311         }
1312
1313         if ((pos = get_preferred_edit_position()) < 0) {
1314                 return;
1315         }
1316
1317         if (pos < cnt) {
1318                 pos = 0;
1319         } else {
1320                 pos -= cnt;
1321         }
1322         
1323         // EDIT CURSOR edit_cursor->set_position (pos);
1324 }
1325
1326 void
1327 Editor::edit_cursor_forward ()
1328 {
1329         //nframes_t pos;
1330         nframes_t cnt;
1331         bool was_floating;
1332         float prefix;
1333
1334         if (get_prefix (prefix, was_floating)) {
1335                 cnt = 1;
1336         } else {
1337                 if (was_floating) {
1338                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1339                 } else {
1340                         cnt = (nframes_t) floor (prefix);
1341                 }
1342         }
1343
1344         // pos = edit_cursor->current_frame;
1345         // EDIT CURSOR edit_cursor->set_position (pos+cnt);
1346 }
1347
1348 void
1349 Editor::goto_frame ()
1350 {
1351         float prefix;
1352         bool was_floating;
1353         nframes_t frame;
1354
1355         if (get_prefix (prefix, was_floating)) {
1356                 return;
1357         }
1358
1359         if (was_floating) {
1360                 frame = (nframes_t) floor (prefix * session->frame_rate());
1361         } else {
1362                 frame = (nframes_t) floor (prefix);
1363         }
1364
1365         session->request_locate (frame);
1366 }
1367
1368 void
1369 Editor::scroll_backward (float pages)
1370 {
1371         nframes_t frame;
1372         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1373         bool was_floating;
1374         float prefix;
1375         nframes_t cnt;
1376         
1377         if (get_prefix (prefix, was_floating)) {
1378                 cnt = (nframes_t) floor (pages * one_page);
1379         } else {
1380                 if (was_floating) {
1381                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1382                 } else {
1383                         cnt = (nframes_t) floor (prefix * one_page);
1384                 }
1385         }
1386
1387         if (leftmost_frame < cnt) {
1388                 frame = 0;
1389         } else {
1390                 frame = leftmost_frame - cnt;
1391         }
1392
1393         reset_x_origin (frame);
1394 }
1395
1396 void
1397 Editor::scroll_forward (float pages)
1398 {
1399         nframes_t frame;
1400         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1401         bool was_floating;
1402         float prefix;
1403         nframes_t cnt;
1404         
1405         if (get_prefix (prefix, was_floating)) {
1406                 cnt = (nframes_t) floor (pages * one_page);
1407         } else {
1408                 if (was_floating) {
1409                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1410                 } else {
1411                         cnt = (nframes_t) floor (prefix * one_page);
1412                 }
1413         }
1414
1415         if (max_frames - cnt < leftmost_frame) {
1416                 frame = max_frames - cnt;
1417         } else {
1418                 frame = leftmost_frame + cnt;
1419         }
1420
1421         reset_x_origin (frame);
1422 }
1423
1424 void
1425 Editor::scroll_tracks_down ()
1426 {
1427         float prefix;
1428         bool was_floating;
1429         int cnt;
1430
1431         if (get_prefix (prefix, was_floating)) {
1432                 cnt = 1;
1433         } else {
1434                 cnt = (int) floor (prefix);
1435         }
1436
1437         double vert_value = vertical_adjustment.get_value() + (cnt *
1438                 vertical_adjustment.get_page_size());
1439         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1440                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1441         }
1442         vertical_adjustment.set_value (vert_value);
1443 }
1444
1445 void
1446 Editor::scroll_tracks_up ()
1447 {
1448         float prefix;
1449         bool was_floating;
1450         int cnt;
1451
1452         if (get_prefix (prefix, was_floating)) {
1453                 cnt = 1;
1454         } else {
1455                 cnt = (int) floor (prefix);
1456         }
1457
1458         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1459 }
1460
1461 void
1462 Editor::scroll_tracks_down_line ()
1463 {
1464
1465         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1466         double vert_value = adj->get_value() + 20;
1467
1468         if (vert_value>adj->get_upper() - canvas_height) {
1469                 vert_value = adj->get_upper() - canvas_height;
1470         }
1471         adj->set_value (vert_value);
1472 }
1473
1474 void
1475 Editor::scroll_tracks_up_line ()
1476 {
1477         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1478         adj->set_value (adj->get_value() - 20);
1479 }
1480
1481 /* ZOOM */
1482
1483 void
1484 Editor::temporal_zoom_step (bool coarser)
1485 {
1486         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1487
1488         double nfpu;
1489
1490         nfpu = frames_per_unit;
1491         
1492         if (coarser) { 
1493                 nfpu *= 1.61803399;
1494         } else { 
1495                 nfpu = max(1.0,(nfpu/1.61803399));
1496         }
1497
1498         temporal_zoom (nfpu);
1499 }       
1500
1501 void
1502 Editor::temporal_zoom (gdouble fpu)
1503 {
1504         if (!session) return;
1505         
1506         nframes64_t current_page = current_page_frames();
1507         nframes64_t current_leftmost = leftmost_frame;
1508         nframes64_t current_rightmost;
1509         nframes64_t current_center;
1510         nframes64_t new_page_size;
1511         nframes64_t half_page_size;
1512         nframes64_t leftmost_after_zoom = 0;
1513         nframes64_t where;
1514         bool in_track_canvas;
1515         double nfpu;
1516         double l;
1517
1518         nfpu = fpu;
1519         
1520         new_page_size = (nframes_t) floor (canvas_width * nfpu);
1521         half_page_size = new_page_size / 2;
1522
1523         switch (zoom_focus) {
1524         case ZoomFocusLeft:
1525                 leftmost_after_zoom = current_leftmost;
1526                 break;
1527                 
1528         case ZoomFocusRight:
1529                 current_rightmost = leftmost_frame + current_page;
1530                 if (current_rightmost < new_page_size) {
1531                         leftmost_after_zoom = 0;
1532                 } else {
1533                         leftmost_after_zoom = current_rightmost - new_page_size;
1534                 }
1535                 break;
1536                 
1537         case ZoomFocusCenter:
1538                 current_center = current_leftmost + (current_page/2); 
1539                 if (current_center < half_page_size) {
1540                         leftmost_after_zoom = 0;
1541                 } else {
1542                         leftmost_after_zoom = current_center - half_page_size;
1543                 }
1544                 break;
1545                 
1546         case ZoomFocusPlayhead:
1547                 /* try to keep the playhead in the same place */
1548
1549                 where = playhead_cursor->current_frame;
1550                 
1551                 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1552                 
1553                 if (l < 0) {
1554                         leftmost_after_zoom = 0;
1555                 } else if (l > max_frames) { 
1556                         leftmost_after_zoom = max_frames - new_page_size;
1557                 } else {
1558                         leftmost_after_zoom = (nframes64_t) l;
1559                 }
1560                 break;
1561
1562         case ZoomFocusMouse:
1563                 /* try to keep the mouse over the same point in the display */
1564
1565                 if (!mouse_frame (where, in_track_canvas)) {
1566                         /* use playhead instead */
1567                         where = playhead_cursor->current_frame;
1568
1569                         if (where < half_page_size) {
1570                                 leftmost_after_zoom = 0;
1571                         } else {
1572                                 leftmost_after_zoom = where - half_page_size;
1573                         }
1574
1575                 } else {
1576
1577                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1578
1579                         if (l < 0) {
1580                                 leftmost_after_zoom = 0;
1581                         } else if (l > max_frames) { 
1582                                 leftmost_after_zoom = max_frames - new_page_size;
1583                         } else {
1584                                 leftmost_after_zoom = (nframes64_t) l;
1585                         }
1586                 }
1587
1588                 break;
1589
1590         case ZoomFocusEdit:
1591                 /* try to keep the edit point in the same place */
1592                 where = get_preferred_edit_position ();
1593
1594                 if (where > 0) {
1595
1596                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1597
1598                         if (l < 0) {
1599                                 leftmost_after_zoom = 0;
1600                         } else if (l > max_frames) { 
1601                                 leftmost_after_zoom = max_frames - new_page_size;
1602                         } else {
1603                                 leftmost_after_zoom = (nframes64_t) l;
1604                         }
1605
1606                 } else {
1607                         /* edit point not defined */
1608                         return;
1609                 }
1610                 break;
1611                 
1612         }
1613  
1614         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1615
1616         reposition_and_zoom (leftmost_after_zoom, nfpu);
1617 }       
1618
1619 void
1620 Editor::temporal_zoom_region ()
1621 {
1622
1623         nframes64_t start = max_frames;
1624         nframes64_t end = 0;
1625
1626         ensure_entered_region_selected (true);
1627
1628         if (selection->regions.empty()) {
1629                 info << _("cannot set loop: no region selected") << endmsg;
1630                 return;
1631         }
1632
1633         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1634                 if ((*i)->region()->position() < start) {
1635                         start = (*i)->region()->position();
1636                 }
1637                 if ((*i)->region()->last_frame() + 1 > end) {
1638                         end = (*i)->region()->last_frame() + 1;
1639                 }
1640         }
1641
1642         /* now comes an "interesting" hack ... make sure we leave a little space
1643            at each end of the editor so that the zoom doesn't fit the region
1644            precisely to the screen.
1645         */
1646
1647         GdkScreen* screen = gdk_screen_get_default ();
1648         gint pixwidth = gdk_screen_get_width (screen);
1649         gint mmwidth = gdk_screen_get_width_mm (screen);
1650         double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1651         double one_centimeter_in_pixels = pix_per_mm * 10.0;
1652         nframes_t extra_samples = unit_to_frame (one_centimeter_in_pixels);
1653         
1654         if (start > extra_samples) {
1655                 start -= extra_samples;
1656         } else {
1657                 start = 0;
1658         } 
1659
1660         if (max_frames - extra_samples > end) {
1661                 end += extra_samples;
1662         } else {
1663                 end = max_frames;
1664         }
1665
1666         temporal_zoom_by_frame (start, end, "zoom to region");
1667         zoomed_to_region = true;
1668 }
1669
1670 void
1671 Editor::toggle_zoom_region ()
1672 {
1673         if (zoomed_to_region) {
1674                 swap_visual_state ();
1675         } else {
1676                 temporal_zoom_region ();
1677         }
1678 }
1679
1680 void
1681 Editor::temporal_zoom_selection ()
1682 {
1683         if (!selection) return;
1684         
1685         if (selection->time.empty()) {
1686                 return;
1687         }
1688
1689         nframes_t start = selection->time[clicked_selection].start;
1690         nframes_t end = selection->time[clicked_selection].end;
1691
1692         temporal_zoom_by_frame (start, end, "zoom to selection");
1693 }
1694
1695 void
1696 Editor::temporal_zoom_session ()
1697 {
1698         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session));
1699
1700         if (session) {
1701                 temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session");
1702         }
1703 }
1704
1705 void
1706 Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & op)
1707 {
1708         if (!session) return;
1709
1710         if ((start == 0 && end == 0) || end < start) {
1711                 return;
1712         }
1713
1714         nframes_t range = end - start;
1715
1716         double new_fpu = (double)range / (double)canvas_width;
1717 //      double p2 = 1.0;
1718
1719 //      while (p2 < new_fpu) {
1720 //              p2 *= 2.0;
1721 //      }
1722 //      new_fpu = p2;
1723         
1724         nframes_t new_page = (nframes_t) floor (canvas_width * new_fpu);
1725         nframes_t middle = (nframes_t) floor( (double)start + ((double)range / 2.0f ));
1726         nframes_t new_leftmost = (nframes_t) floor( (double)middle - ((double)new_page/2.0f));
1727
1728         if (new_leftmost > middle) new_leftmost = 0;
1729
1730 //      begin_reversible_command (op);
1731 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1732 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1733 //      commit_reversible_command ();
1734
1735         reposition_and_zoom (new_leftmost, new_fpu);
1736 }
1737
1738 void 
1739 Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame)
1740 {
1741         if (!session) return;
1742         
1743         double range_before = frame - leftmost_frame;
1744         double new_fpu;
1745         
1746         new_fpu = frames_per_unit;
1747         
1748         if (coarser) { 
1749                 new_fpu *= 1.61803399;
1750                 range_before *= 1.61803399;
1751         } else { 
1752                 new_fpu = max(1.0,(new_fpu/1.61803399));
1753                 range_before /= 1.61803399;
1754         }
1755
1756         if (new_fpu == frames_per_unit) return;
1757
1758         nframes_t new_leftmost = frame - (nframes_t)range_before;
1759
1760         if (new_leftmost > frame) new_leftmost = 0;
1761
1762 //      begin_reversible_command (_("zoom to frame"));
1763 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1764 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1765 //      commit_reversible_command ();
1766
1767         reposition_and_zoom (new_leftmost, new_fpu);
1768 }
1769
1770 void
1771 Editor::add_location_from_selection ()
1772 {
1773         string rangename;
1774
1775         if (selection->time.empty()) {
1776                 return;
1777         }
1778
1779         if (session == 0 || clicked_axisview == 0) {
1780                 return;
1781         }
1782
1783         nframes_t start = selection->time[clicked_selection].start;
1784         nframes_t end = selection->time[clicked_selection].end;
1785
1786         session->locations()->next_available_name(rangename,"selection");
1787         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1788
1789         session->begin_reversible_command (_("add marker"));
1790         XMLNode &before = session->locations()->get_state();
1791         session->locations()->add (location, true);
1792         XMLNode &after = session->locations()->get_state();
1793         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1794         session->commit_reversible_command ();
1795 }
1796
1797 void
1798 Editor::add_location_mark (nframes64_t where)
1799 {
1800         string markername;
1801
1802         select_new_marker = true;
1803
1804         session->locations()->next_available_name(markername,"mark");
1805         Location *location = new Location (where, where, markername, Location::IsMark);
1806         session->begin_reversible_command (_("add marker"));
1807         XMLNode &before = session->locations()->get_state();
1808         session->locations()->add (location, true);
1809         XMLNode &after = session->locations()->get_state();
1810         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1811         session->commit_reversible_command ();
1812 }
1813
1814 void
1815 Editor::add_location_from_playhead_cursor ()
1816 {
1817         add_location_mark (session->audible_frame());
1818 }
1819
1820 void
1821 Editor::add_location_from_audio_region ()
1822 {
1823         if (selection->regions.empty()) {
1824                 return;
1825         }
1826
1827         RegionView* rv = *(selection->regions.begin());
1828         boost::shared_ptr<Region> region = rv->region();
1829         
1830         Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1831         session->begin_reversible_command (_("add marker"));
1832         XMLNode &before = session->locations()->get_state();
1833         session->locations()->add (location, true);
1834         XMLNode &after = session->locations()->get_state();
1835         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1836         session->commit_reversible_command ();
1837 }
1838
1839 void
1840 Editor::amplitude_zoom_step (bool in)
1841 {
1842         gdouble zoom = 1.0;
1843
1844         if (in) {
1845                 zoom *= 2.0;
1846         } else {
1847                 if (zoom > 2.0) {
1848                         zoom /= 2.0;
1849                 } else {
1850                         zoom = 1.0;
1851                 }
1852         }
1853
1854 #ifdef FIX_FOR_CANVAS
1855         /* XXX DO SOMETHING */
1856 #endif
1857 }       
1858
1859
1860 /* DELETION */
1861
1862
1863 void
1864 Editor::delete_sample_forward ()
1865 {
1866 }
1867
1868 void
1869 Editor::delete_sample_backward ()
1870 {
1871 }
1872
1873 void
1874 Editor::delete_screen ()
1875 {
1876 }
1877
1878 /* SEARCH */
1879
1880 void
1881 Editor::search_backwards ()
1882 {
1883         /* what ? */
1884 }
1885
1886 void
1887 Editor::search_forwards ()
1888 {
1889         /* what ? */
1890 }
1891
1892 /* MARKS */
1893
1894 void
1895 Editor::jump_forward_to_mark ()
1896 {
1897         if (!session) {
1898                 return;
1899         }
1900         
1901         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1902
1903         if (location) {
1904                 session->request_locate (location->start(), session->transport_rolling());
1905         } else {
1906                 session->request_locate (session->current_end_frame());
1907         }
1908 }
1909
1910 void
1911 Editor::jump_backward_to_mark ()
1912 {
1913         if (!session) {
1914                 return;
1915         }
1916
1917         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1918         
1919         if (location) {
1920                 session->request_locate (location->start(), session->transport_rolling());
1921         } else {
1922                 session->goto_start ();
1923         }
1924 }
1925
1926 void
1927 Editor::set_mark ()
1928 {
1929         nframes_t pos;
1930         float prefix;
1931         bool was_floating;
1932         string markername;
1933
1934         if (get_prefix (prefix, was_floating)) {
1935                 pos = session->audible_frame ();
1936         } else {
1937                 if (was_floating) {
1938                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1939                 } else {
1940                         pos = (nframes_t) floor (prefix);
1941                 }
1942         }
1943
1944         session->locations()->next_available_name(markername,"mark");
1945         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1946 }
1947
1948 void
1949 Editor::clear_markers ()
1950 {
1951         if (session) {
1952                 session->begin_reversible_command (_("clear markers"));
1953                 XMLNode &before = session->locations()->get_state();
1954                 session->locations()->clear_markers ();
1955                 XMLNode &after = session->locations()->get_state();
1956                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1957                 session->commit_reversible_command ();
1958         }
1959 }
1960
1961 void
1962 Editor::clear_ranges ()
1963 {
1964         if (session) {
1965                 session->begin_reversible_command (_("clear ranges"));
1966                 XMLNode &before = session->locations()->get_state();
1967                 
1968                 Location * looploc = session->locations()->auto_loop_location();
1969                 Location * punchloc = session->locations()->auto_punch_location();
1970                 
1971                 session->locations()->clear_ranges ();
1972                 // re-add these
1973                 if (looploc) session->locations()->add (looploc);
1974                 if (punchloc) session->locations()->add (punchloc);
1975                 
1976                 XMLNode &after = session->locations()->get_state();
1977                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1978                 session->commit_reversible_command ();
1979         }
1980 }
1981
1982 void
1983 Editor::clear_locations ()
1984 {
1985         session->begin_reversible_command (_("clear locations"));
1986         XMLNode &before = session->locations()->get_state();
1987         session->locations()->clear ();
1988         XMLNode &after = session->locations()->get_state();
1989         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1990         session->commit_reversible_command ();
1991         session->locations()->clear ();
1992 }
1993
1994 void
1995 Editor::unhide_markers ()
1996 {
1997         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1998                 Location *l = (*i).first;
1999                 if (l->is_hidden() && l->is_mark()) {
2000                         l->set_hidden(false, this);
2001                 }
2002         }
2003 }
2004
2005 void
2006 Editor::unhide_ranges ()
2007 {
2008         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2009                 Location *l = (*i).first;
2010                 if (l->is_hidden() && l->is_range_marker()) { 
2011                         l->set_hidden(false, this);
2012                 }
2013         }
2014 }
2015
2016 /* INSERT/REPLACE */
2017
2018 void
2019 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
2020 {
2021         double wx, wy;
2022         double cx, cy;
2023         TimeAxisView *tv;
2024         nframes_t where;
2025         RouteTimeAxisView *rtv = 0;
2026         boost::shared_ptr<Playlist> playlist;
2027         
2028         track_canvas.window_to_world (x, y, wx, wy);
2029         wx += horizontal_adjustment.get_value();
2030         wy += vertical_adjustment.get_value();
2031
2032         GdkEvent event;
2033         event.type = GDK_BUTTON_RELEASE;
2034         event.button.x = wx;
2035         event.button.y = wy;
2036         
2037         where = event_frame (&event, &cx, &cy);
2038
2039         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
2040                 /* clearly outside canvas area */
2041                 return;
2042         }
2043         
2044         if ((tv = trackview_by_y_position (cy)) == 0) {
2045                 return;
2046         }
2047         
2048         if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) == 0) {
2049                 return;
2050         }
2051
2052         if ((playlist = rtv->playlist()) == 0) {
2053                 return;
2054         }
2055         
2056         snap_to (where);
2057         
2058         begin_reversible_command (_("insert dragged region"));
2059         XMLNode &before = playlist->get_state();
2060         playlist->add_region (RegionFactory::create (region), where, 1.0);
2061         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2062         commit_reversible_command ();
2063 }
2064
2065 void
2066 Editor::insert_region_list_selection (float times)
2067 {
2068         RouteTimeAxisView *tv = 0;
2069         boost::shared_ptr<Playlist> playlist;
2070
2071         if (clicked_routeview != 0) {
2072                 tv = clicked_routeview;
2073         } else if (!selection->tracks.empty()) {
2074                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2075                         return;
2076                 }
2077         } else if (entered_track != 0) {
2078                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2079                         return;
2080                 }
2081         } else {
2082                 return;
2083         }
2084
2085         if ((playlist = tv->playlist()) == 0) {
2086                 return;
2087         }
2088         
2089         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2090         
2091         if (selected->count_selected_rows() != 1) {
2092                 return;
2093         }
2094         
2095         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
2096
2097         /* only one row selected, so rows.begin() is it */
2098
2099         TreeIter iter;
2100
2101         if ((iter = region_list_model->get_iter (*rows.begin()))) {
2102
2103                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
2104                 
2105                 begin_reversible_command (_("insert region"));
2106                 XMLNode &before = playlist->get_state();
2107                 playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times);
2108                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2109                 commit_reversible_command ();
2110         } 
2111 }
2112
2113 /* BUILT-IN EFFECTS */
2114
2115 void
2116 Editor::reverse_selection ()
2117 {
2118
2119 }
2120
2121 /* GAIN ENVELOPE EDITING */
2122
2123 void
2124 Editor::edit_envelope ()
2125 {
2126 }
2127
2128 /* PLAYBACK */
2129
2130 void
2131 Editor::transition_to_rolling (bool fwd)
2132 {
2133         if (!session) {
2134                 return;
2135         }
2136
2137         switch (Config->get_slave_source()) {
2138         case None:
2139         case JACK:
2140                 break;
2141         default:
2142                 /* transport controlled by the master */
2143                 return;
2144         }
2145
2146         if (session->is_auditioning()) {
2147                 session->cancel_audition ();
2148                 return;
2149         }
2150         
2151         session->request_transport_speed (fwd ? 1.0f : -1.0f);
2152 }
2153
2154 void
2155 Editor::toggle_playback (bool with_abort)
2156 {
2157         if (!session) {
2158                 return;
2159         }
2160
2161         switch (Config->get_slave_source()) {
2162         case None:
2163         case JACK:
2164                 break;
2165         default:
2166                 /* transport controlled by the master */
2167                 return;
2168         }
2169
2170         if (session->is_auditioning()) {
2171                 session->cancel_audition ();
2172                 return;
2173         }
2174         
2175         if (session->transport_rolling()) {
2176                 session->request_stop (with_abort);
2177                 if (session->get_play_loop()) {
2178                         session->request_play_loop (false);
2179                 }
2180         } else {
2181                 session->request_transport_speed (1.0f);
2182         }
2183 }
2184
2185 void
2186 Editor::play_from_start ()
2187 {
2188         session->request_locate (session->current_start_frame(), true);
2189 }
2190
2191 void
2192 Editor::play_from_edit_point ()
2193 {
2194         session->request_locate (get_preferred_edit_position(), true);
2195 }
2196
2197 void
2198 Editor::play_from_edit_point_and_return ()
2199 {
2200         nframes64_t start_frame;
2201         nframes64_t return_frame;
2202
2203         /* don't reset the return frame if its already set */
2204
2205         if ((return_frame = session->requested_return_frame()) < 0) {
2206                 return_frame = session->audible_frame();
2207         }
2208
2209         start_frame = get_preferred_edit_position (true);
2210
2211         if (start_frame >= 0) {
2212                 session->request_roll_at_and_return (start_frame, return_frame);
2213         }
2214 }
2215
2216 void
2217 Editor::play_selection ()
2218 {
2219         if (selection->time.empty()) {
2220                 return;
2221         }
2222
2223         session->request_play_range (true);
2224 }
2225
2226 void
2227 Editor::loop_selected_region ()
2228 {
2229         if (!selection->regions.empty()) {
2230                 RegionView *rv = *(selection->regions.begin());
2231                 Location* tll;
2232
2233                 if ((tll = transport_loop_location()) != 0)  {
2234
2235                         tll->set (rv->region()->position(), rv->region()->last_frame());
2236                         
2237                         // enable looping, reposition and start rolling
2238
2239                         session->request_play_loop (true);
2240                         session->request_locate (tll->start(), false);
2241                         session->request_transport_speed (1.0f);
2242                 }
2243         }
2244 }
2245
2246 void
2247 Editor::play_location (Location& location)
2248 {
2249         if (location.start() <= location.end()) {
2250                 return;
2251         }
2252
2253         session->request_bounded_roll (location.start(), location.end());
2254 }
2255
2256 void
2257 Editor::loop_location (Location& location)
2258 {
2259         if (location.start() <= location.end()) {
2260                 return;
2261         }
2262
2263         Location* tll;
2264
2265         if ((tll = transport_loop_location()) != 0) {
2266                 tll->set (location.start(), location.end());
2267
2268                 // enable looping, reposition and start rolling
2269                 session->request_play_loop (true);
2270                 session->request_locate (tll->start(), true);
2271         }
2272 }
2273
2274 void
2275 Editor::raise_region ()
2276 {
2277         selection->foreach_region (&Region::raise);
2278 }
2279
2280 void
2281 Editor::raise_region_to_top ()
2282 {
2283         selection->foreach_region (&Region::raise_to_top);
2284 }
2285
2286 void
2287 Editor::lower_region ()
2288 {
2289         selection->foreach_region (&Region::lower);
2290 }
2291
2292 void
2293 Editor::lower_region_to_bottom ()
2294 {
2295         selection->foreach_region (&Region::lower_to_bottom);
2296 }
2297
2298 /** Show the region editor for the selected regions */
2299 void
2300 Editor::edit_region ()
2301 {
2302         selection->foreach_regionview (&RegionView::show_region_editor);
2303 }
2304
2305 void
2306 Editor::rename_region()
2307 {
2308         if (selection->regions.empty()) {
2309                 return;
2310         }
2311
2312         WindowTitle title (Glib::get_application_name());
2313         title += _("Rename Region");
2314
2315         ArdourDialog d (*this, title.get_string(), true, false);
2316         Entry entry;
2317         Label label (_("New name:"));
2318         HBox hbox;
2319
2320         hbox.set_spacing (6);
2321         hbox.pack_start (label, false, false);
2322         hbox.pack_start (entry, true, true);
2323
2324         d.get_vbox()->set_border_width (12);
2325         d.get_vbox()->pack_start (hbox, false, false);
2326
2327         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2328         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2329
2330         d.set_size_request (300, -1);
2331         d.set_position (Gtk::WIN_POS_MOUSE);
2332
2333         entry.set_text (selection->regions.front()->region()->name());
2334         entry.select_region (0, -1);
2335
2336         entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK));
2337         
2338         d.show_all ();
2339         
2340         entry.grab_focus();
2341
2342         int ret = d.run();
2343
2344         d.hide ();
2345
2346         if (ret == RESPONSE_OK) {
2347                 std::string str = entry.get_text();
2348                 strip_whitespace_edges (str);
2349                 if (!str.empty()) {
2350                         selection->regions.front()->region()->set_name (str);
2351                         redisplay_regions ();
2352                 }
2353         }
2354 }
2355
2356 void
2357 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2358 {
2359         if (session->is_auditioning()) {
2360                 session->cancel_audition ();
2361         } 
2362
2363         // note: some potential for creativity here, because region doesn't
2364         // have to belong to the playlist that Route is handling
2365
2366         // bool was_soloed = route.soloed();
2367
2368         route.set_solo (true, this);
2369         
2370         session->request_bounded_roll (region->position(), region->position() + region->length());
2371         
2372         /* XXX how to unset the solo state ? */
2373 }
2374
2375 /** Start an audition of the first selected region */
2376 void
2377 Editor::play_edit_range ()
2378 {
2379         nframes64_t start, end;
2380
2381         if (get_edit_op_range (start, end)) {
2382                 session->request_bounded_roll (start, end);
2383         }
2384 }
2385
2386 void
2387 Editor::play_selected_region ()
2388 {
2389         nframes64_t start = max_frames;
2390         nframes64_t end = 0;
2391
2392         ExclusiveRegionSelection esr (*this, entered_regionview);
2393
2394         if (selection->regions.empty()) {
2395                 return;
2396         }
2397
2398         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2399                 if ((*i)->region()->position() < start) {
2400                         start = (*i)->region()->position();
2401                 }
2402                 if ((*i)->region()->last_frame() + 1 > end) {
2403                         end = (*i)->region()->last_frame() + 1;
2404                 }
2405         }
2406
2407         session->request_stop ();
2408         session->request_bounded_roll (start, end);
2409 }
2410
2411 void
2412 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2413 {
2414         session->audition_region (region);
2415 }
2416
2417 void
2418 Editor::build_interthread_progress_window ()
2419 {
2420         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2421
2422         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2423         
2424         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2425         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2426
2427         // GTK2FIX: this button needs a modifiable label
2428
2429         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2430         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2431
2432         interthread_cancel_button.add (interthread_cancel_label);
2433
2434         interthread_progress_window->set_default_size (200, 100);
2435 }
2436
2437 void
2438 Editor::interthread_cancel_clicked ()
2439 {
2440         if (current_interthread_info) {
2441                 current_interthread_info->cancel = true;
2442         }
2443 }
2444
2445 void
2446 Editor::region_from_selection ()
2447 {
2448         if (clicked_axisview == 0) {
2449                 return;
2450         }
2451
2452         if (selection->time.empty()) {
2453                 return;
2454         }
2455
2456         nframes_t start = selection->time[clicked_selection].start;
2457         nframes_t end = selection->time[clicked_selection].end;
2458
2459         nframes_t selection_cnt = end - start + 1;
2460         
2461         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2462                 boost::shared_ptr<AudioRegion> current;
2463                 boost::shared_ptr<Region> current_r;
2464                 boost::shared_ptr<Playlist> pl;
2465
2466                 nframes_t internal_start;
2467                 string new_name;
2468
2469                 if ((pl = (*i)->playlist()) == 0) {
2470                         continue;
2471                 }
2472
2473                 if ((current_r = pl->top_region_at (start)) == 0) {
2474                         continue;
2475                 }
2476
2477                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2478                 assert(current); // FIXME
2479                 if (current != 0) {
2480                         internal_start = start - current->position();
2481                         session->region_name (new_name, current->name(), true);
2482                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2483                 }
2484         }
2485 }       
2486
2487 void
2488 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2489 {
2490         if (selection->time.empty() || selection->tracks.empty()) {
2491                 return;
2492         }
2493
2494         nframes_t start = selection->time[clicked_selection].start;
2495         nframes_t end = selection->time[clicked_selection].end;
2496         
2497         sort_track_selection ();
2498
2499         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2500
2501                 boost::shared_ptr<AudioRegion> current;
2502                 boost::shared_ptr<Region> current_r;
2503                 boost::shared_ptr<Playlist> playlist;
2504                 nframes_t internal_start;
2505                 string new_name;
2506
2507                 if ((playlist = (*i)->playlist()) == 0) {
2508                         continue;
2509                 }
2510
2511                 if ((current_r = playlist->top_region_at(start)) == 0) {
2512                         continue;
2513                 }
2514
2515                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2516                         continue;
2517                 }
2518         
2519                 internal_start = start - current->position();
2520                 session->region_name (new_name, current->name(), true);
2521                 
2522                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2523         }
2524 }
2525
2526 void
2527 Editor::split_multichannel_region ()
2528 {
2529         if (selection->regions.empty()) {
2530                 return;
2531         }
2532
2533         vector<boost::shared_ptr<AudioRegion> > v;
2534
2535         for (list<RegionView*>::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2536
2537                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2538                 
2539                 if (!arv || arv->audio_region()->n_channels() < 2) {
2540                         continue;
2541                 }
2542
2543                 (arv)->audio_region()->separate_by_channel (*session, v);
2544         }
2545 }
2546
2547 void
2548 Editor::new_region_from_selection ()
2549 {
2550         region_from_selection ();
2551         cancel_selection ();
2552 }
2553
2554 static void
2555 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2556 {
2557         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2558         case OverlapNone:
2559                 break;
2560         default:
2561                 rs->push_back (rv);
2562         }
2563 }
2564
2565 void
2566 Editor::separate_regions_between (const TimeSelection& ts)
2567 {
2568         bool in_command = false;
2569         boost::shared_ptr<Playlist> playlist;
2570         RegionSelection new_selection;
2571         TrackSelection tmptracks;
2572
2573         if (selection->tracks.empty()) {
2574                 
2575                 /* use tracks with selected regions */
2576
2577                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2578                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2579
2580                         if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) {
2581                                 tmptracks.push_back (tv);
2582                         }
2583                 }
2584
2585                 if (tmptracks.empty()) {
2586                         /* no regions selected: use all tracks */
2587                         tmptracks = track_views;
2588                 }
2589
2590         } else {
2591
2592                 tmptracks = selection->tracks;
2593
2594         }
2595
2596         sort_track_selection (&tmptracks);
2597
2598         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2599
2600                 RouteTimeAxisView* rtv;
2601
2602                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2603
2604                         if (rtv->is_track()) {
2605
2606                                 /* no edits to destructive tracks */
2607
2608                                 if (rtv->track()->diskstream()->destructive()) {
2609                                         continue;
2610                                 }
2611                                         
2612                                 if ((playlist = rtv->playlist()) != 0) {
2613
2614                                         XMLNode *before;
2615                                         bool got_some;
2616
2617                                         before = &(playlist->get_state());
2618                                         got_some = false;
2619
2620                                         /* XXX need to consider musical time selections here at some point */
2621
2622                                         double speed = rtv->get_diskstream()->speed();
2623
2624
2625                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2626
2627                                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2628                                                 latest_regionviews.clear ();
2629
2630                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2631
2632                                                 c.disconnect ();
2633
2634                                                 if (!latest_regionviews.empty()) {
2635                                                         
2636                                                         got_some = true;
2637
2638                                                         rtv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2639                                                         
2640                                                         if (!in_command) {
2641                                                                 begin_reversible_command (_("separate"));
2642                                                                 in_command = true;
2643                                                         }
2644                                                         
2645                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2646                                                         
2647                                                 } 
2648                                         }
2649
2650                                         if (!got_some) {
2651                                                 delete before;
2652                                         }
2653                                 }
2654                         }
2655                 }
2656         }
2657
2658         if (in_command) {
2659                 selection->set (new_selection);
2660                 set_mouse_mode (MouseObject);
2661
2662                 commit_reversible_command ();
2663         }
2664 }
2665
2666 void
2667 Editor::separate_region_from_selection ()
2668 {
2669         /* preferentially use *all* ranges in the time selection if we're in range mode
2670            to allow discontiguous operation, since get_edit_op_range() currently
2671            returns a single range.
2672         */
2673         if (mouse_mode == MouseRange && !selection->time.empty()) {
2674
2675                 separate_regions_between (selection->time);
2676
2677         } else {
2678
2679                 nframes64_t start;
2680                 nframes64_t end;
2681                 
2682                 if (get_edit_op_range (start, end)) {
2683                         
2684                         AudioRange ar (start, end, 1);
2685                         TimeSelection ts;
2686                         ts.push_back (ar);
2687
2688                         /* force track selection */
2689
2690                         ensure_entered_region_selected ();
2691                         
2692                         separate_regions_between (ts);
2693                 }
2694         }
2695 }
2696
2697 void
2698 Editor::separate_regions_using_location (Location& loc)
2699 {
2700         if (loc.is_mark()) {
2701                 return;
2702         }
2703
2704         AudioRange ar (loc.start(), loc.end(), 1);
2705         TimeSelection ts;
2706
2707         ts.push_back (ar);
2708
2709         separate_regions_between (ts);
2710 }
2711
2712 void
2713 Editor::crop_region_to_selection ()
2714 {
2715         ensure_entered_region_selected (true);
2716
2717         if (!selection->time.empty()) {
2718
2719                 crop_region_to (selection->time.start(), selection->time.end_frame());
2720
2721         } else {
2722
2723                 nframes64_t start;
2724                 nframes64_t end;
2725
2726                 if (get_edit_op_range (start, end)) {
2727                         crop_region_to (start, end);
2728                 }
2729         }
2730                 
2731 }               
2732
2733 void
2734 Editor::crop_region_to (nframes_t start, nframes_t end)
2735 {
2736         vector<boost::shared_ptr<Playlist> > playlists;
2737         boost::shared_ptr<Playlist> playlist;
2738         TrackSelection* ts;
2739
2740         if (selection->tracks.empty()) {
2741                 ts = &track_views;
2742         } else {
2743                 sort_track_selection ();
2744                 ts = &selection->tracks;
2745         }
2746         
2747         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2748                 
2749                 RouteTimeAxisView* rtv;
2750                 
2751                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2752
2753                         boost::shared_ptr<Track> t = rtv->track();
2754
2755                         if (t != 0 && ! t->diskstream()->destructive()) {
2756                                 
2757                                 if ((playlist = rtv->playlist()) != 0) {
2758                                         playlists.push_back (playlist);
2759                                 }
2760                         }
2761                 }
2762         }
2763
2764         if (playlists.empty()) {
2765                 return;
2766         }
2767                 
2768         nframes_t the_start;
2769         nframes_t the_end;
2770         nframes_t cnt;
2771         
2772         begin_reversible_command (_("trim to selection"));
2773         
2774         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2775                 
2776                 boost::shared_ptr<Region> region;
2777         
2778                 the_start = start;
2779         
2780                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2781                         continue;
2782                 }
2783                 
2784                 /* now adjust lengths to that we do the right thing
2785                    if the selection extends beyond the region
2786                 */
2787                 
2788                 the_start = max (the_start, region->position());
2789                 if (max_frames - the_start < region->length()) {
2790                         the_end = the_start + region->length() - 1;
2791                 } else {
2792                         the_end = max_frames;
2793                 }
2794                 the_end = min (end, the_end);
2795                 cnt = the_end - the_start + 1;
2796                 
2797                 XMLNode &before = (*i)->get_state();
2798                 region->trim_to (the_start, cnt, this);
2799                 XMLNode &after = (*i)->get_state();
2800                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2801         }
2802         
2803         commit_reversible_command ();
2804 }               
2805
2806 void
2807 Editor::region_fill_track ()
2808 {
2809         nframes_t end;
2810
2811         if (!session || selection->regions.empty()) {
2812                 return;
2813         }
2814
2815         end = session->current_end_frame ();
2816
2817         begin_reversible_command (_("region fill"));
2818
2819         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2820
2821                 boost::shared_ptr<Region> region ((*i)->region());
2822                 
2823                 // FIXME
2824                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2825                 assert(ar);
2826
2827                 boost::shared_ptr<Playlist> pl = region->playlist();
2828
2829                 if (end <= region->last_frame()) {
2830                         return;
2831                 }
2832
2833                 double times = (double) (end - region->last_frame()) / (double) region->length();
2834
2835                 if (times == 0) {
2836                         return;
2837                 }
2838
2839                 XMLNode &before = pl->get_state();
2840                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2841                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2842         }
2843
2844         commit_reversible_command ();
2845 }
2846
2847 void
2848 Editor::region_fill_selection ()
2849 {
2850         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2851                 return;
2852         }
2853
2854         if (selection->time.empty()) {
2855                 return;
2856         }
2857
2858
2859         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2860
2861         if (selected->count_selected_rows() != 1) {
2862                 return;
2863         }
2864
2865         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2866         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2867
2868         nframes_t start = selection->time[clicked_selection].start;
2869         nframes_t end = selection->time[clicked_selection].end;
2870
2871         boost::shared_ptr<Playlist> playlist; 
2872
2873         if (selection->tracks.empty()) {
2874                 return;
2875         }
2876
2877         nframes_t selection_length = end - start;
2878         float times = (float)selection_length / region->length();
2879         
2880         begin_reversible_command (_("fill selection"));
2881         
2882         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2883
2884                 if ((playlist = (*i)->playlist()) == 0) {
2885                         continue;
2886                 }               
2887                 
2888                 XMLNode &before = playlist->get_state();
2889                 playlist->add_region (RegionFactory::create (region), start, times);
2890                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2891         }
2892         
2893         commit_reversible_command ();                   
2894 }
2895
2896 void
2897 Editor::set_region_sync_from_edit_point ()
2898 {
2899         nframes64_t where = get_preferred_edit_position ();
2900         ensure_entered_region_selected (true);
2901         set_sync_point (where, selection->regions);
2902 }
2903
2904 void
2905 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
2906 {
2907         bool in_command = false;
2908
2909         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2910                 
2911                 if (!(*r)->region()->covers (where)) {
2912                         continue;
2913                 }
2914
2915                 boost::shared_ptr<Region> region ((*r)->region());
2916
2917                 if (!in_command) {
2918                         begin_reversible_command (_("set sync point"));
2919                         in_command = true;
2920                 }
2921
2922                 XMLNode &before = region->playlist()->get_state();
2923                 region->set_sync_position (get_preferred_edit_position());
2924                 XMLNode &after = region->playlist()->get_state();
2925                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2926         }
2927
2928         if (in_command) {
2929                 commit_reversible_command ();
2930         }
2931 }
2932
2933 /** Remove the sync positions of the selection */
2934 void
2935 Editor::remove_region_sync ()
2936 {
2937         begin_reversible_command (_("remove sync"));
2938
2939         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2940                 boost::shared_ptr<Region> r = (*i)->region();
2941                 XMLNode &before = r->playlist()->get_state();
2942                 r->clear_sync_position ();
2943                 XMLNode &after = r->playlist()->get_state();
2944                 session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
2945         }
2946
2947         commit_reversible_command ();
2948 }
2949
2950 void
2951 Editor::naturalize ()
2952 {
2953         if (selection->regions.empty()) {
2954                 return;
2955         }
2956         begin_reversible_command (_("naturalize"));
2957         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2958                 XMLNode &before = (*i)->region()->get_state();
2959                 (*i)->region()->move_to_natural_position (this);
2960                 XMLNode &after = (*i)->region()->get_state();
2961                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2962         }
2963         commit_reversible_command ();
2964 }
2965
2966 void
2967 Editor::align (RegionPoint what)
2968 {
2969         ensure_entered_region_selected ();
2970
2971         nframes64_t where = get_preferred_edit_position();
2972
2973         if (!selection->regions.empty()) {
2974                 align_selection (what, where, selection->regions);
2975         } else {
2976
2977                 RegionSelection rs;
2978                 rs = get_regions_at (where, selection->tracks);
2979                 align_selection (what, where, rs);
2980         }
2981 }
2982
2983 void
2984 Editor::align_relative (RegionPoint what)
2985 {
2986         nframes64_t where = get_preferred_edit_position();
2987
2988         if (!selection->regions.empty()) {
2989                 align_selection_relative (what, where, selection->regions);
2990         } else {
2991
2992                 RegionSelection rs;
2993                 rs = get_regions_at (where, selection->tracks);
2994                 align_selection_relative (what, where, rs);
2995         }
2996 }
2997
2998 struct RegionSortByTime {
2999     bool operator() (const RegionView* a, const RegionView* b) {
3000             return a->region()->position() < b->region()->position();
3001     }
3002 };
3003
3004 void
3005 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
3006 {
3007         if (rs.empty()) {
3008                 return;
3009         }
3010
3011         nframes_t distance;
3012         nframes_t pos = 0;
3013         int dir;
3014
3015         list<RegionView*> sorted;
3016         rs.by_position (sorted);
3017         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3018
3019         switch (point) {
3020         case Start:
3021                 pos = position;
3022                 if (position > r->position()) {
3023                         distance = position - r->position();
3024                         dir = 1;
3025                 } else {
3026                         distance = r->position() - position;
3027                         dir = -1;
3028                 }
3029                 break;
3030                 
3031         case End:
3032                 if (position > r->last_frame()) {
3033                         distance = position - r->last_frame();
3034                         pos = r->position() + distance;
3035                         dir = 1;
3036                 } else {
3037                         distance = r->last_frame() - position;
3038                         pos = r->position() - distance;
3039                         dir = -1;
3040                 }
3041                 break;
3042
3043         case SyncPoint:
3044                 pos = r->adjust_to_sync (position);
3045                 if (pos > r->position()) {
3046                         distance = pos - r->position();
3047                         dir = 1;
3048                 } else {
3049                         distance = r->position() - pos;
3050                         dir = -1;
3051                 }
3052                 break;  
3053         }
3054
3055         if (pos == r->position()) {
3056                 return;
3057         }
3058
3059         begin_reversible_command (_("align selection (relative)"));
3060
3061         /* move first one specially */
3062
3063         XMLNode &before = r->playlist()->get_state();
3064         r->set_position (pos, this);
3065         XMLNode &after = r->playlist()->get_state();
3066         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
3067
3068         /* move rest by the same amount */
3069         
3070         RegionSelection::const_iterator i = rs.begin();
3071         ++i;
3072
3073         for (; i != rs.end(); ++i) {
3074
3075                 boost::shared_ptr<Region> region ((*i)->region());
3076
3077                 XMLNode &before = region->playlist()->get_state();
3078                 
3079                 if (dir > 0) {
3080                         region->set_position (region->position() + distance, this);
3081                 } else {
3082                         region->set_position (region->position() - distance, this);
3083                 }
3084
3085                 XMLNode &after = region->playlist()->get_state();
3086                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3087
3088         }
3089
3090         commit_reversible_command ();
3091 }
3092
3093 void
3094 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
3095 {
3096         if (rs.empty()) {
3097                 return;
3098         }
3099
3100         begin_reversible_command (_("align selection"));
3101
3102         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3103                 align_region_internal ((*i)->region(), point, position);
3104         }
3105
3106         commit_reversible_command ();
3107 }
3108
3109 void
3110 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3111 {
3112         begin_reversible_command (_("align region"));
3113         align_region_internal (region, point, position);
3114         commit_reversible_command ();
3115 }
3116
3117 void
3118 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3119 {
3120         XMLNode &before = region->playlist()->get_state();
3121
3122         switch (point) {
3123         case SyncPoint:
3124                 region->set_position (region->adjust_to_sync (position), this);
3125                 break;
3126
3127         case End:
3128                 if (position > region->length()) {
3129                         region->set_position (position - region->length(), this);
3130                 }
3131                 break;
3132
3133         case Start:
3134                 region->set_position (position, this);
3135                 break;
3136         }
3137
3138         XMLNode &after = region->playlist()->get_state();
3139         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3140 }       
3141
3142 /** Trim the end of the selected regions to the position of the edit cursor */
3143 void
3144 Editor::trim_region_to_loop ()
3145 {
3146         Location* loc = session->locations()->auto_loop_location();
3147         if (!loc) {
3148                 return;
3149         }
3150         trim_region_to_location (*loc, _("trim to loop"));
3151 }
3152
3153 void
3154 Editor::trim_region_to_punch ()
3155 {
3156         Location* loc = session->locations()->auto_punch_location();
3157         if (!loc) {
3158                 return;
3159         }
3160         trim_region_to_location (*loc, _("trim to punch"));
3161 }
3162 void
3163 Editor::trim_region_to_location (const Location& loc, const char* str)
3164 {
3165         ExclusiveRegionSelection ers (*this, entered_regionview);
3166
3167         RegionSelection& rs (get_regions_for_action ());
3168
3169         begin_reversible_command (str);
3170
3171         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3172                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3173
3174                 if (!arv) {
3175                         continue;
3176                 }
3177
3178                 /* require region to span proposed trim */
3179
3180                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3181                 case OverlapInternal:
3182                         break;
3183                 default:
3184                         continue;
3185                 }
3186                                 
3187                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3188
3189                 if (!atav) {
3190                         return;
3191                 }
3192
3193                 float speed = 1.0;
3194                 nframes_t start;
3195                 nframes_t end;
3196
3197                 if (atav->get_diskstream() != 0) {
3198                         speed = atav->get_diskstream()->speed();
3199                 }
3200                 
3201                 start = session_frame_to_track_frame (loc.start(), speed);
3202                 end = session_frame_to_track_frame (loc.end(), speed);
3203
3204                 XMLNode &before = arv->region()->playlist()->get_state();
3205                 arv->region()->trim_to (start, (end - start), this);
3206                 XMLNode &after = arv->region()->playlist()->get_state();
3207                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3208         }
3209
3210         commit_reversible_command ();
3211 }
3212
3213 void
3214 Editor::trim_region_to_edit_point ()
3215 {
3216         ExclusiveRegionSelection ers (*this, entered_regionview);
3217
3218         RegionSelection& rs (get_regions_for_action ());
3219         nframes64_t where = get_preferred_edit_position();
3220
3221         begin_reversible_command (_("trim region start to edit point"));
3222
3223         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3224                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3225
3226                 if (!arv) {
3227                         continue;
3228                 }
3229
3230                 /* require region to cover trim */
3231
3232                 if (!arv->region()->covers (where)) {
3233                         continue;
3234                 }
3235
3236                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3237
3238                 if (!atav) {
3239                         return;
3240                 }
3241
3242                 float speed = 1.0;
3243
3244                 if (atav->get_diskstream() != 0) {
3245                         speed = atav->get_diskstream()->speed();
3246                 }
3247
3248                 XMLNode &before = arv->region()->playlist()->get_state();
3249                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3250                 XMLNode &after = arv->region()->playlist()->get_state();
3251                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3252         }
3253                 
3254         commit_reversible_command ();
3255 }
3256
3257 void
3258 Editor::trim_region_from_edit_point ()
3259 {
3260         ExclusiveRegionSelection ers (*this, entered_regionview);
3261
3262         RegionSelection& rs (get_regions_for_action ());
3263         nframes64_t where = get_preferred_edit_position();
3264
3265         begin_reversible_command (_("trim region end to edit point"));
3266
3267         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3268                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3269
3270                 if (!arv) {
3271                         continue;
3272                 }
3273
3274                 /* require region to cover trim */
3275
3276                 if (!arv->region()->covers (where)) {
3277                         continue;
3278                 }
3279
3280                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3281
3282                 if (!atav) {
3283                         return;
3284                 }
3285
3286                 float speed = 1.0;
3287
3288                 if (atav->get_diskstream() != 0) {
3289                         speed = atav->get_diskstream()->speed();
3290                 }
3291
3292                 XMLNode &before = arv->region()->playlist()->get_state();
3293                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3294                 XMLNode &after = arv->region()->playlist()->get_state();
3295                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3296         }
3297                 
3298         commit_reversible_command ();
3299 }
3300
3301 /** Unfreeze selected routes */
3302 void
3303 Editor::unfreeze_routes ()
3304 {
3305         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3306                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*i);
3307                 if (atv && atv->is_audio_track()) {
3308                         atv->audio_track()->unfreeze ();
3309                 }
3310         }
3311 }
3312
3313 void*
3314 Editor::_freeze_thread (void* arg)
3315 {
3316         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
3317         return static_cast<Editor*>(arg)->freeze_thread ();
3318 }
3319
3320 void*
3321 Editor::freeze_thread ()
3322 {
3323         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3324                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*i);
3325                 if (atv && atv->is_audio_track()) {
3326                         atv->audio_track()->freeze (*current_interthread_info);
3327                 }
3328         }
3329
3330         current_interthread_info->done = true;
3331         
3332         return 0;
3333 }
3334
3335 gint
3336 Editor::freeze_progress_timeout (void *arg)
3337 {
3338         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
3339         return !(current_interthread_info->done || current_interthread_info->cancel);
3340 }
3341
3342 /** Freeze selected routes */
3343 void
3344 Editor::freeze_routes ()
3345 {
3346         InterThreadInfo itt;
3347
3348         if (interthread_progress_window == 0) {
3349                 build_interthread_progress_window ();
3350         }
3351
3352         WindowTitle title(Glib::get_application_name());
3353         title += _("Freeze");
3354         interthread_progress_window->set_title (title.get_string());
3355         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3356         interthread_progress_window->show_all ();
3357         interthread_progress_bar.set_fraction (0.0f);
3358         interthread_progress_label.set_text ("");
3359         interthread_cancel_label.set_text (_("Cancel Freeze"));
3360         current_interthread_info = &itt;
3361
3362         interthread_progress_connection = 
3363           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3364
3365         itt.done = false;
3366         itt.cancel = false;
3367         itt.progress = 0.0f;
3368         
3369         pthread_attr_t attr;
3370         pthread_attr_init(&attr);
3371         pthread_attr_setstacksize(&attr, 500000);
3372
3373         pthread_create (&itt.thread, &attr, _freeze_thread, this);
3374
3375         pthread_attr_destroy(&attr);
3376
3377         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3378
3379         while (!itt.done && !itt.cancel) {
3380                 gtk_main_iteration ();
3381         }
3382
3383         interthread_progress_connection.disconnect ();
3384         interthread_progress_window->hide_all ();
3385         current_interthread_info = 0;
3386         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3387 }
3388
3389 void
3390 Editor::bounce_range_selection ()
3391 {
3392         if (selection->time.empty()) {
3393                 return;
3394         }
3395
3396         TrackSelection views = selection->tracks;
3397
3398         nframes_t start = selection->time[clicked_selection].start;
3399         nframes_t end = selection->time[clicked_selection].end;
3400         nframes_t cnt = end - start + 1;
3401
3402         begin_reversible_command (_("bounce range"));
3403
3404         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3405
3406                 RouteTimeAxisView* rtv;
3407
3408                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3409                         continue;
3410                 }
3411                 
3412                 boost::shared_ptr<Playlist> playlist;
3413                 
3414                 if ((playlist = rtv->playlist()) == 0) {
3415                         return;
3416                 }
3417
3418                 InterThreadInfo itt;
3419                 
3420                 itt.done = false;
3421                 itt.cancel = false;
3422                 itt.progress = false;
3423
3424                 XMLNode &before = playlist->get_state();
3425                 rtv->track()->bounce_range (start, cnt, itt);
3426                 XMLNode &after = playlist->get_state();
3427                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3428         }
3429         
3430         commit_reversible_command ();
3431 }
3432
3433 /** Cut selected regions, automation points or a time range */
3434 void
3435 Editor::cut ()
3436 {
3437         cut_copy (Cut);
3438 }
3439
3440 /** Copy selected regions, automation points or a time range */
3441 void
3442 Editor::copy ()
3443 {
3444         cut_copy (Copy);
3445 }
3446
3447
3448 /** @return true if a Cut, Copy or Clear is possible */
3449 bool
3450 Editor::can_cut_copy () const
3451 {
3452         switch (current_mouse_mode()) {
3453                 
3454         case MouseObject:
3455                 if (!selection->regions.empty() || !selection->points.empty()) {
3456                         return true;
3457                 }
3458                 break;
3459                 
3460         case MouseRange:
3461                 if (!selection->time.empty()) {
3462                         return true;
3463                 }
3464                 break;
3465                 
3466         default:
3467                 break;
3468         }
3469
3470         return false;
3471 }
3472
3473
3474 /** Cut, copy or clear selected regions, automation points or a time range.
3475  * @param op Operation (Cut, Copy or Clear)
3476  */
3477 void 
3478 Editor::cut_copy (CutCopyOp op)
3479 {
3480         /* only cancel selection if cut/copy is successful.*/
3481
3482         string opname;
3483
3484         switch (op) {
3485         case Cut:
3486                 opname = _("cut");
3487                 break;
3488         case Copy:
3489                 opname = _("copy");
3490                 break;
3491         case Clear:
3492                 opname = _("clear");
3493                 break;
3494         }
3495         
3496         cut_buffer->clear ();
3497
3498         if (entered_marker) {
3499
3500                 /* cut/delete op while pointing at a marker */
3501
3502                 bool ignored;
3503                 Location* loc = find_location_from_marker (entered_marker, ignored);
3504
3505                 if (session && loc) {
3506                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3507                 }
3508
3509                 return;
3510         }
3511
3512         switch (current_mouse_mode()) {
3513         case MouseObject: 
3514                 cerr << "cutting in object mode\n";
3515                 if (!selection->regions.empty() || !selection->points.empty()) {
3516
3517                         begin_reversible_command (opname + _(" objects"));
3518
3519                         if (!selection->regions.empty()) {
3520                                 cerr << "have regions to cut" << endl;
3521                                 cut_copy_regions (op);
3522                                 
3523                                 if (op == Cut) {
3524                                         selection->clear_regions ();
3525                                 }
3526                         }
3527
3528                         if (!selection->points.empty()) {
3529                                 cut_copy_points (op);
3530
3531                                 if (op == Cut) {
3532                                         selection->clear_points ();
3533                                 }
3534                         }
3535
3536                         commit_reversible_command ();   
3537                         break; // terminate case statement here
3538                 } 
3539                 cerr << "nope, now cutting time range" << endl;
3540                 if (!selection->time.empty()) {
3541                         /* don't cause suprises */
3542                         break;
3543                 }
3544                 // fall thru if there was nothing selected
3545                 
3546         case MouseRange:
3547                 if (selection->time.empty()) {
3548                         nframes64_t start, end;
3549                         cerr << "no time selection, get edit op range" << endl;
3550                         if (!get_edit_op_range (start, end)) {
3551                                 cerr << "no edit op range" << endl;
3552                                 return;
3553                         }
3554                         selection->set ((TimeAxisView*) 0, start, end);
3555                 }
3556                         
3557                 begin_reversible_command (opname + _(" range"));
3558                 cut_copy_ranges (op);
3559                 commit_reversible_command ();
3560                 
3561                 if (op == Cut) {
3562                         selection->clear_time ();
3563                 }
3564
3565                 break;
3566                 
3567         default:
3568                 break;
3569         }
3570 }
3571
3572 /** Cut, copy or clear selected automation points.
3573  * @param op Operation (Cut, Copy or Clear)
3574  */
3575 void
3576 Editor::cut_copy_points (CutCopyOp op)
3577 {
3578         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3579
3580                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3581
3582                 if (atv) {
3583                         atv->cut_copy_clear_objects (selection->points, op);
3584                 } 
3585         }
3586 }
3587
3588 struct PlaylistState {
3589     boost::shared_ptr<Playlist> playlist;
3590     XMLNode*  before;
3591 };
3592
3593 struct lt_playlist {
3594     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3595             return a.playlist < b.playlist;
3596     }
3597 };
3598         
3599 struct PlaylistMapping { 
3600     TimeAxisView* tv;
3601     boost::shared_ptr<Playlist> pl;
3602
3603     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3604 };
3605
3606
3607 /** Cut, copy or clear selected regions.
3608  * @param op Operation (Cut, Copy or Clear)
3609  */
3610 void
3611 Editor::cut_copy_regions (CutCopyOp op)
3612 {       
3613         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3614            a map when we want ordered access to both elements. i think.
3615         */
3616
3617         vector<PlaylistMapping> pmap;
3618
3619         nframes_t first_position = max_frames;
3620         
3621         set<PlaylistState, lt_playlist> freezelist;
3622         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3623         
3624         /* get ordering correct before we cut/copy */
3625         
3626         selection->regions.sort_by_position_and_track ();
3627
3628         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3629
3630                 first_position = min ((*x)->region()->position(), first_position);
3631
3632                 if (op == Cut || op == Clear) {
3633                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3634
3635                         if (pl) {
3636
3637                                 PlaylistState before;
3638                                 before.playlist = pl;
3639                                 before.before = &pl->get_state();
3640                                 
3641                                 insert_result = freezelist.insert (before);
3642                                 
3643                                 if (insert_result.second) {
3644                                         pl->freeze ();
3645                                 }
3646                         }
3647                 }
3648
3649                 TimeAxisView* tv = &(*x)->get_trackview();
3650                 vector<PlaylistMapping>::iterator z;
3651
3652                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3653                         if ((*z).tv == tv) {
3654                                 break;
3655                         }
3656                 }
3657                 
3658                 if (z == pmap.end()) {
3659                         pmap.push_back (PlaylistMapping (tv));
3660                 }
3661         }
3662
3663         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3664
3665                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3666                 
3667                 if (!pl) {
3668                         /* impossible, but this handles it for the future */
3669                         continue;
3670                 }
3671
3672                 TimeAxisView& tv = (*x)->get_trackview();
3673                 boost::shared_ptr<Playlist> npl;
3674                 RegionSelection::iterator tmp;
3675                 
3676                 tmp = x;
3677                 ++tmp;
3678
3679                 vector<PlaylistMapping>::iterator z;
3680                 
3681                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3682                         if ((*z).tv == &tv) {
3683                                 break;
3684                         }
3685                 }
3686                 
3687                 assert (z != pmap.end());
3688                 
3689                 if (!(*z).pl) {
3690                         npl = PlaylistFactory::create (pl->data_type(), *session, "cutlist", true);
3691                         npl->freeze();
3692                         (*z).pl = npl;
3693                 } else {
3694                         npl = (*z).pl;
3695                 }
3696                 
3697                 boost::shared_ptr<Region> r = (*x)->region();
3698                 boost::shared_ptr<Region> _xx;
3699
3700                 assert (r != 0);
3701
3702                 switch (op) {
3703                 case Cut:
3704                         _xx = RegionFactory::create (r);
3705                         npl->add_region (_xx, r->position() - first_position);
3706                         pl->remove_region (r);
3707                         break;
3708                         
3709                 case Copy:
3710                         /* copy region before adding, so we're not putting same object into two different playlists */
3711                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
3712                         break;
3713                         
3714                 case Clear:
3715                         pl->remove_region (r);
3716                         break;
3717                 }
3718
3719                 x = tmp;
3720         }
3721         
3722         list<boost::shared_ptr<Playlist> > foo;
3723         
3724         /* the pmap is in the same order as the tracks in which selected regions occured */
3725         
3726         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3727                 (*i).pl->thaw();
3728                 foo.push_back ((*i).pl);
3729         }
3730         
3731
3732         if (!foo.empty()) {
3733                 cut_buffer->set (foo);
3734         }
3735
3736         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3737                 (*pl).playlist->thaw ();
3738                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3739         }
3740 }
3741
3742 void
3743 Editor::cut_copy_ranges (CutCopyOp op)
3744 {
3745         TrackSelection* ts;
3746
3747         if (selection->tracks.empty()) {
3748                 ts = &track_views;
3749         } else {
3750                 ts = &selection->tracks;
3751         }
3752
3753         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3754                 (*i)->cut_copy_clear (*selection, op);
3755         }
3756 }
3757
3758 void
3759 Editor::paste (float times)
3760 {
3761         paste_internal (get_preferred_edit_position(), times);
3762 }
3763
3764 void
3765 Editor::mouse_paste ()
3766 {
3767         nframes64_t where;
3768         bool ignored;
3769
3770         if (!mouse_frame (where, ignored)) {
3771                 return;
3772         }
3773
3774         snap_to (where);
3775         paste_internal (where, 1);
3776 }
3777
3778 void
3779 Editor::paste_internal (nframes_t position, float times)
3780 {
3781         bool commit = false;
3782
3783         if (cut_buffer->empty()) {
3784                 return;
3785         }
3786
3787         if (position == max_frames) {
3788                 position = get_preferred_edit_position();
3789         }
3790
3791         begin_reversible_command (_("paste"));
3792
3793         TrackSelection ts;
3794         TrackSelection::iterator i;
3795         size_t nth;
3796
3797         /* get everything in the correct order */
3798
3799
3800         if (!selection->tracks.empty()) {
3801                 sort_track_selection ();
3802                 ts = selection->tracks;
3803         } else if (entered_track) {
3804                 ts.push_back (entered_track);
3805         }
3806
3807         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3808
3809                 /* undo/redo is handled by individual tracks */
3810
3811                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3812                         commit = true;
3813                 }
3814         }
3815         
3816         if (commit) {
3817                 commit_reversible_command ();
3818         }
3819 }
3820
3821 void
3822 Editor::paste_named_selection (float times)
3823 {
3824         TrackSelection::iterator t;
3825
3826         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3827
3828         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3829                 return;
3830         }
3831
3832         TreeModel::iterator i = selected->get_selected();
3833         NamedSelection* ns = (*i)[named_selection_columns.selection];
3834
3835         list<boost::shared_ptr<Playlist> >::iterator chunk;
3836         list<boost::shared_ptr<Playlist> >::iterator tmp;
3837
3838         chunk = ns->playlists.begin();
3839                 
3840         begin_reversible_command (_("paste chunk"));
3841         
3842         sort_track_selection ();
3843
3844         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3845                 
3846                 RouteTimeAxisView* rtv;
3847                 boost::shared_ptr<Playlist> pl;
3848                 boost::shared_ptr<AudioPlaylist> apl;
3849
3850                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*t)) == 0) {
3851                         continue;
3852                 }
3853
3854                 if ((pl = rtv->playlist()) == 0) {
3855                         continue;
3856                 }
3857                 
3858                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3859                         continue;
3860                 }
3861
3862                 tmp = chunk;
3863                 ++tmp;
3864
3865                 XMLNode &before = apl->get_state();
3866                 apl->paste (*chunk, get_preferred_edit_position(), times);
3867                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3868
3869                 if (tmp != ns->playlists.end()) {
3870                         chunk = tmp;
3871                 }
3872         }
3873
3874         commit_reversible_command();
3875 }
3876
3877 void
3878 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3879 {
3880         boost::shared_ptr<Playlist> playlist; 
3881         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
3882         RegionSelection foo;
3883
3884         begin_reversible_command (_("duplicate region"));
3885
3886         selection->clear_regions ();
3887
3888         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3889
3890                 boost::shared_ptr<Region> r ((*i)->region());
3891
3892                 TimeAxisView& tv = (*i)->get_time_axis_view();
3893                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3894                 latest_regionviews.clear ();
3895                 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3896                 
3897                 playlist = (*i)->region()->playlist();
3898                 XMLNode &before = playlist->get_state();
3899                 playlist->duplicate (r, r->last_frame(), times);
3900                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3901
3902                 c.disconnect ();
3903                 
3904                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3905         }
3906
3907         commit_reversible_command ();
3908
3909         if (!foo.empty()) {
3910                 selection->set (foo);
3911         }
3912 }
3913
3914 void
3915 Editor::duplicate_selection (float times)
3916 {
3917         if (selection->time.empty() || selection->tracks.empty()) {
3918                 return;
3919         }
3920
3921         boost::shared_ptr<Playlist> playlist; 
3922         vector<boost::shared_ptr<AudioRegion> > new_regions;
3923         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3924                 
3925         create_region_from_selection (new_regions);
3926
3927         if (new_regions.empty()) {
3928                 return;
3929         }
3930         
3931         begin_reversible_command (_("duplicate selection"));
3932
3933         ri = new_regions.begin();
3934
3935         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3936                 if ((playlist = (*i)->playlist()) == 0) {
3937                         continue;
3938                 }
3939                 XMLNode &before = playlist->get_state();
3940                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3941                 XMLNode &after = playlist->get_state();
3942                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3943
3944                 ++ri;
3945                 if (ri == new_regions.end()) {
3946                         --ri;
3947                 }
3948         }
3949
3950         commit_reversible_command ();
3951 }
3952
3953 void
3954 Editor::reset_point_selection ()
3955 {
3956         /* reset all selected points to the relevant default value */
3957
3958         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3959                 
3960                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3961                 
3962                 if (atv) {
3963                         atv->reset_objects (selection->points);
3964                 } 
3965         }
3966 }
3967
3968 void
3969 Editor::center_playhead ()
3970 {
3971         float page = canvas_width * frames_per_unit;
3972         center_screen_internal (playhead_cursor->current_frame, page);
3973 }
3974
3975 void
3976 Editor::center_edit_point ()
3977 {
3978         float page = canvas_width * frames_per_unit;
3979         center_screen_internal (get_preferred_edit_position(), page);
3980 }
3981
3982 void
3983 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3984 {
3985         begin_reversible_command (_("clear playlist"));
3986         XMLNode &before = playlist->get_state();
3987         playlist->clear ();
3988         XMLNode &after = playlist->get_state();
3989         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3990         commit_reversible_command ();
3991 }
3992
3993 void
3994 Editor::nudge_selected_tracks (bool use_edit, bool forwards)
3995 {
3996         boost::shared_ptr<Playlist> playlist; 
3997         nframes_t distance;
3998         nframes_t next_distance;
3999         nframes_t start;
4000
4001         if (use_edit) {
4002                 start = get_preferred_edit_position();
4003         } else {
4004                 start = 0;
4005         }
4006
4007         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4008                 return;
4009         }
4010         
4011         if (selection->tracks.empty()) {
4012                 return;
4013         }
4014         
4015         begin_reversible_command (_("nudge track"));
4016         
4017         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4018
4019                 if ((playlist = (*i)->playlist()) == 0) {
4020                         continue;
4021                 }               
4022                 
4023                 XMLNode &before = playlist->get_state();
4024                 playlist->nudge_after (start, distance, forwards);
4025                 XMLNode &after = playlist->get_state();
4026                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4027         }
4028         
4029         commit_reversible_command ();                   
4030 }
4031
4032 void
4033 Editor::remove_last_capture ()
4034 {
4035         vector<string> choices;
4036         string prompt;
4037         
4038         if (!session) {
4039                 return;
4040         }
4041
4042         if (Config->get_verify_remove_last_capture()) {
4043                 prompt  = _("Do you really want to destroy the last capture?"
4044                             "\n(This is destructive and cannot be undone)");
4045
4046                 choices.push_back (_("No, do nothing."));
4047                 choices.push_back (_("Yes, destroy it."));
4048                 
4049                 Gtkmm2ext::Choice prompter (prompt, choices);
4050                 
4051                 if (prompter.run () == 1) {
4052                         session->remove_last_capture ();
4053                 }
4054
4055         } else {
4056                 session->remove_last_capture();
4057         }
4058 }
4059
4060 void
4061 Editor::normalize_regions ()
4062 {
4063         if (!session) {
4064                 return;
4065         }
4066
4067         if (selection->regions.empty()) {
4068                 return;
4069         }
4070
4071         begin_reversible_command (_("normalize"));
4072
4073         track_canvas.get_window()->set_cursor (*wait_cursor);
4074         gdk_flush ();
4075
4076         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4077                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4078                 if (!arv)
4079                         continue;
4080                 XMLNode &before = arv->region()->get_state();
4081                 arv->audio_region()->normalize_to (0.0f);
4082                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4083         }
4084
4085         commit_reversible_command ();
4086         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4087 }
4088
4089
4090 void
4091 Editor::denormalize_regions ()
4092 {
4093         if (!session) {
4094                 return;
4095         }
4096
4097         if (selection->regions.empty()) {
4098                 return;
4099         }
4100
4101         begin_reversible_command ("denormalize");
4102
4103         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4104                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4105                 if (!arv)
4106                         continue;
4107                 XMLNode &before = arv->region()->get_state();
4108                 arv->audio_region()->set_scale_amplitude (1.0f);
4109                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4110         }
4111
4112         commit_reversible_command ();
4113 }
4114
4115
4116 void
4117 Editor::reverse_regions ()
4118 {
4119         if (!session) {
4120                 return;
4121         }
4122
4123         Reverse rev (*session);
4124         apply_filter (rev, _("reverse regions"));
4125 }
4126
4127
4128 void
4129 Editor::quantize_regions ()
4130 {
4131         if (!session) {
4132                 return;
4133         }
4134
4135         // FIXME: varying meter?
4136         Quantize quant (*session, snap_length_beats(0));
4137         apply_filter (quant, _("quantize regions"));
4138 }
4139
4140 void
4141 Editor::apply_filter (Filter& filter, string command)
4142 {
4143         if (selection->regions.empty()) {
4144                 return;
4145         }
4146
4147         begin_reversible_command (command);
4148
4149         track_canvas.get_window()->set_cursor (*wait_cursor);
4150         gdk_flush ();
4151
4152         /* this is ugly. */
4153         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
4154                 RegionSelection::iterator tmp = r;
4155                 ++tmp;
4156
4157                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4158                 if (mrv) {
4159                         if (mrv->midi_region()->apply(filter) == 0) {
4160                                 mrv->redisplay_model();
4161                         }
4162                 }
4163
4164                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4165                 if (arv) {
4166                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4167
4168                         if (arv->audio_region()->apply (filter) == 0) {
4169
4170                                 XMLNode &before = playlist->get_state();
4171                                 playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4172                                 XMLNode &after = playlist->get_state();
4173                                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4174                         } else {
4175                                 goto out;
4176                         }
4177                 }
4178
4179                 r = tmp;
4180         }
4181
4182         commit_reversible_command ();
4183         selection->regions.clear ();
4184
4185   out:
4186         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4187 }
4188
4189 void
4190 Editor::region_selection_op (void (Region::*pmf)(void))
4191 {
4192         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4193                 Region* region = (*i)->region().get();
4194                 (region->*pmf)();
4195         }
4196 }
4197
4198
4199 void
4200 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4201 {
4202         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4203                 Region* region = (*i)->region().get();
4204                 (region->*pmf)(arg);
4205         }
4206 }
4207
4208 void
4209 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4210 {
4211         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4212                 Region* region = (*i)->region().get();
4213                 (region->*pmf)(yn);
4214         }
4215 }
4216
4217 void
4218 Editor::external_edit_region ()
4219 {
4220         /* more to come */
4221 }
4222
4223 void
4224 Editor::brush (nframes_t pos)
4225 {
4226         RegionSelection sel;
4227         snap_to (pos);
4228
4229         if (selection->regions.empty()) {
4230                 /* XXX get selection from region list */
4231         } else { 
4232                 sel = selection->regions;
4233         }
4234
4235         if (sel.empty()) {
4236                 return;
4237         }
4238
4239         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4240                 mouse_brush_insert_region ((*i), pos);
4241         }
4242 }
4243
4244 void
4245 Editor::reset_region_gain_envelopes ()
4246 {
4247         if (!session || selection->regions.empty()) {
4248                 return;
4249         }
4250
4251         session->begin_reversible_command (_("reset region gain"));
4252
4253         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4254                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4255                 if (arv) {
4256                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4257                         XMLNode& before (alist->get_state());
4258
4259                         arv->audio_region()->set_default_envelope ();
4260                         session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4261                 }
4262         }
4263
4264         session->commit_reversible_command ();
4265 }
4266
4267 void
4268 Editor::toggle_gain_envelope_visibility ()
4269 {
4270         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4271                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4272                 if (arv) {
4273                         bool x = region_envelope_visible_item->get_active();
4274                         if (x != arv->envelope_visible()) {
4275                                 arv->set_envelope_visible (x);
4276                         }
4277                 }
4278         }
4279 }
4280
4281 void
4282 Editor::toggle_gain_envelope_active ()
4283 {
4284         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4285                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4286                 if (arv) {
4287                         bool x = region_envelope_active_item->get_active();
4288                         if (x != arv->audio_region()->envelope_active()) {
4289                                 arv->audio_region()->set_envelope_active (x);
4290                         }
4291                 }
4292         }
4293 }
4294
4295 /** Set the position-locked state of all selected regions to a particular value.
4296  * @param yn true to make locked, false to make unlocked.
4297  */
4298 void
4299 Editor::toggle_region_position_lock ()
4300 {
4301         bool x = region_lock_position_item->get_active();
4302
4303         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4304                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4305                 if (arv) {
4306                         if (x != arv->audio_region()->locked()) {
4307                                 arv->audio_region()->set_position_locked (x);
4308                         }
4309                 }
4310         }
4311 }
4312
4313 void
4314 Editor::toggle_region_lock ()
4315 {
4316         bool x = region_lock_item->get_active();
4317
4318         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4319                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4320                 if (arv) {
4321                         if (x != arv->audio_region()->locked()) {
4322                                 arv->audio_region()->set_locked (x);
4323                         }
4324                 }
4325         }
4326 }
4327
4328 void
4329 Editor::toggle_region_mute ()
4330 {
4331         bool x = region_mute_item->get_active();
4332
4333         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4334                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4335                 if (arv) {
4336                         if (x != arv->audio_region()->muted()) {
4337                                 arv->audio_region()->set_muted (x);
4338                         }
4339                 }
4340         }
4341 }
4342
4343 void
4344 Editor::toggle_region_opaque ()
4345 {
4346         bool x = region_opaque_item->get_active();
4347
4348         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4349                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4350                 if (arv) {
4351                         if (x != arv->audio_region()->opaque()) {
4352                                 arv->audio_region()->set_opaque (x);
4353                         }
4354                 }
4355         }
4356 }
4357
4358 void
4359 Editor::set_fade_length (bool in)
4360 {
4361         ExclusiveRegionSelection esr (*this, entered_regionview);
4362
4363         /* we need a region to measure the offset from the start */
4364
4365         RegionView* rv;
4366
4367         if (!selection->regions.empty()) {
4368                 rv = selection->regions.front();
4369         } else if (entered_regionview) {
4370                 rv = entered_regionview;
4371         } else {
4372                 return;
4373         }
4374
4375         nframes64_t pos = get_preferred_edit_position();
4376         nframes_t len;
4377         char* cmd;
4378         
4379         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4380                 /* edit point is outside the relevant region */
4381                 return;
4382         }
4383
4384         if (in) {
4385                 if (pos <= rv->region()->position()) {
4386                         /* can't do it */
4387                         return;
4388                 }
4389                 len = pos - rv->region()->position();
4390                 cmd = _("set fade in length");
4391         } else {
4392                 if (pos >= rv->region()->last_frame()) {
4393                         /* can't do it */
4394                         return;
4395                 }
4396                 len = rv->region()->last_frame() - pos;
4397                 cmd = _("set fade out length");
4398         }
4399
4400         begin_reversible_command (cmd);
4401
4402         RegionSelection& rs (get_regions_for_action());
4403
4404         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4405                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4406
4407                 if (!tmp) {
4408                         return;
4409                 }
4410
4411                 boost::shared_ptr<AutomationList> alist;
4412                 if (in) {
4413                         alist = tmp->audio_region()->fade_in();
4414                 } else {
4415                         alist = tmp->audio_region()->fade_out();
4416                 }
4417
4418                 XMLNode &before = alist->get_state();
4419
4420                 if (in) {
4421                         tmp->audio_region()->set_fade_in_length (len);
4422                 } else {
4423                         tmp->audio_region()->set_fade_out_length (len);
4424                 }
4425                 
4426                 XMLNode &after = alist->get_state();
4427                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4428         }
4429
4430         commit_reversible_command ();
4431 }
4432
4433 void
4434 Editor::toggle_fade_active (bool in)
4435 {
4436         ensure_entered_region_selected (true);
4437
4438         if (selection->regions.empty()) {
4439                 return;
4440         }
4441
4442         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4443         bool have_switch = false;
4444         bool yn;
4445
4446         begin_reversible_command (cmd);
4447
4448         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4449                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4450                 
4451                 if (!tmp) {
4452                         return;
4453                 }
4454
4455                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4456
4457                 /* make the behaviour consistent across all regions */
4458                 
4459                 if (!have_switch) {
4460                         if (in) {
4461                                 yn = region->fade_in_active();
4462                         } else {
4463                                 yn = region->fade_out_active();
4464                         }
4465                         have_switch = true;
4466                 }
4467
4468                 XMLNode &before = region->get_state();
4469                 if (in) {
4470                         region->set_fade_in_active (!yn);
4471                 } else {
4472                         region->set_fade_out_active (!yn);
4473                 }
4474                 XMLNode &after = region->get_state();
4475                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4476         }
4477
4478         commit_reversible_command ();
4479 }
4480
4481 void
4482 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4483 {
4484
4485         begin_reversible_command (_("set fade in shape"));
4486
4487         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4488                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4489
4490                 if (!tmp) {
4491                         return;
4492                 }
4493
4494                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4495                 XMLNode &before = alist->get_state();
4496
4497                 tmp->audio_region()->set_fade_in_shape (shape);
4498                 
4499                 XMLNode &after = alist->get_state();
4500                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4501         }
4502
4503         commit_reversible_command ();
4504                 
4505 }
4506
4507 void
4508 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4509 {
4510         begin_reversible_command (_("set fade out shape"));
4511
4512         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4513                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4514
4515                 if (!tmp) {
4516                         return;
4517                 }
4518
4519                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4520                 XMLNode &before = alist->get_state();
4521
4522                 tmp->audio_region()->set_fade_out_shape (shape);
4523                 
4524                 XMLNode &after = alist->get_state();
4525                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4526         }
4527
4528         commit_reversible_command ();
4529 }
4530
4531 void
4532 Editor::set_fade_in_active (bool yn)
4533 {
4534         begin_reversible_command (_("set fade in active"));
4535
4536         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4537                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4538
4539                 if (!tmp) {
4540                         return;
4541                 }
4542
4543
4544                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4545
4546                 XMLNode &before = ar->get_state();
4547
4548                 ar->set_fade_in_active (yn);
4549                 
4550                 XMLNode &after = ar->get_state();
4551                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4552         }
4553
4554         commit_reversible_command ();
4555 }
4556
4557 void
4558 Editor::set_fade_out_active (bool yn)
4559 {
4560         begin_reversible_command (_("set fade out active"));
4561
4562         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4563                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4564
4565                 if (!tmp) {
4566                         return;
4567                 }
4568
4569                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4570
4571                 XMLNode &before = ar->get_state();
4572
4573                 ar->set_fade_out_active (yn);
4574                 
4575                 XMLNode &after = ar->get_state();
4576                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4577         }
4578
4579         commit_reversible_command ();
4580 }
4581
4582
4583 /** Update crossfade visibility after its configuration has been changed */
4584 void
4585 Editor::update_xfade_visibility ()
4586 {
4587         _xfade_visibility = Config->get_xfades_visible ();
4588         
4589         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4590                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4591                 if (v) {
4592                         if (_xfade_visibility) {
4593                                 v->show_all_xfades ();
4594                         } else {
4595                                 v->hide_all_xfades ();
4596                         }
4597                 }
4598         }
4599 }
4600
4601 void
4602 Editor::set_edit_point ()
4603 {
4604         nframes64_t where;
4605         bool ignored;
4606
4607         if (!mouse_frame (where, ignored)) {
4608                 return;
4609         }
4610         
4611         snap_to (where);
4612
4613         if (selection->markers.empty()) {
4614                 
4615                 mouse_add_new_marker (where);
4616
4617         } else {
4618                 bool ignored;
4619
4620                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4621
4622                 if (loc) {
4623                         loc->move_to (where);
4624                 }
4625         }
4626 }
4627
4628 void
4629 Editor::set_playhead_cursor ()
4630 {
4631         if (entered_marker) {
4632                 session->request_locate (entered_marker->position(), session->transport_rolling());
4633         } else {
4634                 nframes64_t where;
4635                 bool ignored;
4636
4637                 if (!mouse_frame (where, ignored)) {
4638                         return;
4639                 }
4640                         
4641                 snap_to (where);
4642                 
4643                 if (session) {
4644                         session->request_locate (where, session->transport_rolling());
4645                 }
4646         }
4647 }
4648
4649 void
4650 Editor::split ()
4651 {
4652         ensure_entered_region_selected ();
4653         
4654         nframes64_t where = get_preferred_edit_position();
4655
4656         if (!selection->regions.empty()) {
4657                 
4658                 split_regions_at (where, selection->regions);
4659
4660         } else {
4661                 
4662                 RegionSelection rs;
4663                 rs = get_regions_at (where, selection->tracks);
4664                 split_regions_at (where, rs);
4665         }
4666 }
4667
4668 void
4669 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4670 {
4671         if (entered_track && mouse_mode == MouseObject) {
4672                 if (!selection->tracks.empty()) {
4673                         if (!selection->selected (entered_track)) {
4674                                 selection->add (entered_track);
4675                         }
4676                 } else {
4677                         /* there is no selection, but this operation requires/prefers selected objects */
4678
4679                         if (op_really_wants_one_track_if_none_are_selected) {
4680                                 selection->set (entered_track);
4681                         }
4682                 }
4683         }
4684 }
4685
4686 void
4687 Editor::ensure_entered_region_selected (bool op_really_wants_one_region_if_none_are_selected)
4688 {
4689         if (!entered_regionview || mouse_mode != MouseObject) {
4690                 return;
4691         }
4692
4693         
4694         /* heuristic:
4695            
4696         - if there is no existing selection, don't change it. the operation will thus apply to "all"
4697         
4698         - if there is an existing selection, but the entered regionview isn't in it, add it. this
4699         avoids key-mouse ops on unselected regions from interfering with an existing selection,
4700         but also means that the operation will apply to the pointed-at region.
4701         */
4702         
4703         if (!selection->regions.empty()) {
4704                 if (!selection->selected (entered_regionview)) {
4705                         selection->add (entered_regionview);
4706                 }
4707         } else {
4708                 /* there is no selection, but this operation requires/prefers selected objects */
4709                 
4710                 if (op_really_wants_one_region_if_none_are_selected) {
4711                         selection->set (entered_regionview, false);
4712                 }
4713         }
4714 }
4715
4716 void
4717 Editor::trim_region_front ()
4718 {
4719         trim_region (true);
4720 }
4721
4722 void
4723 Editor::trim_region_back ()
4724 {
4725         trim_region (false);
4726 }
4727
4728 void
4729 Editor::trim_region (bool front)
4730 {
4731         ExclusiveRegionSelection ers (*this, entered_regionview);
4732
4733         nframes64_t where = get_preferred_edit_position();
4734         RegionSelection& rs = get_regions_for_action ();
4735
4736         if (rs.empty()) {
4737                 return;
4738         }
4739
4740         begin_reversible_command (front ? _("trim front") : _("trim back"));
4741
4742         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4743                 if (!(*i)->region()->locked()) {
4744                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4745                         XMLNode &before = pl->get_state();
4746                         if (front) {
4747                                 (*i)->region()->trim_front (where, this);       
4748                         } else {
4749                                 (*i)->region()->trim_end (where, this); 
4750                         }
4751                         XMLNode &after = pl->get_state();
4752                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4753                 }
4754         }
4755
4756         commit_reversible_command ();
4757 }
4758
4759 struct EditorOrderRouteSorter {
4760     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4761             /* use of ">" forces the correct sort order */
4762             return a->order_key ("editor") < b->order_key ("editor");
4763     }
4764 };
4765
4766 void
4767 Editor::select_next_route()
4768 {
4769         if (selection->tracks.empty()) {
4770                 selection->set (track_views.front());
4771                 return;
4772         }
4773
4774         TimeAxisView* current = selection->tracks.front();
4775
4776         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4777                 if (*i == current) {
4778                         ++i;
4779                         if (i != track_views.end()) {
4780                                 selection->set (*i);
4781                         } else {
4782                                 selection->set (*(track_views.begin()));
4783                         }
4784                         break;
4785                 }
4786         }
4787 }
4788
4789 void
4790 Editor::select_prev_route()
4791 {
4792         if (selection->tracks.empty()) {
4793                 selection->set (track_views.front());
4794                 return;
4795         }
4796
4797         TimeAxisView* current = selection->tracks.front();
4798
4799         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4800                 if (*i == current) {
4801                         ++i;
4802                         if (i != track_views.rend()) {
4803                                 selection->set (*i);
4804                         } else {
4805                                 selection->set (*(track_views.rbegin()));
4806                         }
4807                         break;
4808                 }
4809         }
4810 }
4811
4812 void
4813 Editor::set_loop_from_selection (bool play)
4814 {
4815         if (session == 0 || selection->time.empty()) {
4816                 return;
4817         }
4818
4819         nframes_t start = selection->time[clicked_selection].start;
4820         nframes_t end = selection->time[clicked_selection].end;
4821         
4822         set_loop_range (start, end,  _("set loop range from selection"));
4823
4824         if (play) {
4825                 session->request_play_loop (true);
4826                 session->request_locate (start, true);
4827         }
4828 }
4829
4830 void
4831 Editor::set_loop_from_edit_range (bool play)
4832 {
4833         if (session == 0) {
4834                 return;
4835         }
4836
4837         nframes64_t start;
4838         nframes64_t end;
4839         
4840         if (!get_edit_op_range (start, end)) {
4841                 return;
4842         }
4843
4844         set_loop_range (start, end,  _("set loop range from edit range"));
4845
4846         if (play) {
4847                 session->request_play_loop (true);
4848                 session->request_locate (start, true);
4849         }
4850 }
4851
4852 void
4853 Editor::set_loop_from_region (bool play)
4854 {
4855         nframes64_t start = max_frames;
4856         nframes64_t end = 0;
4857
4858         ExclusiveRegionSelection esr (*this, entered_regionview);
4859
4860         if (selection->regions.empty()) {
4861                 info << _("cannot set loop: no region selected") << endmsg;
4862                 return;
4863         }
4864
4865         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4866                 if ((*i)->region()->position() < start) {
4867                         start = (*i)->region()->position();
4868                 }
4869                 if ((*i)->region()->last_frame() + 1 > end) {
4870                         end = (*i)->region()->last_frame() + 1;
4871                 }
4872         }
4873
4874         set_loop_range (start, end, _("set loop range from region"));
4875
4876         if (play) {
4877                 session->request_play_loop (true);
4878                 session->request_locate (start, true);
4879         }
4880 }
4881
4882 void
4883 Editor::set_punch_from_selection ()
4884 {
4885         if (session == 0 || selection->time.empty()) {
4886                 return;
4887         }
4888
4889         nframes_t start = selection->time[clicked_selection].start;
4890         nframes_t end = selection->time[clicked_selection].end;
4891         
4892         set_punch_range (start, end,  _("set punch range from selection"));
4893 }
4894
4895 void
4896 Editor::set_punch_from_edit_range ()
4897 {
4898         if (session == 0) {
4899                 return;
4900         }
4901
4902         nframes64_t start;
4903         nframes64_t end;
4904         
4905         if (!get_edit_op_range (start, end)) {
4906                 return;
4907         }
4908
4909         set_punch_range (start, end,  _("set punch range from edit range"));
4910 }
4911
4912 void
4913 Editor::set_punch_from_region ()
4914 {
4915         nframes64_t start = max_frames;
4916         nframes64_t end = 0;
4917
4918         ExclusiveRegionSelection esr (*this, entered_regionview);
4919
4920         if (selection->regions.empty()) {
4921                 info << _("cannot set punch: no region selected") << endmsg;
4922                 return;
4923         }
4924
4925         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4926                 if ((*i)->region()->position() < start) {
4927                         start = (*i)->region()->position();
4928                 }
4929                 if ((*i)->region()->last_frame() + 1 > end) {
4930                         end = (*i)->region()->last_frame() + 1;
4931                 }
4932         }
4933
4934         set_punch_range (start, end, _("set punch range from region"));
4935 }
4936
4937 void
4938 Editor::pitch_shift_regions ()
4939 {
4940         ensure_entered_region_selected (true);
4941         
4942         if (selection->regions.empty()) {
4943                 return;
4944         }
4945
4946         pitch_shift (selection->regions, 1.2);
4947 }
4948