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