Merge libs/ardour and gtk2_ardour with 2.0-ongoing R2837.
[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         ensure_entered_region_selected (true);
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_bounded_roll (start, end);
2408 }
2409
2410 void
2411 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2412 {
2413         session->audition_region (region);
2414 }
2415
2416 void
2417 Editor::build_interthread_progress_window ()
2418 {
2419         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2420
2421         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2422         
2423         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2424         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2425
2426         // GTK2FIX: this button needs a modifiable label
2427
2428         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2429         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2430
2431         interthread_cancel_button.add (interthread_cancel_label);
2432
2433         interthread_progress_window->set_default_size (200, 100);
2434 }
2435
2436 void
2437 Editor::interthread_cancel_clicked ()
2438 {
2439         if (current_interthread_info) {
2440                 current_interthread_info->cancel = true;
2441         }
2442 }
2443
2444 void
2445 Editor::region_from_selection ()
2446 {
2447         if (clicked_axisview == 0) {
2448                 return;
2449         }
2450
2451         if (selection->time.empty()) {
2452                 return;
2453         }
2454
2455         nframes_t start = selection->time[clicked_selection].start;
2456         nframes_t end = selection->time[clicked_selection].end;
2457
2458         nframes_t selection_cnt = end - start + 1;
2459         
2460         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2461                 boost::shared_ptr<AudioRegion> current;
2462                 boost::shared_ptr<Region> current_r;
2463                 boost::shared_ptr<Playlist> pl;
2464
2465                 nframes_t internal_start;
2466                 string new_name;
2467
2468                 if ((pl = (*i)->playlist()) == 0) {
2469                         continue;
2470                 }
2471
2472                 if ((current_r = pl->top_region_at (start)) == 0) {
2473                         continue;
2474                 }
2475
2476                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2477                 assert(current); // FIXME
2478                 if (current != 0) {
2479                         internal_start = start - current->position();
2480                         session->region_name (new_name, current->name(), true);
2481                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2482                 }
2483         }
2484 }       
2485
2486 void
2487 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2488 {
2489         if (selection->time.empty() || selection->tracks.empty()) {
2490                 return;
2491         }
2492
2493         nframes_t start = selection->time[clicked_selection].start;
2494         nframes_t end = selection->time[clicked_selection].end;
2495         
2496         sort_track_selection ();
2497
2498         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2499
2500                 boost::shared_ptr<AudioRegion> current;
2501                 boost::shared_ptr<Region> current_r;
2502                 boost::shared_ptr<Playlist> playlist;
2503                 nframes_t internal_start;
2504                 string new_name;
2505
2506                 if ((playlist = (*i)->playlist()) == 0) {
2507                         continue;
2508                 }
2509
2510                 if ((current_r = playlist->top_region_at(start)) == 0) {
2511                         continue;
2512                 }
2513
2514                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2515                         continue;
2516                 }
2517         
2518                 internal_start = start - current->position();
2519                 session->region_name (new_name, current->name(), true);
2520                 
2521                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2522         }
2523 }
2524
2525 void
2526 Editor::split_multichannel_region ()
2527 {
2528         if (selection->regions.empty()) {
2529                 return;
2530         }
2531
2532         vector<boost::shared_ptr<AudioRegion> > v;
2533
2534         for (list<RegionView*>::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2535
2536                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2537                 
2538                 if (!arv || arv->audio_region()->n_channels() < 2) {
2539                         continue;
2540                 }
2541
2542                 (arv)->audio_region()->separate_by_channel (*session, v);
2543         }
2544 }
2545
2546 void
2547 Editor::new_region_from_selection ()
2548 {
2549         region_from_selection ();
2550         cancel_selection ();
2551 }
2552
2553 static void
2554 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2555 {
2556         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2557         case OverlapNone:
2558                 break;
2559         default:
2560                 rs->push_back (rv);
2561         }
2562 }
2563
2564 void
2565 Editor::separate_regions_between (const TimeSelection& ts)
2566 {
2567         bool in_command = false;
2568         boost::shared_ptr<Playlist> playlist;
2569         RegionSelection new_selection;
2570                 
2571         sort_track_selection ();
2572
2573         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2574
2575                 RouteTimeAxisView* rtv;
2576                 
2577                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2578
2579                         boost::shared_ptr<Track> t = rtv->track();
2580
2581                         if (t != 0 && ! t->diskstream()->destructive()) {
2582                                 
2583                                 if ((playlist = rtv->playlist()) != 0) {
2584
2585                                         XMLNode *before = &(playlist->get_state());
2586                                         bool got_some = false;  
2587                                         
2588                                         /* XXX need to consider musical time selections here at some point */
2589
2590                                         double speed = t->diskstream()->speed();
2591
2592
2593                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2594
2595                                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2596                                                 latest_regionviews.clear ();
2597
2598                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2599
2600                                                 c.disconnect ();
2601
2602                                                 if (!latest_regionviews.empty()) {
2603                                                         
2604                                                         got_some = true;
2605
2606                                                         rtv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2607                                                         
2608                                                         if (!in_command) {
2609                                                                 begin_reversible_command (_("separate"));
2610                                                                 in_command = true;
2611                                                         }
2612                                                         
2613                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2614                                                         
2615                                                 } 
2616                                         }
2617
2618                                         if (!got_some) {
2619                                                 delete before;
2620                                         }
2621                                 }
2622                         }
2623                 }
2624         }
2625
2626         if (in_command) {
2627                 selection->set (new_selection);
2628                 set_mouse_mode (MouseObject);
2629
2630                 commit_reversible_command ();
2631         }
2632 }
2633
2634 void
2635 Editor::separate_region_from_selection ()
2636 {
2637         /* preferentially use *all* ranges in the time selection if we're in range mode
2638            to allow discontiguous operation, since get_edit_op_range() currently
2639            returns a single range.
2640         */
2641         if (mouse_mode == MouseRange && !selection->time.empty()) {
2642
2643                 separate_regions_between (selection->time);
2644
2645         } else {
2646
2647                 nframes64_t start;
2648                 nframes64_t end;
2649                 
2650                 if (get_edit_op_range (start, end)) {
2651                         
2652                         AudioRange ar (start, end, 1);
2653                         TimeSelection ts;
2654                         ts.push_back (ar);
2655
2656                         /* force track selection */
2657
2658                         ensure_entered_region_selected ();
2659                         
2660                         separate_regions_between (ts);
2661                 }
2662         }
2663 }
2664
2665 void
2666 Editor::separate_regions_using_location (Location& loc)
2667 {
2668         if (loc.is_mark()) {
2669                 return;
2670         }
2671
2672         AudioRange ar (loc.start(), loc.end(), 1);
2673         TimeSelection ts;
2674
2675         ts.push_back (ar);
2676
2677         separate_regions_between (ts);
2678 }
2679
2680 void
2681 Editor::crop_region_to_selection ()
2682 {
2683         ensure_entered_region_selected (true);
2684
2685         if (!selection->time.empty()) {
2686
2687                 crop_region_to (selection->time.start(), selection->time.end_frame());
2688
2689         } else {
2690
2691                 nframes64_t start;
2692                 nframes64_t end;
2693
2694                 if (get_edit_op_range (start, end)) {
2695                         crop_region_to (start, end);
2696                 }
2697         }
2698                 
2699 }               
2700
2701 void
2702 Editor::crop_region_to (nframes_t start, nframes_t end)
2703 {
2704         vector<boost::shared_ptr<Playlist> > playlists;
2705         boost::shared_ptr<Playlist> playlist;
2706         TrackSelection* ts;
2707
2708         if (selection->tracks.empty()) {
2709                 ts = &track_views;
2710         } else {
2711                 sort_track_selection ();
2712                 ts = &selection->tracks;
2713         }
2714         
2715         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2716                 
2717                 RouteTimeAxisView* rtv;
2718                 
2719                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2720
2721                         boost::shared_ptr<Track> t = rtv->track();
2722
2723                         if (t != 0 && ! t->diskstream()->destructive()) {
2724                                 
2725                                 if ((playlist = rtv->playlist()) != 0) {
2726                                         playlists.push_back (playlist);
2727                                 }
2728                         }
2729                 }
2730         }
2731
2732         if (playlists.empty()) {
2733                 return;
2734         }
2735                 
2736         nframes_t the_start;
2737         nframes_t the_end;
2738         nframes_t cnt;
2739         
2740         begin_reversible_command (_("trim to selection"));
2741         
2742         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2743                 
2744                 boost::shared_ptr<Region> region;
2745         
2746                 the_start = start;
2747         
2748                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2749                         continue;
2750                 }
2751                 
2752                 /* now adjust lengths to that we do the right thing
2753                    if the selection extends beyond the region
2754                 */
2755                 
2756                 the_start = max (the_start, region->position());
2757                 if (max_frames - the_start < region->length()) {
2758                         the_end = the_start + region->length() - 1;
2759                 } else {
2760                         the_end = max_frames;
2761                 }
2762                 the_end = min (end, the_end);
2763                 cnt = the_end - the_start + 1;
2764                 
2765                 XMLNode &before = (*i)->get_state();
2766                 region->trim_to (the_start, cnt, this);
2767                 XMLNode &after = (*i)->get_state();
2768                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2769         }
2770         
2771         commit_reversible_command ();
2772 }               
2773
2774 void
2775 Editor::region_fill_track ()
2776 {
2777         nframes_t end;
2778
2779         if (!session || selection->regions.empty()) {
2780                 return;
2781         }
2782
2783         end = session->current_end_frame ();
2784
2785         begin_reversible_command (_("region fill"));
2786
2787         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2788
2789                 boost::shared_ptr<Region> region ((*i)->region());
2790                 
2791                 // FIXME
2792                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2793                 assert(ar);
2794
2795                 boost::shared_ptr<Playlist> pl = region->playlist();
2796
2797                 if (end <= region->last_frame()) {
2798                         return;
2799                 }
2800
2801                 double times = (double) (end - region->last_frame()) / (double) region->length();
2802
2803                 if (times == 0) {
2804                         return;
2805                 }
2806
2807                 XMLNode &before = pl->get_state();
2808                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2809                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2810         }
2811
2812         commit_reversible_command ();
2813 }
2814
2815 void
2816 Editor::region_fill_selection ()
2817 {
2818         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2819                 return;
2820         }
2821
2822         if (selection->time.empty()) {
2823                 return;
2824         }
2825
2826
2827         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2828
2829         if (selected->count_selected_rows() != 1) {
2830                 return;
2831         }
2832
2833         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2834         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2835
2836         nframes_t start = selection->time[clicked_selection].start;
2837         nframes_t end = selection->time[clicked_selection].end;
2838
2839         boost::shared_ptr<Playlist> playlist; 
2840
2841         if (selection->tracks.empty()) {
2842                 return;
2843         }
2844
2845         nframes_t selection_length = end - start;
2846         float times = (float)selection_length / region->length();
2847         
2848         begin_reversible_command (_("fill selection"));
2849         
2850         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2851
2852                 if ((playlist = (*i)->playlist()) == 0) {
2853                         continue;
2854                 }               
2855                 
2856                 XMLNode &before = playlist->get_state();
2857                 playlist->add_region (RegionFactory::create (region), start, times);
2858                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2859         }
2860         
2861         commit_reversible_command ();                   
2862 }
2863
2864 void
2865 Editor::set_region_sync_from_edit_point ()
2866 {
2867         nframes64_t where = get_preferred_edit_position ();
2868         ensure_entered_region_selected (true);
2869         set_sync_point (where, selection->regions);
2870 }
2871
2872 void
2873 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
2874 {
2875         bool in_command = false;
2876
2877         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2878                 
2879                 if (!(*r)->region()->covers (where)) {
2880                         continue;
2881                 }
2882
2883                 boost::shared_ptr<Region> region ((*r)->region());
2884
2885                 if (!in_command) {
2886                         begin_reversible_command (_("set sync point"));
2887                         in_command = true;
2888                 }
2889
2890                 XMLNode &before = region->playlist()->get_state();
2891                 region->set_sync_position (get_preferred_edit_position());
2892                 XMLNode &after = region->playlist()->get_state();
2893                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2894         }
2895
2896         if (in_command) {
2897                 commit_reversible_command ();
2898         }
2899 }
2900
2901 /** Remove the sync positions of the selection */
2902 void
2903 Editor::remove_region_sync ()
2904 {
2905         begin_reversible_command (_("remove sync"));
2906
2907         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2908                 boost::shared_ptr<Region> r = (*i)->region();
2909                 XMLNode &before = r->playlist()->get_state();
2910                 r->clear_sync_position ();
2911                 XMLNode &after = r->playlist()->get_state();
2912                 session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
2913         }
2914
2915         commit_reversible_command ();
2916 }
2917
2918 void
2919 Editor::naturalize ()
2920 {
2921         if (selection->regions.empty()) {
2922                 return;
2923         }
2924         begin_reversible_command (_("naturalize"));
2925         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2926                 XMLNode &before = (*i)->region()->get_state();
2927                 (*i)->region()->move_to_natural_position (this);
2928                 XMLNode &after = (*i)->region()->get_state();
2929                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2930         }
2931         commit_reversible_command ();
2932 }
2933
2934 void
2935 Editor::align (RegionPoint what)
2936 {
2937         ensure_entered_region_selected ();
2938
2939         nframes64_t where = get_preferred_edit_position();
2940
2941         if (!selection->regions.empty()) {
2942                 align_selection (what, where, selection->regions);
2943         } else {
2944
2945                 RegionSelection rs;
2946                 rs = get_regions_at (where, selection->tracks);
2947                 align_selection (what, where, rs);
2948         }
2949 }
2950
2951 void
2952 Editor::align_relative (RegionPoint what)
2953 {
2954         nframes64_t where = get_preferred_edit_position();
2955
2956         if (!selection->regions.empty()) {
2957                 align_selection_relative (what, where, selection->regions);
2958         } else {
2959
2960                 RegionSelection rs;
2961                 rs = get_regions_at (where, selection->tracks);
2962                 align_selection_relative (what, where, rs);
2963         }
2964 }
2965
2966 struct RegionSortByTime {
2967     bool operator() (const RegionView* a, const RegionView* b) {
2968             return a->region()->position() < b->region()->position();
2969     }
2970 };
2971
2972 void
2973 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
2974 {
2975         if (rs.empty()) {
2976                 return;
2977         }
2978
2979         nframes_t distance;
2980         nframes_t pos = 0;
2981         int dir;
2982
2983         list<RegionView*> sorted;
2984         rs.by_position (sorted);
2985         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2986
2987         switch (point) {
2988         case Start:
2989                 pos = r->first_frame ();
2990                 break;
2991
2992         case End:
2993                 pos = r->last_frame();
2994                 break;
2995
2996         case SyncPoint:
2997                 pos = r->adjust_to_sync (r->first_frame());
2998                 break;  
2999         }
3000
3001         if (pos > position) {
3002                 distance = pos - position;
3003                 dir = -1;
3004         } else {
3005                 distance = position - pos;
3006                 dir = 1;
3007         }
3008
3009         begin_reversible_command (_("align selection (relative)"));
3010
3011         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3012
3013                 boost::shared_ptr<Region> region ((*i)->region());
3014
3015                 XMLNode &before = region->playlist()->get_state();
3016                 
3017                 if (dir > 0) {
3018                         region->set_position (region->position() + distance, this);
3019                 } else {
3020                         region->set_position (region->position() - distance, this);
3021                 }
3022
3023                 XMLNode &after = region->playlist()->get_state();
3024                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3025
3026         }
3027
3028         commit_reversible_command ();
3029 }
3030
3031 void
3032 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
3033 {
3034         if (rs.empty()) {
3035                 return;
3036         }
3037
3038         begin_reversible_command (_("align selection"));
3039
3040         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3041                 align_region_internal ((*i)->region(), point, position);
3042         }
3043
3044         commit_reversible_command ();
3045 }
3046
3047 void
3048 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3049 {
3050         begin_reversible_command (_("align region"));
3051         align_region_internal (region, point, position);
3052         commit_reversible_command ();
3053 }
3054
3055 void
3056 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3057 {
3058         XMLNode &before = region->playlist()->get_state();
3059
3060         switch (point) {
3061         case SyncPoint:
3062                 region->set_position (region->adjust_to_sync (position), this);
3063                 break;
3064
3065         case End:
3066                 if (position > region->length()) {
3067                         region->set_position (position - region->length(), this);
3068                 }
3069                 break;
3070
3071         case Start:
3072                 region->set_position (position, this);
3073                 break;
3074         }
3075
3076         XMLNode &after = region->playlist()->get_state();
3077         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3078 }       
3079
3080 /** Trim the end of the selected regions to the position of the edit cursor */
3081 void
3082 Editor::trim_region_to_loop ()
3083 {
3084         Location* loc = session->locations()->auto_loop_location();
3085         if (!loc) {
3086                 return;
3087         }
3088         trim_region_to_location (*loc, _("trim to loop"));
3089 }
3090
3091 void
3092 Editor::trim_region_to_punch ()
3093 {
3094         Location* loc = session->locations()->auto_punch_location();
3095         if (!loc) {
3096                 return;
3097         }
3098         trim_region_to_location (*loc, _("trim to punch"));
3099 }
3100 void
3101 Editor::trim_region_to_location (const Location& loc, const char* str)
3102 {
3103         ensure_entered_region_selected ();
3104
3105         RegionSelection& rs (get_regions_for_action ());
3106
3107         begin_reversible_command (str);
3108
3109         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3110                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3111
3112                 if (!arv) {
3113                         continue;
3114                 }
3115
3116                 /* require region to span proposed trim */
3117
3118                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3119                 case OverlapInternal:
3120                         break;
3121                 default:
3122                         continue;
3123                 }
3124                                 
3125                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3126
3127                 if (!atav) {
3128                         return;
3129                 }
3130
3131                 float speed = 1.0;
3132                 nframes_t start;
3133                 nframes_t end;
3134
3135                 if (atav->get_diskstream() != 0) {
3136                         speed = atav->get_diskstream()->speed();
3137                 }
3138                 
3139                 start = session_frame_to_track_frame (loc.start(), speed);
3140                 end = session_frame_to_track_frame (loc.end(), speed);
3141
3142                 XMLNode &before = arv->region()->playlist()->get_state();
3143                 arv->region()->trim_to (start, (end - start), this);
3144                 XMLNode &after = arv->region()->playlist()->get_state();
3145                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3146         }
3147
3148         commit_reversible_command ();
3149 }
3150
3151 void
3152 Editor::trim_region_to_edit_point ()
3153 {
3154         RegionSelection& rs (get_regions_for_action ());
3155         nframes64_t where = get_preferred_edit_position();
3156
3157         begin_reversible_command (_("trim region start to edit point"));
3158
3159         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3160                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3161
3162                 if (!arv) {
3163                         continue;
3164                 }
3165
3166                 /* require region to cover trim */
3167
3168                 if (!arv->region()->covers (where)) {
3169                         continue;
3170                 }
3171
3172                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3173
3174                 if (!atav) {
3175                         return;
3176                 }
3177
3178                 float speed = 1.0;
3179
3180                 if (atav->get_diskstream() != 0) {
3181                         speed = atav->get_diskstream()->speed();
3182                 }
3183
3184                 XMLNode &before = arv->region()->playlist()->get_state();
3185                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3186                 XMLNode &after = arv->region()->playlist()->get_state();
3187                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3188         }
3189                 
3190         commit_reversible_command ();
3191 }
3192
3193 void
3194 Editor::trim_region_from_edit_point ()
3195 {
3196         RegionSelection& rs (get_regions_for_action ());
3197         nframes64_t where = get_preferred_edit_position();
3198
3199         begin_reversible_command (_("trim region end to edit point"));
3200
3201         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3202                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3203
3204                 if (!arv) {
3205                         continue;
3206                 }
3207
3208                 /* require region to cover trim */
3209
3210                 if (!arv->region()->covers (where)) {
3211                         continue;
3212                 }
3213
3214                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3215
3216                 if (!atav) {
3217                         return;
3218                 }
3219
3220                 float speed = 1.0;
3221
3222                 if (atav->get_diskstream() != 0) {
3223                         speed = atav->get_diskstream()->speed();
3224                 }
3225
3226                 XMLNode &before = arv->region()->playlist()->get_state();
3227                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3228                 XMLNode &after = arv->region()->playlist()->get_state();
3229                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3230         }
3231                 
3232         commit_reversible_command ();
3233 }
3234
3235 /** Unfreeze selected routes */
3236 void
3237 Editor::unfreeze_routes ()
3238 {
3239         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3240                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*i);
3241                 if (atv && atv->is_audio_track()) {
3242                         atv->audio_track()->unfreeze ();
3243                 }
3244         }
3245 }
3246
3247 void*
3248 Editor::_freeze_thread (void* arg)
3249 {
3250         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
3251         return static_cast<Editor*>(arg)->freeze_thread ();
3252 }
3253
3254 void*
3255 Editor::freeze_thread ()
3256 {
3257         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3258                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*i);
3259                 if (atv && atv->is_audio_track()) {
3260                         atv->audio_track()->freeze (*current_interthread_info);
3261                 }
3262         }
3263
3264         current_interthread_info->done = true;
3265         
3266         return 0;
3267 }
3268
3269 gint
3270 Editor::freeze_progress_timeout (void *arg)
3271 {
3272         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
3273         return !(current_interthread_info->done || current_interthread_info->cancel);
3274 }
3275
3276 /** Freeze selected routes */
3277 void
3278 Editor::freeze_routes ()
3279 {
3280         InterThreadInfo itt;
3281
3282         if (interthread_progress_window == 0) {
3283                 build_interthread_progress_window ();
3284         }
3285
3286         WindowTitle title(Glib::get_application_name());
3287         title += _("Freeze");
3288         interthread_progress_window->set_title (title.get_string());
3289         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3290         interthread_progress_window->show_all ();
3291         interthread_progress_bar.set_fraction (0.0f);
3292         interthread_progress_label.set_text ("");
3293         interthread_cancel_label.set_text (_("Cancel Freeze"));
3294         current_interthread_info = &itt;
3295
3296         interthread_progress_connection = 
3297           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3298
3299         itt.done = false;
3300         itt.cancel = false;
3301         itt.progress = 0.0f;
3302         
3303         pthread_attr_t attr;
3304         pthread_attr_init(&attr);
3305         pthread_attr_setstacksize(&attr, 500000);
3306
3307         pthread_create (&itt.thread, &attr, _freeze_thread, this);
3308
3309         pthread_attr_destroy(&attr);
3310
3311         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3312
3313         while (!itt.done && !itt.cancel) {
3314                 gtk_main_iteration ();
3315         }
3316
3317         interthread_progress_connection.disconnect ();
3318         interthread_progress_window->hide_all ();
3319         current_interthread_info = 0;
3320         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3321 }
3322
3323 void
3324 Editor::bounce_range_selection ()
3325 {
3326         if (selection->time.empty()) {
3327                 return;
3328         }
3329
3330         TrackSelection views = selection->tracks;
3331
3332         nframes_t start = selection->time[clicked_selection].start;
3333         nframes_t end = selection->time[clicked_selection].end;
3334         nframes_t cnt = end - start + 1;
3335
3336         begin_reversible_command (_("bounce range"));
3337
3338         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3339
3340                 RouteTimeAxisView* rtv;
3341
3342                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3343                         continue;
3344                 }
3345                 
3346                 boost::shared_ptr<Playlist> playlist;
3347                 
3348                 if ((playlist = rtv->playlist()) == 0) {
3349                         return;
3350                 }
3351
3352                 InterThreadInfo itt;
3353                 
3354                 itt.done = false;
3355                 itt.cancel = false;
3356                 itt.progress = false;
3357
3358                 XMLNode &before = playlist->get_state();
3359                 rtv->track()->bounce_range (start, cnt, itt);
3360                 XMLNode &after = playlist->get_state();
3361                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3362         }
3363         
3364         commit_reversible_command ();
3365 }
3366
3367 /** Cut selected regions, automation points or a time range */
3368 void
3369 Editor::cut ()
3370 {
3371         cut_copy (Cut);
3372 }
3373
3374 /** Copy selected regions, automation points or a time range */
3375 void
3376 Editor::copy ()
3377 {
3378         cut_copy (Copy);
3379 }
3380
3381
3382 /** @return true if a Cut, Copy or Clear is possible */
3383 bool
3384 Editor::can_cut_copy () const
3385 {
3386         switch (current_mouse_mode()) {
3387                 
3388         case MouseObject:
3389                 if (!selection->regions.empty() || !selection->points.empty()) {
3390                         return true;
3391                 }
3392                 break;
3393                 
3394         case MouseRange:
3395                 if (!selection->time.empty()) {
3396                         return true;
3397                 }
3398                 break;
3399                 
3400         default:
3401                 break;
3402         }
3403
3404         return false;
3405 }
3406
3407
3408 /** Cut, copy or clear selected regions, automation points or a time range.
3409  * @param op Operation (Cut, Copy or Clear)
3410  */
3411 void 
3412 Editor::cut_copy (CutCopyOp op)
3413 {
3414         /* only cancel selection if cut/copy is successful.*/
3415
3416         string opname;
3417
3418         switch (op) {
3419         case Cut:
3420                 opname = _("cut");
3421                 break;
3422         case Copy:
3423                 opname = _("copy");
3424                 break;
3425         case Clear:
3426                 opname = _("clear");
3427                 break;
3428         }
3429         
3430         cut_buffer->clear ();
3431
3432         if (entered_marker) {
3433
3434                 /* cut/delete op while pointing at a marker */
3435
3436                 bool ignored;
3437                 Location* loc = find_location_from_marker (entered_marker, ignored);
3438
3439                 if (session && loc) {
3440                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3441                 }
3442
3443                 return;
3444         }
3445
3446         switch (current_mouse_mode()) {
3447         case MouseObject: 
3448                 cerr << "cutting in object mode\n";
3449                 if (!selection->regions.empty() || !selection->points.empty()) {
3450
3451                         begin_reversible_command (opname + _(" objects"));
3452
3453                         if (!selection->regions.empty()) {
3454                                 cerr << "have regions to cut" << endl;
3455                                 cut_copy_regions (op);
3456                                 
3457                                 if (op == Cut) {
3458                                         selection->clear_regions ();
3459                                 }
3460                         }
3461
3462                         if (!selection->points.empty()) {
3463                                 cut_copy_points (op);
3464
3465                                 if (op == Cut) {
3466                                         selection->clear_points ();
3467                                 }
3468                         }
3469
3470                         commit_reversible_command ();   
3471                         break; // terminate case statement here
3472                 } 
3473                 cerr << "nope, now cutting time range" << endl;
3474                 if (!selection->time.empty()) {
3475                         /* don't cause suprises */
3476                         break;
3477                 }
3478                 // fall thru if there was nothing selected
3479                 
3480         case MouseRange:
3481                 if (selection->time.empty()) {
3482                         nframes64_t start, end;
3483                         cerr << "no time selection, get edit op range" << endl;
3484                         if (!get_edit_op_range (start, end)) {
3485                                 cerr << "no edit op range" << endl;
3486                                 return;
3487                         }
3488                         selection->set ((TimeAxisView*) 0, start, end);
3489                 }
3490                         
3491                 begin_reversible_command (opname + _(" range"));
3492                 cut_copy_ranges (op);
3493                 commit_reversible_command ();
3494                 
3495                 if (op == Cut) {
3496                         selection->clear_time ();
3497                 }
3498
3499                 break;
3500                 
3501         default:
3502                 break;
3503         }
3504 }
3505
3506 /** Cut, copy or clear selected automation points.
3507  * @param op Operation (Cut, Copy or Clear)
3508  */
3509 void
3510 Editor::cut_copy_points (CutCopyOp op)
3511 {
3512         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3513
3514                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3515
3516                 if (atv) {
3517                         atv->cut_copy_clear_objects (selection->points, op);
3518                 } 
3519         }
3520 }
3521
3522 struct PlaylistState {
3523     boost::shared_ptr<Playlist> playlist;
3524     XMLNode*  before;
3525 };
3526
3527 struct lt_playlist {
3528     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3529             return a.playlist < b.playlist;
3530     }
3531 };
3532         
3533 struct PlaylistMapping { 
3534     TimeAxisView* tv;
3535     boost::shared_ptr<Playlist> pl;
3536
3537     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3538 };
3539
3540
3541 /** Cut, copy or clear selected regions.
3542  * @param op Operation (Cut, Copy or Clear)
3543  */
3544 void
3545 Editor::cut_copy_regions (CutCopyOp op)
3546 {       
3547         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3548            a map when we want ordered access to both elements. i think.
3549         */
3550
3551         vector<PlaylistMapping> pmap;
3552
3553         nframes_t first_position = max_frames;
3554         
3555         set<PlaylistState, lt_playlist> freezelist;
3556         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3557         
3558         /* get ordering correct before we cut/copy */
3559         
3560         selection->regions.sort_by_position_and_track ();
3561
3562         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3563
3564                 first_position = min ((*x)->region()->position(), first_position);
3565
3566                 if (op == Cut || op == Clear) {
3567                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3568
3569                         if (pl) {
3570
3571                                 PlaylistState before;
3572                                 before.playlist = pl;
3573                                 before.before = &pl->get_state();
3574                                 
3575                                 insert_result = freezelist.insert (before);
3576                                 
3577                                 if (insert_result.second) {
3578                                         pl->freeze ();
3579                                 }
3580                         }
3581                 }
3582
3583                 TimeAxisView* tv = &(*x)->get_trackview();
3584                 vector<PlaylistMapping>::iterator z;
3585
3586                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3587                         if ((*z).tv == tv) {
3588                                 break;
3589                         }
3590                 }
3591                 
3592                 if (z == pmap.end()) {
3593                         pmap.push_back (PlaylistMapping (tv));
3594                 }
3595         }
3596
3597         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3598
3599                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3600                 
3601                 if (!pl) {
3602                         /* impossible, but this handles it for the future */
3603                         continue;
3604                 }
3605
3606                 TimeAxisView& tv = (*x)->get_trackview();
3607                 boost::shared_ptr<Playlist> npl;
3608                 RegionSelection::iterator tmp;
3609                 
3610                 tmp = x;
3611                 ++tmp;
3612
3613                 vector<PlaylistMapping>::iterator z;
3614                 
3615                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3616                         if ((*z).tv == &tv) {
3617                                 break;
3618                         }
3619                 }
3620                 
3621                 assert (z != pmap.end());
3622                 
3623                 if (!(*z).pl) {
3624                         npl = PlaylistFactory::create (pl->data_type(), *session, "cutlist", true);
3625                         npl->freeze();
3626                         (*z).pl = npl;
3627                 } else {
3628                         npl = (*z).pl;
3629                 }
3630                 
3631                 boost::shared_ptr<Region> r = (*x)->region();
3632                 boost::shared_ptr<Region> _xx;
3633
3634                 assert (r != 0);
3635
3636                 switch (op) {
3637                 case Cut:
3638                         _xx = RegionFactory::create (r);
3639                         npl->add_region (_xx, r->position() - first_position);
3640                         pl->remove_region (r);
3641                         break;
3642                         
3643                 case Copy:
3644                         /* copy region before adding, so we're not putting same object into two different playlists */
3645                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
3646                         break;
3647                         
3648                 case Clear:
3649                         pl->remove_region (r);
3650                         break;
3651                 }
3652
3653                 x = tmp;
3654         }
3655         
3656         list<boost::shared_ptr<Playlist> > foo;
3657         
3658         /* the pmap is in the same order as the tracks in which selected regions occured */
3659         
3660         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3661                 (*i).pl->thaw();
3662                 foo.push_back ((*i).pl);
3663         }
3664         
3665
3666         if (!foo.empty()) {
3667                 cut_buffer->set (foo);
3668         }
3669
3670         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3671                 (*pl).playlist->thaw ();
3672                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3673         }
3674 }
3675
3676 void
3677 Editor::cut_copy_ranges (CutCopyOp op)
3678 {
3679         TrackSelection* ts;
3680
3681         if (selection->tracks.empty()) {
3682                 ts = &track_views;
3683         } else {
3684                 ts = &selection->tracks;
3685         }
3686
3687         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3688                 (*i)->cut_copy_clear (*selection, op);
3689         }
3690 }
3691
3692 void
3693 Editor::paste (float times)
3694 {
3695         paste_internal (get_preferred_edit_position(), times);
3696 }
3697
3698 void
3699 Editor::mouse_paste ()
3700 {
3701         nframes64_t where;
3702         bool ignored;
3703
3704         if (!mouse_frame (where, ignored)) {
3705                 return;
3706         }
3707
3708         snap_to (where);
3709         paste_internal (where, 1);
3710 }
3711
3712 void
3713 Editor::paste_internal (nframes_t position, float times)
3714 {
3715         bool commit = false;
3716
3717         if (cut_buffer->empty()) {
3718                 return;
3719         }
3720
3721         if (position == max_frames) {
3722                 position = get_preferred_edit_position();
3723         }
3724
3725         begin_reversible_command (_("paste"));
3726
3727         TrackSelection ts;
3728         TrackSelection::iterator i;
3729         size_t nth;
3730
3731         /* get everything in the correct order */
3732
3733
3734         if (!selection->tracks.empty()) {
3735                 sort_track_selection ();
3736                 ts = selection->tracks;
3737         } else if (entered_track) {
3738                 ts.push_back (entered_track);
3739         }
3740
3741         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3742
3743                 /* undo/redo is handled by individual tracks */
3744
3745                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3746                         commit = true;
3747                 }
3748         }
3749         
3750         if (commit) {
3751                 commit_reversible_command ();
3752         }
3753 }
3754
3755 void
3756 Editor::paste_named_selection (float times)
3757 {
3758         TrackSelection::iterator t;
3759
3760         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3761
3762         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3763                 return;
3764         }
3765
3766         TreeModel::iterator i = selected->get_selected();
3767         NamedSelection* ns = (*i)[named_selection_columns.selection];
3768
3769         list<boost::shared_ptr<Playlist> >::iterator chunk;
3770         list<boost::shared_ptr<Playlist> >::iterator tmp;
3771
3772         chunk = ns->playlists.begin();
3773                 
3774         begin_reversible_command (_("paste chunk"));
3775         
3776         sort_track_selection ();
3777
3778         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3779                 
3780                 RouteTimeAxisView* rtv;
3781                 boost::shared_ptr<Playlist> pl;
3782                 boost::shared_ptr<AudioPlaylist> apl;
3783
3784                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*t)) == 0) {
3785                         continue;
3786                 }
3787
3788                 if ((pl = rtv->playlist()) == 0) {
3789                         continue;
3790                 }
3791                 
3792                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3793                         continue;
3794                 }
3795
3796                 tmp = chunk;
3797                 ++tmp;
3798
3799                 XMLNode &before = apl->get_state();
3800                 apl->paste (*chunk, get_preferred_edit_position(), times);
3801                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3802
3803                 if (tmp != ns->playlists.end()) {
3804                         chunk = tmp;
3805                 }
3806         }
3807
3808         commit_reversible_command();
3809 }
3810
3811 void
3812 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3813 {
3814         boost::shared_ptr<Playlist> playlist; 
3815         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
3816         RegionSelection foo;
3817
3818         begin_reversible_command (_("duplicate region"));
3819
3820         selection->clear_regions ();
3821
3822         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3823
3824                 boost::shared_ptr<Region> r ((*i)->region());
3825
3826                 TimeAxisView& tv = (*i)->get_time_axis_view();
3827                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3828                 latest_regionviews.clear ();
3829                 sigc::connection c = rtv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3830                 
3831                 playlist = (*i)->region()->playlist();
3832                 XMLNode &before = playlist->get_state();
3833                 playlist->duplicate (r, r->last_frame(), times);
3834                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3835
3836                 c.disconnect ();
3837                 
3838                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3839         }
3840
3841         commit_reversible_command ();
3842
3843         if (!foo.empty()) {
3844                 selection->set (foo);
3845         }
3846 }
3847
3848 void
3849 Editor::duplicate_selection (float times)
3850 {
3851         if (selection->time.empty() || selection->tracks.empty()) {
3852                 return;
3853         }
3854
3855         boost::shared_ptr<Playlist> playlist; 
3856         vector<boost::shared_ptr<AudioRegion> > new_regions;
3857         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3858                 
3859         create_region_from_selection (new_regions);
3860
3861         if (new_regions.empty()) {
3862                 return;
3863         }
3864         
3865         begin_reversible_command (_("duplicate selection"));
3866
3867         ri = new_regions.begin();
3868
3869         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3870                 if ((playlist = (*i)->playlist()) == 0) {
3871                         continue;
3872                 }
3873                 XMLNode &before = playlist->get_state();
3874                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3875                 XMLNode &after = playlist->get_state();
3876                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3877
3878                 ++ri;
3879                 if (ri == new_regions.end()) {
3880                         --ri;
3881                 }
3882         }
3883
3884         commit_reversible_command ();
3885 }
3886
3887 void
3888 Editor::reset_point_selection ()
3889 {
3890         /* reset all selected points to the relevant default value */
3891
3892         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3893                 
3894                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3895                 
3896                 if (atv) {
3897                         atv->reset_objects (selection->points);
3898                 } 
3899         }
3900 }
3901
3902 void
3903 Editor::center_playhead ()
3904 {
3905         float page = canvas_width * frames_per_unit;
3906         center_screen_internal (playhead_cursor->current_frame, page);
3907 }
3908
3909 void
3910 Editor::center_edit_point ()
3911 {
3912         float page = canvas_width * frames_per_unit;
3913         center_screen_internal (get_preferred_edit_position(), page);
3914 }
3915
3916 void
3917 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3918 {
3919         begin_reversible_command (_("clear playlist"));
3920         XMLNode &before = playlist->get_state();
3921         playlist->clear ();
3922         XMLNode &after = playlist->get_state();
3923         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3924         commit_reversible_command ();
3925 }
3926
3927 void
3928 Editor::nudge_selected_tracks (bool use_edit, bool forwards)
3929 {
3930         boost::shared_ptr<Playlist> playlist; 
3931         nframes_t distance;
3932         nframes_t next_distance;
3933         nframes_t start;
3934
3935         if (use_edit) {
3936                 start = get_preferred_edit_position();
3937         } else {
3938                 start = 0;
3939         }
3940
3941         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3942                 return;
3943         }
3944         
3945         if (selection->tracks.empty()) {
3946                 return;
3947         }
3948         
3949         begin_reversible_command (_("nudge track"));
3950         
3951         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3952
3953                 if ((playlist = (*i)->playlist()) == 0) {
3954                         continue;
3955                 }               
3956                 
3957                 XMLNode &before = playlist->get_state();
3958                 playlist->nudge_after (start, distance, forwards);
3959                 XMLNode &after = playlist->get_state();
3960                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3961         }
3962         
3963         commit_reversible_command ();                   
3964 }
3965
3966 void
3967 Editor::remove_last_capture ()
3968 {
3969         vector<string> choices;
3970         string prompt;
3971         
3972         if (!session) {
3973                 return;
3974         }
3975
3976         if (Config->get_verify_remove_last_capture()) {
3977                 prompt  = _("Do you really want to destroy the last capture?"
3978                             "\n(This is destructive and cannot be undone)");
3979
3980                 choices.push_back (_("No, do nothing."));
3981                 choices.push_back (_("Yes, destroy it."));
3982                 
3983                 Gtkmm2ext::Choice prompter (prompt, choices);
3984                 
3985                 if (prompter.run () == 1) {
3986                         session->remove_last_capture ();
3987                 }
3988
3989         } else {
3990                 session->remove_last_capture();
3991         }
3992 }
3993
3994 void
3995 Editor::normalize_regions ()
3996 {
3997         if (!session) {
3998                 return;
3999         }
4000
4001         if (selection->regions.empty()) {
4002                 return;
4003         }
4004
4005         begin_reversible_command (_("normalize"));
4006
4007         track_canvas.get_window()->set_cursor (*wait_cursor);
4008         gdk_flush ();
4009
4010         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4011                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4012                 if (!arv)
4013                         continue;
4014                 XMLNode &before = arv->region()->get_state();
4015                 arv->audio_region()->normalize_to (0.0f);
4016                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4017         }
4018
4019         commit_reversible_command ();
4020         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4021 }
4022
4023
4024 void
4025 Editor::denormalize_regions ()
4026 {
4027         if (!session) {
4028                 return;
4029         }
4030
4031         if (selection->regions.empty()) {
4032                 return;
4033         }
4034
4035         begin_reversible_command ("denormalize");
4036
4037         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4038                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4039                 if (!arv)
4040                         continue;
4041                 XMLNode &before = arv->region()->get_state();
4042                 arv->audio_region()->set_scale_amplitude (1.0f);
4043                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4044         }
4045
4046         commit_reversible_command ();
4047 }
4048
4049
4050 void
4051 Editor::reverse_regions ()
4052 {
4053         if (!session) {
4054                 return;
4055         }
4056
4057         Reverse rev (*session);
4058         apply_filter (rev, _("reverse regions"));
4059 }
4060
4061
4062 void
4063 Editor::quantize_regions ()
4064 {
4065         if (!session) {
4066                 return;
4067         }
4068
4069         // FIXME: varying meter?
4070         Quantize quant (*session, snap_length_beats(0));
4071         apply_filter (quant, _("quantize regions"));
4072 }
4073
4074 void
4075 Editor::apply_filter (Filter& filter, string command)
4076 {
4077         if (selection->regions.empty()) {
4078                 return;
4079         }
4080
4081         begin_reversible_command (command);
4082
4083         track_canvas.get_window()->set_cursor (*wait_cursor);
4084         gdk_flush ();
4085
4086         /* this is ugly. */
4087         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
4088                 RegionSelection::iterator tmp = r;
4089                 ++tmp;
4090
4091                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4092                 if (mrv) {
4093                         if (mrv->midi_region()->apply(filter) == 0) {
4094                                 mrv->redisplay_model();
4095                         }
4096                 }
4097
4098                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4099                 if (arv) {
4100                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4101
4102                         if (arv->audio_region()->apply (filter) == 0) {
4103
4104                                 XMLNode &before = playlist->get_state();
4105                                 playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4106                                 XMLNode &after = playlist->get_state();
4107                                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4108                         } else {
4109                                 goto out;
4110                         }
4111                 }
4112
4113                 r = tmp;
4114         }
4115
4116         commit_reversible_command ();
4117         selection->regions.clear ();
4118
4119   out:
4120         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4121 }
4122
4123 void
4124 Editor::region_selection_op (void (Region::*pmf)(void))
4125 {
4126         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4127                 Region* region = (*i)->region().get();
4128                 (region->*pmf)();
4129         }
4130 }
4131
4132
4133 void
4134 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4135 {
4136         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4137                 Region* region = (*i)->region().get();
4138                 (region->*pmf)(arg);
4139         }
4140 }
4141
4142 void
4143 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4144 {
4145         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4146                 Region* region = (*i)->region().get();
4147                 (region->*pmf)(yn);
4148         }
4149 }
4150
4151 void
4152 Editor::external_edit_region ()
4153 {
4154         /* more to come */
4155 }
4156
4157 void
4158 Editor::brush (nframes_t pos)
4159 {
4160         RegionSelection sel;
4161         snap_to (pos);
4162
4163         if (selection->regions.empty()) {
4164                 /* XXX get selection from region list */
4165         } else { 
4166                 sel = selection->regions;
4167         }
4168
4169         if (sel.empty()) {
4170                 return;
4171         }
4172
4173         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4174                 mouse_brush_insert_region ((*i), pos);
4175         }
4176 }
4177
4178 void
4179 Editor::reset_region_gain_envelopes ()
4180 {
4181         if (!session || selection->regions.empty()) {
4182                 return;
4183         }
4184
4185         session->begin_reversible_command (_("reset region gain"));
4186
4187         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4188                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4189                 if (arv) {
4190                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4191                         XMLNode& before (alist->get_state());
4192
4193                         arv->audio_region()->set_default_envelope ();
4194                         session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4195                 }
4196         }
4197
4198         session->commit_reversible_command ();
4199 }
4200
4201 void
4202 Editor::toggle_gain_envelope_visibility ()
4203 {
4204         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4205                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4206                 if (arv) {
4207                         bool x = region_envelope_visible_item->get_active();
4208                         if (x != arv->envelope_visible()) {
4209                                 arv->set_envelope_visible (x);
4210                         }
4211                 }
4212         }
4213 }
4214
4215 void
4216 Editor::toggle_gain_envelope_active ()
4217 {
4218         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4219                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4220                 if (arv) {
4221                         bool x = region_envelope_active_item->get_active();
4222                         if (x != arv->audio_region()->envelope_active()) {
4223                                 arv->audio_region()->set_envelope_active (x);
4224                         }
4225                 }
4226         }
4227 }
4228
4229 /** Set the position-locked state of all selected regions to a particular value.
4230  * @param yn true to make locked, false to make unlocked.
4231  */
4232 void
4233 Editor::toggle_region_position_lock ()
4234 {
4235         bool x = region_lock_position_item->get_active();
4236
4237         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4238                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4239                 if (arv) {
4240                         if (x != arv->audio_region()->locked()) {
4241                                 arv->audio_region()->set_position_locked (x);
4242                         }
4243                 }
4244         }
4245 }
4246
4247 void
4248 Editor::toggle_region_lock ()
4249 {
4250         bool x = region_lock_item->get_active();
4251
4252         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4253                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4254                 if (arv) {
4255                         if (x != arv->audio_region()->locked()) {
4256                                 arv->audio_region()->set_locked (x);
4257                         }
4258                 }
4259         }
4260 }
4261
4262 void
4263 Editor::toggle_region_mute ()
4264 {
4265         bool x = region_mute_item->get_active();
4266
4267         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4268                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4269                 if (arv) {
4270                         if (x != arv->audio_region()->muted()) {
4271                                 arv->audio_region()->set_muted (x);
4272                         }
4273                 }
4274         }
4275 }
4276
4277 void
4278 Editor::toggle_region_opaque ()
4279 {
4280         bool x = region_opaque_item->get_active();
4281
4282         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4283                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4284                 if (arv) {
4285                         if (x != arv->audio_region()->opaque()) {
4286                                 arv->audio_region()->set_opaque (x);
4287                         }
4288                 }
4289         }
4290 }
4291
4292 void
4293 Editor::set_fade_length (bool in)
4294 {
4295         ensure_entered_region_selected (true);
4296
4297         /* we need a region to measure the offset from the start */
4298
4299         RegionView* rv;
4300
4301         if (entered_regionview) {
4302                 rv = entered_regionview;
4303         } else if (!selection->regions.empty()) {
4304                 rv = selection->regions.front();
4305         } else {
4306                 return;
4307         }
4308
4309         nframes64_t pos = get_preferred_edit_position();
4310         nframes_t len;
4311         char* cmd;
4312
4313         if (in) {
4314                 if (pos <= rv->region()->position()) {
4315                         /* can't do it */
4316                         return;
4317                 }
4318                 len = pos - rv->region()->position();
4319                 cmd = _("set fade in length");
4320         } else {
4321                 if (pos >= rv->region()->last_frame()) {
4322                         /* can't do it */
4323                         return;
4324                 }
4325                 len = rv->region()->last_frame() - pos;
4326                 cmd = _("set fade out length");
4327         }
4328
4329         begin_reversible_command (cmd);
4330
4331         RegionSelection& rs (get_regions_for_action());
4332
4333         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4334                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4335
4336                 if (!tmp) {
4337                         return;
4338                 }
4339
4340                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4341                 XMLNode &before = alist->get_state();
4342
4343                 if (in) {
4344                         tmp->audio_region()->set_fade_in_length (len);
4345                 } else {
4346                         tmp->audio_region()->set_fade_out_length (len);
4347                 }
4348                 
4349                 XMLNode &after = alist->get_state();
4350                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4351         }
4352
4353         commit_reversible_command ();
4354 }
4355
4356
4357 void
4358 Editor::toggle_fade_active (bool in)
4359 {
4360         ensure_entered_region_selected (true);
4361
4362         if (selection->regions.empty()) {
4363                 return;
4364         }
4365
4366         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4367         bool have_switch = false;
4368         bool yn;
4369         bool in_command = false;
4370
4371         begin_reversible_command (cmd);
4372
4373         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4374                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4375                 
4376                 if (!tmp) {
4377                         return;
4378                 }
4379
4380                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4381
4382                 /* make the behaviour consistent across all regions */
4383                 
4384                 if (!have_switch) {
4385                         yn = region->fade_in_active();
4386                         have_switch = true;
4387                 }
4388
4389                 XMLNode &before = region->get_state();
4390                 region->set_fade_in_active (!yn);
4391                 XMLNode &after = region->get_state();
4392                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4393                 in_command = true;
4394         }
4395
4396         if (in_command) {
4397                 commit_reversible_command ();
4398         }
4399 }
4400
4401 void
4402 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4403 {
4404         begin_reversible_command (_("set fade in shape"));
4405
4406         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4407                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4408
4409                 if (!tmp) {
4410                         return;
4411                 }
4412
4413                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4414                 XMLNode &before = alist->get_state();
4415
4416                 tmp->audio_region()->set_fade_in_shape (shape);
4417                 
4418                 XMLNode &after = alist->get_state();
4419                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4420         }
4421
4422         commit_reversible_command ();
4423 }
4424
4425 void
4426 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4427 {
4428         begin_reversible_command (_("set fade out shape"));
4429
4430         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4431                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4432
4433                 if (!tmp) {
4434                         return;
4435                 }
4436
4437                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4438                 XMLNode &before = alist->get_state();
4439
4440                 tmp->audio_region()->set_fade_out_shape (shape);
4441                 
4442                 XMLNode &after = alist->get_state();
4443                 session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4444         }
4445
4446         commit_reversible_command ();
4447 }
4448
4449 void
4450 Editor::set_fade_in_active (bool yn)
4451 {
4452         begin_reversible_command (_("set fade in active"));
4453
4454         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4455                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4456
4457                 if (!tmp) {
4458                         return;
4459                 }
4460
4461
4462                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4463
4464                 XMLNode &before = ar->get_state();
4465
4466                 ar->set_fade_in_active (yn);
4467                 
4468                 XMLNode &after = ar->get_state();
4469                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4470         }
4471 }
4472
4473 void
4474 Editor::set_fade_out_active (bool yn)
4475 {
4476         begin_reversible_command (_("set fade out active"));
4477
4478         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4479                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4480
4481                 if (!tmp) {
4482                         return;
4483                 }
4484
4485                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4486
4487                 XMLNode &before = ar->get_state();
4488
4489                 ar->set_fade_out_active (yn);
4490                 
4491                 XMLNode &after = ar->get_state();
4492                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4493         }
4494 }
4495
4496
4497 /** Update crossfade visibility after its configuration has been changed */
4498 void
4499 Editor::update_xfade_visibility ()
4500 {
4501         _xfade_visibility = Config->get_xfades_visible ();
4502         
4503         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4504                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4505                 if (v) {
4506                         if (_xfade_visibility) {
4507                                 v->show_all_xfades ();
4508                         } else {
4509                                 v->hide_all_xfades ();
4510                         }
4511                 }
4512         }
4513 }
4514
4515 void
4516 Editor::set_edit_point ()
4517 {
4518         nframes64_t where;
4519         bool ignored;
4520
4521         if (!mouse_frame (where, ignored)) {
4522                 return;
4523         }
4524         
4525         snap_to (where);
4526
4527         if (selection->markers.empty()) {
4528                 
4529                 mouse_add_new_marker (where);
4530
4531         } else {
4532                 bool ignored;
4533
4534                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4535
4536                 if (loc) {
4537                         loc->move_to (where);
4538                 }
4539         }
4540 }
4541
4542 void
4543 Editor::set_playhead_cursor ()
4544 {
4545         if (entered_marker) {
4546                 session->request_locate (entered_marker->position(), session->transport_rolling());
4547         } else {
4548                 nframes64_t where;
4549                 bool ignored;
4550
4551                 if (!mouse_frame (where, ignored)) {
4552                         return;
4553                 }
4554                         
4555                 snap_to (where);
4556                 
4557                 if (session) {
4558                         session->request_locate (where, session->transport_rolling());
4559                 }
4560         }
4561 }
4562
4563 void
4564 Editor::split ()
4565 {
4566         ensure_entered_region_selected ();
4567         
4568         nframes64_t where = get_preferred_edit_position();
4569
4570         if (!selection->regions.empty()) {
4571                 
4572                 split_regions_at (where, selection->regions);
4573
4574         } else {
4575                 
4576                 RegionSelection rs;
4577                 rs = get_regions_at (where, selection->tracks);
4578                 split_regions_at (where, rs);
4579         }
4580 }
4581
4582 void
4583 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4584 {
4585         if (entered_track && mouse_mode == MouseObject) {
4586                 if (!selection->tracks.empty()) {
4587                         if (!selection->selected (entered_track)) {
4588                                 selection->add (entered_track);
4589                         }
4590                 } else {
4591                         /* there is no selection, but this operation requires/prefers selected objects */
4592
4593                         if (op_really_wants_one_track_if_none_are_selected) {
4594                                 selection->set (entered_track);
4595                         }
4596                 }
4597         }
4598 }
4599
4600 void
4601 Editor::ensure_entered_region_selected (bool op_really_wants_one_region_if_none_are_selected)
4602 {
4603         if (entered_regionview && mouse_mode == MouseObject) {
4604
4605                 /* heuristic:
4606
4607                    - if there is no existing selection, don't change it. the operation will thus apply to "all"
4608
4609                    - if there is an existing selection, but the entered regionview isn't in it, add it. this
4610                        avoids key-mouse ops on unselected regions from interfering with an existing selection,
4611                        but also means that the operation will apply to the pointed-at region.
4612                 */
4613
4614                 if (!selection->regions.empty()) {
4615                         if (find (selection->regions.begin(), selection->regions.end(), entered_regionview) != selection->regions.end()) {
4616                                 selection->add (entered_regionview);
4617                         }
4618                 } else {
4619                         /* there is no selection, but this operation requires/prefers selected objects */
4620
4621                         if (op_really_wants_one_region_if_none_are_selected) {
4622                                 selection->set (entered_regionview, false);
4623                         }
4624                 }
4625         }
4626 }
4627
4628 void
4629 Editor::trim_region_front ()
4630 {
4631         trim_region (true);
4632 }
4633
4634 void
4635 Editor::trim_region_back ()
4636 {
4637         trim_region (false);
4638 }
4639
4640 void
4641 Editor::trim_region (bool front)
4642 {
4643         nframes64_t where = get_preferred_edit_position();
4644         RegionSelection& rs = get_regions_for_action ();
4645
4646         if (rs.empty()) {
4647                 return;
4648         }
4649
4650         begin_reversible_command (front ? _("trim front") : _("trim back"));
4651
4652         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4653                 if (!(*i)->region()->locked()) {
4654                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4655                         XMLNode &before = pl->get_state();
4656                         if (front) {
4657                                 (*i)->region()->trim_front (where, this);       
4658                         } else {
4659                                 (*i)->region()->trim_end (where, this); 
4660                         }
4661                         XMLNode &after = pl->get_state();
4662                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4663                 }
4664         }
4665         commit_reversible_command ();
4666 }
4667
4668 struct EditorOrderRouteSorter {
4669     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4670             /* use of ">" forces the correct sort order */
4671             return a->order_key ("editor") < b->order_key ("editor");
4672     }
4673 };
4674
4675 void
4676 Editor::select_next_route()
4677 {
4678         if (selection->tracks.empty()) {
4679                 selection->set (track_views.front());
4680                 return;
4681         }
4682
4683         TimeAxisView* current = selection->tracks.front();
4684
4685         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4686                 if (*i == current) {
4687                         ++i;
4688                         if (i != track_views.end()) {
4689                                 selection->set (*i);
4690                         } else {
4691                                 selection->set (*(track_views.begin()));
4692                         }
4693                         break;
4694                 }
4695         }
4696 }
4697
4698 void
4699 Editor::select_prev_route()
4700 {
4701         if (selection->tracks.empty()) {
4702                 selection->set (track_views.front());
4703                 return;
4704         }
4705
4706         TimeAxisView* current = selection->tracks.front();
4707
4708         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4709                 if (*i == current) {
4710                         ++i;
4711                         if (i != track_views.rend()) {
4712                                 selection->set (*i);
4713                         } else {
4714                                 selection->set (*(track_views.rbegin()));
4715                         }
4716                         break;
4717                 }
4718         }
4719 }
4720
4721 void
4722 Editor::set_loop_from_selection (bool play)
4723 {
4724         if (session == 0 || selection->time.empty()) {
4725                 return;
4726         }
4727
4728         nframes_t start = selection->time[clicked_selection].start;
4729         nframes_t end = selection->time[clicked_selection].end;
4730         
4731         set_loop_range (start, end,  _("set loop range from selection"));
4732
4733         if (play) {
4734                 session->request_play_loop (true);
4735                 session->request_locate (start, true);
4736         }
4737 }
4738
4739 void
4740 Editor::set_loop_from_edit_range (bool play)
4741 {
4742         if (session == 0) {
4743                 return;
4744         }
4745
4746         nframes64_t start;
4747         nframes64_t end;
4748         
4749         if (!get_edit_op_range (start, end)) {
4750                 return;
4751         }
4752
4753         set_loop_range (start, end,  _("set loop range from edit range"));
4754
4755         if (play) {
4756                 session->request_play_loop (true);
4757                 session->request_locate (start, true);
4758         }
4759 }
4760
4761 void
4762 Editor::set_loop_from_region (bool play)
4763 {
4764         nframes64_t start = max_frames;
4765         nframes64_t end = 0;
4766
4767         ensure_entered_region_selected (true);
4768
4769         if (selection->regions.empty()) {
4770                 info << _("cannot set loop: no region selected") << endmsg;
4771                 return;
4772         }
4773
4774         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4775                 if ((*i)->region()->position() < start) {
4776                         start = (*i)->region()->position();
4777                 }
4778                 if ((*i)->region()->last_frame() + 1 > end) {
4779                         end = (*i)->region()->last_frame() + 1;
4780                 }
4781         }
4782
4783         set_loop_range (start, end, _("set loop range from region"));
4784
4785         if (play) {
4786                 session->request_play_loop (true);
4787                 session->request_locate (start, true);
4788         }
4789 }
4790
4791 void
4792 Editor::set_punch_from_selection ()
4793 {
4794         if (session == 0 || selection->time.empty()) {
4795                 return;
4796         }
4797
4798         nframes_t start = selection->time[clicked_selection].start;
4799         nframes_t end = selection->time[clicked_selection].end;
4800         
4801         set_punch_range (start, end,  _("set punch range from selection"));
4802 }
4803
4804 void
4805 Editor::set_punch_from_edit_range ()
4806 {
4807         if (session == 0) {
4808                 return;
4809         }
4810
4811         nframes64_t start;
4812         nframes64_t end;
4813         
4814         if (!get_edit_op_range (start, end)) {
4815                 return;
4816         }
4817
4818         set_punch_range (start, end,  _("set punch range from edit range"));
4819 }
4820
4821 void
4822 Editor::pitch_shift_regions ()
4823 {
4824         ensure_entered_region_selected (true);
4825         
4826         if (selection->regions.empty()) {
4827                 return;
4828         }
4829
4830         pitch_shift (selection->regions, 1.2);
4831 }
4832