first pass of dynamically resizable track heights
[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
1840 bool
1841 Editor::choose_new_marker_name(string &name) {
1842
1843         if (!Config->get_name_new_markers()) {
1844                 /* don't prompt user for a new name */
1845                 return true;
1846         }
1847
1848         ArdourPrompter dialog (true);
1849
1850         dialog.set_prompt (_("New Name:"));
1851
1852         WindowTitle title(Glib::get_application_name());
1853         title += _("Name New Location Marker");
1854
1855         dialog.set_title(title.get_string());
1856
1857         dialog.set_name ("MarkNameWindow");
1858         dialog.set_size_request (250, -1);
1859         dialog.set_position (Gtk::WIN_POS_MOUSE);
1860
1861         dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1862         dialog.set_initial_text (name);
1863
1864         dialog.show ();
1865
1866         switch (dialog.run ()) {
1867         case RESPONSE_ACCEPT:
1868                 break;
1869         default:
1870                 return false;
1871         }
1872         
1873         dialog.get_result(name);
1874         return true;
1875
1876 }
1877
1878
1879 void
1880 Editor::add_location_from_selection ()
1881 {
1882         string rangename;
1883
1884         if (selection->time.empty()) {
1885                 return;
1886         }
1887
1888         if (session == 0 || clicked_trackview == 0) {
1889                 return;
1890         }
1891
1892         nframes_t start = selection->time[clicked_selection].start;
1893         nframes_t end = selection->time[clicked_selection].end;
1894
1895         session->locations()->next_available_name(rangename,"selection");
1896         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1897
1898         session->begin_reversible_command (_("add marker"));
1899         XMLNode &before = session->locations()->get_state();
1900         session->locations()->add (location, true);
1901         XMLNode &after = session->locations()->get_state();
1902         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1903         session->commit_reversible_command ();
1904 }
1905
1906 void
1907 Editor::add_location_mark (nframes64_t where)
1908 {
1909         string markername;
1910
1911         select_new_marker = true;
1912
1913         session->locations()->next_available_name(markername,"mark");
1914         if (!choose_new_marker_name(markername)) {
1915                 return;
1916         }
1917         Location *location = new Location (where, where, markername, Location::IsMark);
1918         session->begin_reversible_command (_("add marker"));
1919         XMLNode &before = session->locations()->get_state();
1920         session->locations()->add (location, true);
1921         XMLNode &after = session->locations()->get_state();
1922         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1923         session->commit_reversible_command ();
1924 }
1925
1926 void
1927 Editor::add_location_from_playhead_cursor ()
1928 {
1929         add_location_mark (session->audible_frame());
1930 }
1931
1932 void
1933 Editor::add_location_from_audio_region ()
1934 {
1935         RegionSelection rs; 
1936
1937         get_regions_for_action (rs);
1938
1939         if (rs.empty()) {
1940                 return;
1941         }
1942
1943         RegionView* rv = *(rs.begin());
1944         boost::shared_ptr<Region> region = rv->region();
1945         
1946         Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1947         session->begin_reversible_command (_("add marker"));
1948         XMLNode &before = session->locations()->get_state();
1949         session->locations()->add (location, true);
1950         XMLNode &after = session->locations()->get_state();
1951         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1952         session->commit_reversible_command ();
1953 }
1954
1955 void
1956 Editor::amplitude_zoom_step (bool in)
1957 {
1958         gdouble zoom = 1.0;
1959
1960         if (in) {
1961                 zoom *= 2.0;
1962         } else {
1963                 if (zoom > 2.0) {
1964                         zoom /= 2.0;
1965                 } else {
1966                         zoom = 1.0;
1967                 }
1968         }
1969
1970 #ifdef FIX_FOR_CANVAS
1971         /* XXX DO SOMETHING */
1972 #endif
1973 }       
1974
1975
1976 /* DELETION */
1977
1978
1979 void
1980 Editor::delete_sample_forward ()
1981 {
1982 }
1983
1984 void
1985 Editor::delete_sample_backward ()
1986 {
1987 }
1988
1989 void
1990 Editor::delete_screen ()
1991 {
1992 }
1993
1994 /* SEARCH */
1995
1996 void
1997 Editor::search_backwards ()
1998 {
1999         /* what ? */
2000 }
2001
2002 void
2003 Editor::search_forwards ()
2004 {
2005         /* what ? */
2006 }
2007
2008 /* MARKS */
2009
2010 void
2011 Editor::jump_forward_to_mark ()
2012 {
2013         if (!session) {
2014                 return;
2015         }
2016         
2017         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
2018
2019         if (location) {
2020                 session->request_locate (location->start(), session->transport_rolling());
2021         } else {
2022                 session->request_locate (session->current_end_frame());
2023         }
2024 }
2025
2026 void
2027 Editor::jump_backward_to_mark ()
2028 {
2029         if (!session) {
2030                 return;
2031         }
2032
2033         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
2034         
2035         if (location) {
2036                 session->request_locate (location->start(), session->transport_rolling());
2037         } else {
2038                 session->goto_start ();
2039         }
2040 }
2041
2042 void
2043 Editor::set_mark ()
2044 {
2045         nframes_t pos;
2046         float prefix;
2047         bool was_floating;
2048         string markername;
2049
2050         if (get_prefix (prefix, was_floating)) {
2051                 pos = session->audible_frame ();
2052         } else {
2053                 if (was_floating) {
2054                         pos = (nframes_t) floor (prefix * session->frame_rate ());
2055                 } else {
2056                         pos = (nframes_t) floor (prefix);
2057                 }
2058         }
2059
2060         session->locations()->next_available_name(markername,"mark");
2061         if (!choose_new_marker_name(markername)) {
2062                 return;
2063         }
2064         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
2065 }
2066
2067 void
2068 Editor::clear_markers ()
2069 {
2070         if (session) {
2071                 session->begin_reversible_command (_("clear markers"));
2072                 XMLNode &before = session->locations()->get_state();
2073                 session->locations()->clear_markers ();
2074                 XMLNode &after = session->locations()->get_state();
2075                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2076                 session->commit_reversible_command ();
2077         }
2078 }
2079
2080 void
2081 Editor::clear_ranges ()
2082 {
2083         if (session) {
2084                 session->begin_reversible_command (_("clear ranges"));
2085                 XMLNode &before = session->locations()->get_state();
2086                 
2087                 Location * looploc = session->locations()->auto_loop_location();
2088                 Location * punchloc = session->locations()->auto_punch_location();
2089                 
2090                 session->locations()->clear_ranges ();
2091                 // re-add these
2092                 if (looploc) session->locations()->add (looploc);
2093                 if (punchloc) session->locations()->add (punchloc);
2094                 
2095                 XMLNode &after = session->locations()->get_state();
2096                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2097                 session->commit_reversible_command ();
2098         }
2099 }
2100
2101 void
2102 Editor::clear_locations ()
2103 {
2104         session->begin_reversible_command (_("clear locations"));
2105         XMLNode &before = session->locations()->get_state();
2106         session->locations()->clear ();
2107         XMLNode &after = session->locations()->get_state();
2108         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2109         session->commit_reversible_command ();
2110         session->locations()->clear ();
2111 }
2112
2113 void
2114 Editor::unhide_markers ()
2115 {
2116         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2117                 Location *l = (*i).first;
2118                 if (l->is_hidden() && l->is_mark()) {
2119                         l->set_hidden(false, this);
2120                 }
2121         }
2122 }
2123
2124 void
2125 Editor::unhide_ranges ()
2126 {
2127         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2128                 Location *l = (*i).first;
2129                 if (l->is_hidden() && l->is_range_marker()) { 
2130                         l->set_hidden(false, this);
2131                 }
2132         }
2133 }
2134
2135 /* INSERT/REPLACE */
2136
2137 void
2138 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
2139 {
2140         double wx, wy;
2141         double cx, cy;
2142         TimeAxisView *tv;
2143         nframes_t where;
2144         AudioTimeAxisView *atv = 0;
2145         boost::shared_ptr<Playlist> playlist;
2146         
2147         track_canvas->window_to_world (x, y, wx, wy);
2148         wx += horizontal_adjustment.get_value();
2149         wy += vertical_adjustment.get_value();
2150
2151         GdkEvent event;
2152         event.type = GDK_BUTTON_RELEASE;
2153         event.button.x = wx;
2154         event.button.y = wy;
2155         
2156         where = event_frame (&event, &cx, &cy);
2157
2158         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
2159                 /* clearly outside canvas area */
2160                 return;
2161         }
2162         
2163         if ((tv = trackview_by_y_position (cy)) == 0) {
2164                 return;
2165         }
2166         
2167         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
2168                 return;
2169         }
2170
2171         if ((playlist = atv->playlist()) == 0) {
2172                 return;
2173         }
2174         
2175         snap_to (where);
2176         
2177         begin_reversible_command (_("insert dragged region"));
2178         XMLNode &before = playlist->get_state();
2179         playlist->add_region (RegionFactory::create (region), where, 1.0);
2180         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2181         commit_reversible_command ();
2182 }
2183
2184 void
2185 Editor::insert_region_list_selection (float times)
2186 {
2187         RouteTimeAxisView *tv = 0;
2188         boost::shared_ptr<Playlist> playlist;
2189
2190         if (clicked_audio_trackview != 0) {
2191                 tv = clicked_audio_trackview;
2192         } else if (!selection->tracks.empty()) {
2193                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2194                         return;
2195                 }
2196         } else if (entered_track != 0) {
2197                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2198                         return;
2199                 }
2200         } else {
2201                 return;
2202         }
2203
2204         if ((playlist = tv->playlist()) == 0) {
2205                 return;
2206         }
2207         
2208         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2209         
2210         if (selected->count_selected_rows() != 1) {
2211                 return;
2212         }
2213         
2214         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
2215
2216         /* only one row selected, so rows.begin() is it */
2217
2218         TreeIter iter;
2219
2220         if ((iter = region_list_model->get_iter (*rows.begin()))) {
2221
2222                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
2223                 
2224                 begin_reversible_command (_("insert region"));
2225                 XMLNode &before = playlist->get_state();
2226                 playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times);
2227                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2228                 commit_reversible_command ();
2229         } 
2230 }
2231
2232 /* BUILT-IN EFFECTS */
2233
2234 void
2235 Editor::reverse_selection ()
2236 {
2237
2238 }
2239
2240 /* GAIN ENVELOPE EDITING */
2241
2242 void
2243 Editor::edit_envelope ()
2244 {
2245 }
2246
2247 /* PLAYBACK */
2248
2249 void
2250 Editor::transition_to_rolling (bool fwd)
2251 {
2252         if (!session) {
2253                 return;
2254         }
2255
2256         switch (Config->get_slave_source()) {
2257         case None:
2258         case JACK:
2259                 break;
2260         default:
2261                 /* transport controlled by the master */
2262                 return;
2263         }
2264
2265         if (session->is_auditioning()) {
2266                 session->cancel_audition ();
2267                 return;
2268         }
2269         
2270         session->request_transport_speed (fwd ? 1.0f : -1.0f);
2271 }
2272
2273 void
2274 Editor::toggle_playback (bool with_abort)
2275 {
2276         if (!session) {
2277                 return;
2278         }
2279
2280         switch (Config->get_slave_source()) {
2281         case None:
2282         case JACK:
2283                 break;
2284         default:
2285                 /* transport controlled by the master */
2286                 return;
2287         }
2288
2289         if (session->is_auditioning()) {
2290                 session->cancel_audition ();
2291                 return;
2292         }
2293         
2294         if (session->transport_rolling()) {
2295                 session->request_stop (with_abort);
2296                 if (session->get_play_loop()) {
2297                         session->request_play_loop (false);
2298                 }
2299         } else {
2300                 session->request_transport_speed (1.0f);
2301         }
2302 }
2303
2304 void
2305 Editor::play_from_start ()
2306 {
2307         session->request_locate (session->current_start_frame(), true);
2308 }
2309
2310 void
2311 Editor::play_from_edit_point ()
2312 {
2313         session->request_locate (get_preferred_edit_position(), true);
2314 }
2315
2316 void
2317 Editor::play_from_edit_point_and_return ()
2318 {
2319         nframes64_t start_frame;
2320         nframes64_t return_frame;
2321
2322         start_frame = get_preferred_edit_position (true);
2323
2324         if (session->transport_rolling()) {
2325                 session->request_locate (start_frame, false);
2326                 return;
2327         }
2328
2329         /* don't reset the return frame if its already set */
2330
2331         if ((return_frame = session->requested_return_frame()) < 0) {
2332                 return_frame = session->audible_frame();
2333         }
2334
2335         if (start_frame >= 0) {
2336                 session->request_roll_at_and_return (start_frame, return_frame);
2337         }
2338 }
2339
2340 void
2341 Editor::play_selection ()
2342 {
2343         if (selection->time.empty()) {
2344                 return;
2345         }
2346
2347         session->request_play_range (true);
2348 }
2349
2350 void
2351 Editor::loop_selected_region ()
2352 {
2353         RegionSelection rs; 
2354
2355         get_regions_for_action (rs);
2356
2357         if (!rs.empty()) {
2358                 RegionView *rv = *(rs.begin());
2359                 Location* tll;
2360
2361                 if ((tll = transport_loop_location()) != 0)  {
2362
2363                         tll->set (rv->region()->position(), rv->region()->last_frame());
2364                         
2365                         // enable looping, reposition and start rolling
2366
2367                         session->request_play_loop (true);
2368                         session->request_locate (tll->start(), false);
2369                         session->request_transport_speed (1.0f);
2370                 }
2371         }
2372 }
2373
2374 void
2375 Editor::play_location (Location& location)
2376 {
2377         if (location.start() <= location.end()) {
2378                 return;
2379         }
2380
2381         session->request_bounded_roll (location.start(), location.end());
2382 }
2383
2384 void
2385 Editor::loop_location (Location& location)
2386 {
2387         if (location.start() <= location.end()) {
2388                 return;
2389         }
2390
2391         Location* tll;
2392
2393         if ((tll = transport_loop_location()) != 0) {
2394                 tll->set (location.start(), location.end());
2395
2396                 // enable looping, reposition and start rolling
2397                 session->request_play_loop (true);
2398                 session->request_locate (tll->start(), true);
2399         }
2400 }
2401
2402 void
2403 Editor::raise_region ()
2404 {
2405         selection->foreach_region (&Region::raise);
2406 }
2407
2408 void
2409 Editor::raise_region_to_top ()
2410 {
2411         selection->foreach_region (&Region::raise_to_top);
2412 }
2413
2414 void
2415 Editor::lower_region ()
2416 {
2417         selection->foreach_region (&Region::lower);
2418 }
2419
2420 void
2421 Editor::lower_region_to_bottom ()
2422 {
2423         selection->foreach_region (&Region::lower_to_bottom);
2424 }
2425
2426 void
2427 Editor::edit_region ()
2428 {
2429         if (clicked_regionview == 0) {
2430                 return;
2431         }
2432         
2433         clicked_regionview->show_region_editor ();
2434 }
2435
2436 void
2437 Editor::rename_region()
2438 {
2439         RegionSelection rs; 
2440
2441         get_regions_for_action (rs);
2442
2443         if (rs.empty()) {
2444                 return;
2445         }
2446
2447         WindowTitle title (Glib::get_application_name());
2448         title += _("Rename Region");
2449
2450         ArdourDialog d (*this, title.get_string(), true, false);
2451         Entry entry;
2452         Label label (_("New name:"));
2453         HBox hbox;
2454
2455         hbox.set_spacing (6);
2456         hbox.pack_start (label, false, false);
2457         hbox.pack_start (entry, true, true);
2458
2459         d.get_vbox()->set_border_width (12);
2460         d.get_vbox()->pack_start (hbox, false, false);
2461
2462         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2463         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2464
2465         d.set_size_request (300, -1);
2466         d.set_position (Gtk::WIN_POS_MOUSE);
2467
2468         entry.set_text (rs.front()->region()->name());
2469         entry.select_region (0, -1);
2470
2471         entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK));
2472         
2473         d.show_all ();
2474         
2475         entry.grab_focus();
2476
2477         int ret = d.run();
2478
2479         d.hide ();
2480
2481         if (ret == RESPONSE_OK) {
2482                 std::string str = entry.get_text();
2483                 strip_whitespace_edges (str);
2484                 if (!str.empty()) {
2485                         rs.front()->region()->set_name (str);
2486                         redisplay_regions ();
2487                 }
2488         }
2489 }
2490
2491 void
2492 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2493 {
2494         if (session->is_auditioning()) {
2495                 session->cancel_audition ();
2496         } 
2497
2498         // note: some potential for creativity here, because region doesn't
2499         // have to belong to the playlist that Route is handling
2500
2501         // bool was_soloed = route.soloed();
2502
2503         route.set_solo (true, this);
2504         
2505         session->request_bounded_roll (region->position(), region->position() + region->length());
2506         
2507         /* XXX how to unset the solo state ? */
2508 }
2509
2510 void
2511 Editor::play_edit_range ()
2512 {
2513         nframes64_t start, end;
2514
2515         if (get_edit_op_range (start, end)) {
2516                 session->request_bounded_roll (start, end);
2517         }
2518 }
2519
2520 void
2521 Editor::play_selected_region ()
2522 {
2523         nframes64_t start = max_frames;
2524         nframes64_t end = 0;
2525         RegionSelection rs; 
2526
2527         get_regions_for_action (rs);
2528          
2529         if (rs.empty()) {
2530                 return;
2531         }
2532
2533         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2534                 if ((*i)->region()->position() < start) {
2535                         start = (*i)->region()->position();
2536                 }
2537                 if ((*i)->region()->last_frame() + 1 > end) {
2538                         end = (*i)->region()->last_frame() + 1;
2539                 }
2540         }
2541
2542         session->request_stop ();
2543         session->request_bounded_roll (start, end);
2544 }
2545
2546 void
2547 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2548 {
2549         session->audition_region (region);
2550 }
2551
2552 void
2553 Editor::build_interthread_progress_window ()
2554 {
2555         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2556
2557         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2558         
2559         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2560         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2561
2562         // GTK2FIX: this button needs a modifiable label
2563
2564         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2565         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2566
2567         interthread_cancel_button.add (interthread_cancel_label);
2568
2569         interthread_progress_window->set_default_size (200, 100);
2570 }
2571
2572 void
2573 Editor::interthread_cancel_clicked ()
2574 {
2575         if (current_interthread_info) {
2576                 current_interthread_info->cancel = true;
2577         }
2578 }
2579
2580 void
2581 Editor::region_from_selection ()
2582 {
2583         if (clicked_trackview == 0) {
2584                 return;
2585         }
2586
2587         if (selection->time.empty()) {
2588                 return;
2589         }
2590
2591         nframes_t start = selection->time[clicked_selection].start;
2592         nframes_t end = selection->time[clicked_selection].end;
2593
2594         nframes_t selection_cnt = end - start + 1;
2595         
2596         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2597                 boost::shared_ptr<AudioRegion> current;
2598                 boost::shared_ptr<Region> current_r;
2599                 boost::shared_ptr<Playlist> pl;
2600
2601                 nframes_t internal_start;
2602                 string new_name;
2603
2604                 if ((pl = (*i)->playlist()) == 0) {
2605                         continue;
2606                 }
2607
2608                 if ((current_r = pl->top_region_at (start)) == 0) {
2609                         continue;
2610                 }
2611
2612                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2613                 // FIXME: audio only
2614                 if (current != 0) {
2615                         internal_start = start - current->position();
2616                         session->region_name (new_name, current->name(), true);
2617                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2618                 }
2619         }
2620 }       
2621
2622 void
2623 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2624 {
2625         if (selection->time.empty() || selection->tracks.empty()) {
2626                 return;
2627         }
2628
2629         nframes_t start = selection->time[clicked_selection].start;
2630         nframes_t end = selection->time[clicked_selection].end;
2631         
2632         sort_track_selection ();
2633
2634         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2635
2636                 boost::shared_ptr<AudioRegion> current;
2637                 boost::shared_ptr<Region> current_r;
2638                 boost::shared_ptr<Playlist> playlist;
2639                 nframes_t internal_start;
2640                 string new_name;
2641
2642                 if ((playlist = (*i)->playlist()) == 0) {
2643                         continue;
2644                 }
2645
2646                 if ((current_r = playlist->top_region_at(start)) == 0) {
2647                         continue;
2648                 }
2649
2650                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2651                         continue;
2652                 }
2653         
2654                 internal_start = start - current->position();
2655                 session->region_name (new_name, current->name(), true);
2656                 
2657                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2658         }
2659 }
2660
2661 void
2662 Editor::split_multichannel_region ()
2663 {
2664         RegionSelection rs; 
2665
2666         get_regions_for_action (rs);
2667
2668         if (rs.empty()) {
2669                 return;
2670         }
2671
2672         vector<boost::shared_ptr<AudioRegion> > v;
2673
2674         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2675
2676                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2677                 
2678                 if (!arv || arv->audio_region()->n_channels() < 2) {
2679                         continue;
2680                 }
2681
2682                 (arv)->audio_region()->separate_by_channel (*session, v);
2683         }
2684 }
2685
2686 void
2687 Editor::new_region_from_selection ()
2688 {
2689         region_from_selection ();
2690         cancel_selection ();
2691 }
2692
2693 static void
2694 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2695 {
2696         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2697         case OverlapNone:
2698                 break;
2699         default:
2700                 rs->push_back (rv);
2701         }
2702 }
2703
2704 void
2705 Editor::separate_regions_between (const TimeSelection& ts)
2706 {
2707         bool in_command = false;
2708         boost::shared_ptr<Playlist> playlist;
2709         RegionSelection new_selection;
2710         TrackSelection tmptracks;
2711
2712         if (selection->tracks.empty()) {
2713                 
2714                 /* use tracks with selected regions */
2715
2716                 RegionSelection rs; 
2717
2718                 get_regions_for_action (rs);
2719
2720                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2721                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2722
2723                         if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) {
2724                                 tmptracks.push_back (tv);
2725                         }
2726                 }
2727
2728                 if (tmptracks.empty()) {
2729                         /* no regions selected: do nothing */
2730                         return;
2731                 }
2732
2733         } else {
2734
2735                 tmptracks = selection->tracks;
2736
2737         }
2738
2739         sort_track_selection (&tmptracks);
2740
2741         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2742
2743                 AudioTimeAxisView* atv;
2744
2745                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2746
2747                         if (atv->is_audio_track()) {
2748
2749                                 /* no edits to destructive tracks */
2750
2751                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2752                                         continue;
2753                                 }
2754                                         
2755                                 if ((playlist = atv->playlist()) != 0) {
2756
2757
2758                                         XMLNode *before;
2759                                         bool got_some;
2760
2761                                         before = &(playlist->get_state());
2762                                         got_some = false;
2763
2764                                         /* XXX need to consider musical time selections here at some point */
2765
2766                                         double speed = atv->get_diskstream()->speed();
2767
2768
2769                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2770
2771                                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2772                                                 latest_regionviews.clear ();
2773
2774                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2775
2776                                                 c.disconnect ();
2777
2778                                                 if (!latest_regionviews.empty()) {
2779                                                         
2780                                                         got_some = true;
2781
2782                                                         atv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2783                                                         
2784                                                         if (!in_command) {
2785                                                                 begin_reversible_command (_("separate"));
2786                                                                 in_command = true;
2787                                                         }
2788                                                         
2789                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2790                                                         
2791                                                 } 
2792                                         }
2793
2794                                         if (!got_some) {
2795                                                 delete before;
2796                                         }
2797                                 }
2798                         }
2799                 }
2800         }
2801
2802         if (in_command) {
2803                 selection->set (new_selection);
2804                 set_mouse_mode (MouseObject);
2805
2806                 commit_reversible_command ();
2807         }
2808 }
2809
2810 void
2811 Editor::separate_region_from_selection ()
2812 {
2813         /* preferentially use *all* ranges in the time selection if we're in range mode
2814            to allow discontiguous operation, since get_edit_op_range() currently
2815            returns a single range.
2816         */
2817
2818         if (mouse_mode == MouseRange && !selection->time.empty()) {
2819
2820                 separate_regions_between (selection->time);
2821
2822         } else {
2823
2824                 nframes64_t start;
2825                 nframes64_t end;
2826                 
2827                 if (get_edit_op_range (start, end)) {
2828                         
2829                         AudioRange ar (start, end, 1);
2830                         TimeSelection ts;
2831                         ts.push_back (ar);
2832
2833                         separate_regions_between (ts);
2834                 }
2835         }
2836 }
2837
2838 void
2839 Editor::separate_region_from_punch ()
2840 {
2841         Location* loc  = session->locations()->auto_punch_location();
2842         if (loc) {
2843                 separate_regions_using_location (*loc);
2844         }
2845 }
2846
2847 void
2848 Editor::separate_region_from_loop ()
2849 {
2850         Location* loc  = session->locations()->auto_loop_location();
2851         if (loc) {
2852                 separate_regions_using_location (*loc);
2853         }
2854 }
2855
2856 void
2857 Editor::separate_regions_using_location (Location& loc)
2858 {
2859         if (loc.is_mark()) {
2860                 return;
2861         }
2862
2863         AudioRange ar (loc.start(), loc.end(), 1);
2864         TimeSelection ts;
2865
2866         ts.push_back (ar);
2867
2868         separate_regions_between (ts);
2869 }
2870
2871 void
2872 Editor::crop_region_to_selection ()
2873 {
2874         if (!selection->time.empty()) {
2875
2876                 crop_region_to (selection->time.start(), selection->time.end_frame());
2877
2878         } else {
2879
2880                 nframes64_t start;
2881                 nframes64_t end;
2882
2883                 if (get_edit_op_range (start, end)) {
2884                         crop_region_to (start, end);
2885                 }
2886         }
2887                 
2888 }               
2889
2890 void
2891 Editor::crop_region_to (nframes_t start, nframes_t end)
2892 {
2893         vector<boost::shared_ptr<Playlist> > playlists;
2894         boost::shared_ptr<Playlist> playlist;
2895         TrackSelection* ts;
2896
2897         if (selection->tracks.empty()) {
2898                 ts = &track_views;
2899         } else {
2900                 sort_track_selection ();
2901                 ts = &selection->tracks;
2902         }
2903         
2904         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2905                 
2906                 AudioTimeAxisView* atv;
2907                 
2908                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2909                         
2910                         if (atv->is_audio_track()) {
2911                                 
2912                                 /* no edits to destructive tracks */
2913
2914                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2915                                         continue;
2916                                 }
2917
2918                                 if ((playlist = atv->playlist()) != 0) {
2919                                         playlists.push_back (playlist);
2920                                 }
2921                         }
2922                 }
2923         }
2924
2925         if (playlists.empty()) {
2926                 return;
2927         }
2928                 
2929         nframes_t the_start;
2930         nframes_t the_end;
2931         nframes_t cnt;
2932         
2933         begin_reversible_command (_("trim to selection"));
2934         
2935         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2936                 
2937                 boost::shared_ptr<Region> region;
2938         
2939                 the_start = start;
2940         
2941                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2942                         continue;
2943                 }
2944                 
2945                 /* now adjust lengths to that we do the right thing
2946                    if the selection extends beyond the region
2947                 */
2948                 
2949                 the_start = max (the_start, region->position());
2950                 if (max_frames - the_start < region->length()) {
2951                         the_end = the_start + region->length() - 1;
2952                 } else {
2953                         the_end = max_frames;
2954                 }
2955                 the_end = min (end, the_end);
2956                 cnt = the_end - the_start + 1;
2957                 
2958                 XMLNode &before = (*i)->get_state();
2959                 region->trim_to (the_start, cnt, this);
2960                 XMLNode &after = (*i)->get_state();
2961                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2962         }
2963         
2964         commit_reversible_command ();
2965 }               
2966
2967 void
2968 Editor::region_fill_track ()
2969 {
2970         nframes_t end;
2971         RegionSelection rs; 
2972
2973         get_regions_for_action (rs);
2974
2975         if (!session || rs.empty()) {
2976                 return;
2977         }
2978
2979         end = session->current_end_frame ();
2980
2981         begin_reversible_command (_("region fill"));
2982
2983         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2984
2985                 boost::shared_ptr<Region> region ((*i)->region());
2986                 
2987                 // FIXME
2988                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2989                 if (!ar)
2990                         continue;
2991
2992                 boost::shared_ptr<Playlist> pl = region->playlist();
2993
2994                 if (end <= region->last_frame()) {
2995                         return;
2996                 }
2997
2998                 double times = (double) (end - region->last_frame()) / (double) region->length();
2999
3000                 if (times == 0) {
3001                         return;
3002                 }
3003
3004                 XMLNode &before = pl->get_state();
3005                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
3006                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
3007         }
3008
3009         commit_reversible_command ();
3010 }
3011
3012 void
3013 Editor::region_fill_selection ()
3014 {
3015         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3016                 return;
3017         }
3018
3019         if (selection->time.empty()) {
3020                 return;
3021         }
3022
3023
3024         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
3025
3026         if (selected->count_selected_rows() != 1) {
3027                 return;
3028         }
3029
3030         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
3031         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
3032
3033         nframes_t start = selection->time[clicked_selection].start;
3034         nframes_t end = selection->time[clicked_selection].end;
3035
3036         boost::shared_ptr<Playlist> playlist; 
3037
3038         if (selection->tracks.empty()) {
3039                 return;
3040         }
3041
3042         nframes_t selection_length = end - start;
3043         float times = (float)selection_length / region->length();
3044         
3045         begin_reversible_command (_("fill selection"));
3046         
3047         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3048
3049                 if ((playlist = (*i)->playlist()) == 0) {
3050                         continue;
3051                 }               
3052                 
3053                 XMLNode &before = playlist->get_state();
3054                 playlist->add_region (RegionFactory::create (region), start, times);
3055                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3056         }
3057         
3058         commit_reversible_command ();                   
3059 }
3060
3061 void
3062 Editor::set_region_sync_from_edit_point ()
3063 {
3064         nframes64_t where = get_preferred_edit_position ();
3065         RegionSelection rs;
3066         get_regions_for_action (rs);
3067         set_sync_point (where, rs);
3068 }
3069
3070 void
3071 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
3072 {
3073         bool in_command = false;
3074
3075         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3076                 
3077                 if (!(*r)->region()->covers (where)) {
3078                         continue;
3079                 }
3080
3081                 boost::shared_ptr<Region> region ((*r)->region());
3082
3083                 if (!in_command) {
3084                         begin_reversible_command (_("set sync point"));
3085                         in_command = true;
3086                 }
3087
3088                 XMLNode &before = region->playlist()->get_state();
3089                 region->set_sync_position (get_preferred_edit_position());
3090                 XMLNode &after = region->playlist()->get_state();
3091                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3092         }
3093
3094         if (in_command) {
3095                 commit_reversible_command ();
3096         }
3097 }
3098
3099 void
3100 Editor::remove_region_sync ()
3101 {
3102         if (clicked_regionview) {
3103                 boost::shared_ptr<Region> region (clicked_regionview->region());
3104                 begin_reversible_command (_("remove sync"));
3105                 XMLNode &before = region->playlist()->get_state();
3106                 region->clear_sync_position ();
3107                 XMLNode &after = region->playlist()->get_state();
3108                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3109                 commit_reversible_command ();
3110         }
3111 }
3112
3113 void
3114 Editor::naturalize ()
3115 {
3116         RegionSelection rs; 
3117
3118         get_regions_for_action (rs);
3119
3120         if (rs.empty()) {
3121                 return;
3122         }
3123
3124         begin_reversible_command (_("naturalize"));
3125         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3126                 XMLNode &before = (*i)->region()->get_state();
3127                 (*i)->region()->move_to_natural_position (this);
3128                 XMLNode &after = (*i)->region()->get_state();
3129                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
3130         }
3131         commit_reversible_command ();
3132 }
3133
3134 void
3135 Editor::align (RegionPoint what)
3136 {
3137         RegionSelection rs; 
3138
3139         get_regions_for_action (rs);
3140         nframes64_t where = get_preferred_edit_position();
3141
3142         if (!rs.empty()) {
3143                 align_selection (what, where, rs);
3144         } else {
3145
3146                 RegionSelection rs;
3147                 get_regions_at (rs, where, selection->tracks);
3148                 align_selection (what, where, rs);
3149         }
3150 }
3151
3152 void
3153 Editor::align_relative (RegionPoint what)
3154 {
3155         nframes64_t where = get_preferred_edit_position();
3156         RegionSelection rs; 
3157
3158         get_regions_for_action (rs);
3159
3160         if (!rs.empty()) {
3161                 align_selection_relative (what, where, rs);
3162         } 
3163 }
3164
3165 struct RegionSortByTime {
3166     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
3167             return a->region()->position() < b->region()->position();
3168     }
3169 };
3170
3171 void
3172 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
3173 {
3174         if (rs.empty()) {
3175                 return;
3176         }
3177
3178         nframes_t distance;
3179         nframes_t pos = 0;
3180         int dir;
3181
3182         list<RegionView*> sorted;
3183         rs.by_position (sorted);
3184         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3185
3186         switch (point) {
3187         case Start:
3188                 pos = position;
3189                 if (position > r->position()) {
3190                         distance = position - r->position();
3191                         dir = 1;
3192                 } else {
3193                         distance = r->position() - position;
3194                         dir = -1;
3195                 }
3196                 break;
3197                 
3198         case End:
3199                 if (position > r->last_frame()) {
3200                         distance = position - r->last_frame();
3201                         pos = r->position() + distance;
3202                         dir = 1;
3203                 } else {
3204                         distance = r->last_frame() - position;
3205                         pos = r->position() - distance;
3206                         dir = -1;
3207                 }
3208                 break;
3209
3210         case SyncPoint:
3211                 pos = r->adjust_to_sync (position);
3212                 if (pos > r->position()) {
3213                         distance = pos - r->position();
3214                         dir = 1;
3215                 } else {
3216                         distance = r->position() - pos;
3217                         dir = -1;
3218                 }
3219                 break;  
3220         }
3221
3222         if (pos == r->position()) {
3223                 return;
3224         }
3225
3226         begin_reversible_command (_("align selection (relative)"));
3227
3228         /* move first one specially */
3229
3230         XMLNode &before = r->playlist()->get_state();
3231         r->set_position (pos, this);
3232         XMLNode &after = r->playlist()->get_state();
3233         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
3234
3235         /* move rest by the same amount */
3236         
3237         sorted.pop_front();
3238         
3239         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3240
3241                 boost::shared_ptr<Region> region ((*i)->region());
3242
3243                 XMLNode &before = region->playlist()->get_state();
3244                 
3245                 if (dir > 0) {
3246                         region->set_position (region->position() + distance, this);
3247                 } else {
3248                         region->set_position (region->position() - distance, this);
3249                 }
3250
3251                 XMLNode &after = region->playlist()->get_state();
3252                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3253
3254         }
3255
3256         commit_reversible_command ();
3257 }
3258
3259 void
3260 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
3261 {
3262         if (rs.empty()) {
3263                 return;
3264         }
3265
3266         begin_reversible_command (_("align selection"));
3267
3268         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3269                 align_region_internal ((*i)->region(), point, position);
3270         }
3271
3272         commit_reversible_command ();
3273 }
3274
3275 void
3276 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3277 {
3278         begin_reversible_command (_("align region"));
3279         align_region_internal (region, point, position);
3280         commit_reversible_command ();
3281 }
3282
3283 void
3284 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3285 {
3286         XMLNode &before = region->playlist()->get_state();
3287
3288         switch (point) {
3289         case SyncPoint:
3290                 region->set_position (region->adjust_to_sync (position), this);
3291                 break;
3292
3293         case End:
3294                 if (position > region->length()) {
3295                         region->set_position (position - region->length(), this);
3296                 }
3297                 break;
3298
3299         case Start:
3300                 region->set_position (position, this);
3301                 break;
3302         }
3303
3304         XMLNode &after = region->playlist()->get_state();
3305         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3306 }       
3307
3308 void
3309 Editor::trim_region_to_loop ()
3310 {
3311         Location* loc = session->locations()->auto_loop_location();
3312         if (!loc) {
3313                 return;
3314         }
3315         trim_region_to_location (*loc, _("trim to loop"));
3316 }
3317
3318 void
3319 Editor::trim_region_to_punch ()
3320 {
3321         Location* loc = session->locations()->auto_punch_location();
3322         if (!loc) {
3323                 return;
3324         }
3325         trim_region_to_location (*loc, _("trim to punch"));
3326 }
3327
3328 void
3329 Editor::trim_region_to_location (const Location& loc, const char* str)
3330 {
3331         RegionSelection rs;
3332
3333         get_regions_for_action (rs);
3334
3335         begin_reversible_command (str);
3336
3337         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3338                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3339
3340                 if (!arv) {
3341                         continue;
3342                 }
3343
3344                 /* require region to span proposed trim */
3345
3346                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3347                 case OverlapInternal:
3348                         break;
3349                 default:
3350                         continue;
3351                 }
3352                                 
3353                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3354
3355                 if (!atav) {
3356                         return;
3357                 }
3358
3359                 float speed = 1.0;
3360                 nframes_t start;
3361                 nframes_t end;
3362
3363                 if (atav->get_diskstream() != 0) {
3364                         speed = atav->get_diskstream()->speed();
3365                 }
3366
3367                 start = session_frame_to_track_frame (loc.start(), speed);
3368                 end = session_frame_to_track_frame (loc.end(), speed);
3369
3370                 XMLNode &before = arv->region()->playlist()->get_state();
3371                 arv->region()->trim_to (start, (end - start), this);
3372                 XMLNode &after = arv->region()->playlist()->get_state();
3373                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3374         }
3375                 
3376         commit_reversible_command ();
3377 }
3378
3379 void
3380 Editor::trim_region_to_edit_point ()
3381 {
3382         RegionSelection rs;
3383         
3384         get_regions_for_action (rs);
3385
3386         nframes64_t where = get_preferred_edit_position();
3387
3388         begin_reversible_command (_("trim region start to edit point"));
3389
3390         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3391                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3392
3393                 if (!arv) {
3394                         continue;
3395                 }
3396
3397                 /* require region to cover trim */
3398
3399                 if (!arv->region()->covers (where)) {
3400                         continue;
3401                 }
3402
3403                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3404
3405                 if (!atav) {
3406                         return;
3407                 }
3408
3409                 float speed = 1.0;
3410
3411                 if (atav->get_diskstream() != 0) {
3412                         speed = atav->get_diskstream()->speed();
3413                 }
3414
3415                 XMLNode &before = arv->region()->playlist()->get_state();
3416                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3417                 XMLNode &after = arv->region()->playlist()->get_state();
3418                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3419         }
3420                 
3421         commit_reversible_command ();
3422 }
3423
3424 void
3425 Editor::trim_region_from_edit_point ()
3426 {
3427         RegionSelection rs;
3428
3429         get_regions_for_action (rs);
3430
3431         nframes64_t where = get_preferred_edit_position();
3432
3433         begin_reversible_command (_("trim region end to edit point"));
3434
3435         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3436                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3437
3438                 if (!arv) {
3439                         continue;
3440                 }
3441
3442                 /* require region to cover trim */
3443
3444                 if (!arv->region()->covers (where)) {
3445                         continue;
3446                 }
3447
3448                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3449
3450                 if (!atav) {
3451                         return;
3452                 }
3453
3454                 float speed = 1.0;
3455
3456                 if (atav->get_diskstream() != 0) {
3457                         speed = atav->get_diskstream()->speed();
3458                 }
3459
3460                 XMLNode &before = arv->region()->playlist()->get_state();
3461                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3462                 XMLNode &after = arv->region()->playlist()->get_state();
3463                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3464         }
3465                 
3466         commit_reversible_command ();
3467 }
3468
3469 void
3470 Editor::unfreeze_route ()
3471 {
3472         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3473                 return;
3474         }
3475         
3476         clicked_audio_trackview->audio_track()->unfreeze ();
3477 }
3478
3479 void*
3480 Editor::_freeze_thread (void* arg)
3481 {
3482         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
3483         return static_cast<Editor*>(arg)->freeze_thread ();
3484 }
3485
3486 void*
3487 Editor::freeze_thread ()
3488 {
3489         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
3490         return 0;
3491 }
3492
3493 gint
3494 Editor::freeze_progress_timeout (void *arg)
3495 {
3496         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
3497         return !(current_interthread_info->done || current_interthread_info->cancel);
3498 }
3499
3500 void
3501 Editor::freeze_route ()
3502 {
3503         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3504                 return;
3505         }
3506         
3507         InterThreadInfo itt;
3508
3509         if (interthread_progress_window == 0) {
3510                 build_interthread_progress_window ();
3511         }
3512
3513         WindowTitle title(Glib::get_application_name());
3514         title += _("Freeze");
3515         interthread_progress_window->set_title (title.get_string());
3516         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3517         interthread_progress_window->show_all ();
3518         interthread_progress_bar.set_fraction (0.0f);
3519         interthread_progress_label.set_text ("");
3520         interthread_cancel_label.set_text (_("Cancel Freeze"));
3521         current_interthread_info = &itt;
3522
3523         interthread_progress_connection = 
3524           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3525
3526         itt.done = false;
3527         itt.cancel = false;
3528         itt.progress = 0.0f;
3529         
3530         pthread_attr_t attr;
3531         pthread_attr_init(&attr);
3532         pthread_attr_setstacksize(&attr, 500000);
3533
3534         pthread_create (&itt.thread, &attr, _freeze_thread, this);
3535
3536         pthread_attr_destroy(&attr);
3537
3538         track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3539
3540         while (!itt.done && !itt.cancel) {
3541                 gtk_main_iteration ();
3542         }
3543
3544         interthread_progress_connection.disconnect ();
3545         interthread_progress_window->hide_all ();
3546         current_interthread_info = 0;
3547         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
3548 }
3549
3550 void
3551 Editor::bounce_range_selection ()
3552 {
3553         if (selection->time.empty()) {
3554                 return;
3555         }
3556
3557         TrackSelection views = selection->tracks;
3558
3559         nframes_t start = selection->time[clicked_selection].start;
3560         nframes_t end = selection->time[clicked_selection].end;
3561         nframes_t cnt = end - start + 1;
3562
3563         begin_reversible_command (_("bounce range"));
3564
3565         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3566
3567                 AudioTimeAxisView* atv;
3568
3569                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
3570                         continue;
3571                 }
3572                 
3573                 boost::shared_ptr<Playlist> playlist;
3574                 
3575                 if ((playlist = atv->playlist()) == 0) {
3576                         return;
3577                 }
3578
3579                 InterThreadInfo itt;
3580                 
3581                 itt.done = false;
3582                 itt.cancel = false;
3583                 itt.progress = false;
3584
3585                 XMLNode &before = playlist->get_state();
3586                 atv->audio_track()->bounce_range (start, cnt, itt);
3587                 XMLNode &after = playlist->get_state();
3588                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3589         }
3590         
3591         commit_reversible_command ();
3592 }
3593
3594 void
3595 Editor::cut ()
3596 {
3597         cut_copy (Cut);
3598 }
3599
3600 void
3601 Editor::copy ()
3602 {
3603         cut_copy (Copy);
3604 }
3605
3606 void 
3607 Editor::cut_copy (CutCopyOp op)
3608 {
3609         /* only cancel selection if cut/copy is successful.*/
3610
3611         string opname;
3612
3613         switch (op) {
3614         case Cut:
3615                 opname = _("cut");
3616                 break;
3617         case Copy:
3618                 opname = _("copy");
3619                 break;
3620         case Clear:
3621                 opname = _("clear");
3622                 break;
3623         }
3624         
3625         cut_buffer->clear ();
3626
3627         if (entered_marker) {
3628
3629                 /* cut/delete op while pointing at a marker */
3630
3631                 bool ignored;
3632                 Location* loc = find_location_from_marker (entered_marker, ignored);
3633
3634                 if (session && loc) {
3635                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3636                 }
3637
3638                 return;
3639         }
3640
3641         RegionSelection rs; 
3642
3643         /* we only want to cut regions if some are selected */
3644
3645         if (!selection->regions.empty()) {
3646                 get_regions_for_action (rs);
3647         }
3648
3649         switch (current_mouse_mode()) {
3650         case MouseObject: 
3651                 if (!rs.empty() || !selection->points.empty()) {
3652
3653                         begin_reversible_command (opname + _(" objects"));
3654
3655                         if (!rs.empty()) {
3656                                 cut_copy_regions (op, rs);
3657                                 
3658                                 if (op == Cut) {
3659                                         selection->clear_regions ();
3660                                 }
3661                         }
3662
3663                         if (!selection->points.empty()) {
3664                                 cut_copy_points (op);
3665
3666                                 if (op == Cut) {
3667                                         selection->clear_points ();
3668                                 }
3669                         }
3670
3671                         commit_reversible_command ();   
3672                         break; // terminate case statement here
3673                 } 
3674                 if (!selection->time.empty()) {
3675                         /* don't cause suprises */
3676                         break;
3677                 }
3678                 // fall thru if there was nothing selected
3679                 
3680         case MouseRange:
3681                 if (selection->time.empty()) {
3682                         nframes64_t start, end;
3683                         if (!get_edit_op_range (start, end)) {
3684                                 return;
3685                         }
3686                         selection->set ((TimeAxisView*) 0, start, end);
3687                 }
3688                         
3689                 begin_reversible_command (opname + _(" range"));
3690                 cut_copy_ranges (op);
3691                 commit_reversible_command ();
3692                 
3693                 if (op == Cut) {
3694                         selection->clear_time ();
3695                 }
3696
3697                 break;
3698                 
3699         default:
3700                 break;
3701         }
3702 }
3703
3704 void
3705 Editor::cut_copy_points (CutCopyOp op)
3706 {
3707         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3708
3709                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3710
3711                 if (atv) {
3712                         atv->cut_copy_clear_objects (selection->points, op);
3713                 } 
3714         }
3715 }
3716
3717 struct PlaylistState {
3718     boost::shared_ptr<Playlist> playlist;
3719     XMLNode*  before;
3720 };
3721
3722 struct lt_playlist {
3723     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3724             return a.playlist < b.playlist;
3725     }
3726 };
3727         
3728 struct PlaylistMapping { 
3729     TimeAxisView* tv;
3730     boost::shared_ptr<AudioPlaylist> pl;
3731
3732     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3733 };
3734
3735 void
3736 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3737 {
3738         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3739            a map when we want ordered access to both elements. i think.
3740         */
3741
3742         vector<PlaylistMapping> pmap;
3743
3744         nframes_t first_position = max_frames;
3745         
3746         set<PlaylistState, lt_playlist> freezelist;
3747         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3748         
3749         /* get ordering correct before we cut/copy */
3750         
3751         rs.sort_by_position_and_track ();
3752
3753         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3754
3755                 first_position = min ((*x)->region()->position(), first_position);
3756
3757                 if (op == Cut || op == Clear) {
3758                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3759
3760                         if (pl) {
3761
3762                                 PlaylistState before;
3763                                 before.playlist = pl;
3764                                 before.before = &pl->get_state();
3765                                 
3766                                 insert_result = freezelist.insert (before);
3767                                 
3768                                 if (insert_result.second) {
3769                                         pl->freeze ();
3770                                 }
3771                         }
3772                 }
3773
3774                 TimeAxisView* tv = &(*x)->get_trackview();
3775                 vector<PlaylistMapping>::iterator z;
3776
3777                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3778                         if ((*z).tv == tv) {
3779                                 break;
3780                         }
3781                 }
3782                 
3783                 if (z == pmap.end()) {
3784                         pmap.push_back (PlaylistMapping (tv));
3785                 }
3786         }
3787
3788         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3789
3790                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3791                 
3792                 if (!pl) {
3793                         /* impossible, but this handles it for the future */
3794                         continue;
3795                 }
3796
3797                 TimeAxisView& tv = (*x)->get_trackview();
3798                 boost::shared_ptr<AudioPlaylist> npl;
3799                 RegionSelection::iterator tmp;
3800                 
3801                 tmp = x;
3802                 ++tmp;
3803
3804                 vector<PlaylistMapping>::iterator z;
3805                 
3806                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3807                         if ((*z).tv == &tv) {
3808                                 break;
3809                         }
3810                 }
3811                 
3812                 assert (z != pmap.end());
3813                 
3814                 if (!(*z).pl) {
3815                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3816                         npl->freeze();
3817                         (*z).pl = npl;
3818                 } else {
3819                         npl = (*z).pl;
3820                 }
3821                 
3822                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3823                 boost::shared_ptr<Region> _xx;
3824                 
3825                 switch (op) {
3826                 case Cut:
3827                         if (!ar) break;
3828                         
3829                         _xx = RegionFactory::create ((*x)->region());
3830                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3831                         pl->remove_region (((*x)->region()));
3832                         break;
3833                         
3834                 case Copy:
3835                         if (!ar) break;
3836
3837                         /* copy region before adding, so we're not putting same object into two different playlists */
3838                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3839                         break;
3840                         
3841                 case Clear:
3842                         pl->remove_region (((*x)->region()));
3843                         break;
3844                 }
3845
3846                 x = tmp;
3847         }
3848         
3849         list<boost::shared_ptr<Playlist> > foo;
3850         
3851         /* the pmap is in the same order as the tracks in which selected regions occured */
3852         
3853         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3854                 (*i).pl->thaw();
3855                 foo.push_back ((*i).pl);
3856         }
3857         
3858
3859         if (!foo.empty()) {
3860                 cut_buffer->set (foo);
3861         }
3862
3863         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3864                 (*pl).playlist->thaw ();
3865                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3866         }
3867 }
3868
3869 void
3870 Editor::cut_copy_ranges (CutCopyOp op)
3871 {
3872         TrackSelection* ts;
3873         TrackSelection entered;
3874
3875         if (selection->tracks.empty()) {
3876                 if (!entered_track) {
3877                         return;
3878                 }
3879                 entered.push_back (entered_track);
3880                 ts = &entered;
3881         } else {
3882                 ts = &selection->tracks;
3883         }
3884
3885         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3886                 (*i)->cut_copy_clear (*selection, op);
3887         }
3888 }
3889
3890 void
3891 Editor::paste (float times)
3892 {
3893         paste_internal (get_preferred_edit_position(), times);
3894 }
3895
3896 void
3897 Editor::mouse_paste ()
3898 {
3899         nframes64_t where;
3900         bool ignored;
3901
3902         if (!mouse_frame (where, ignored)) {
3903                 return;
3904         }
3905
3906         snap_to (where);
3907         paste_internal (where, 1);
3908 }
3909
3910 void
3911 Editor::paste_internal (nframes_t position, float times)
3912 {
3913         bool commit = false;
3914
3915         if (cut_buffer->empty()) {
3916                 return;
3917         }
3918
3919         if (position == max_frames) {
3920                 position = get_preferred_edit_position();
3921         }
3922
3923         begin_reversible_command (_("paste"));
3924
3925         TrackSelection ts;
3926         TrackSelection::iterator i;
3927         size_t nth;
3928
3929         /* get everything in the correct order */
3930
3931
3932         if (!selection->tracks.empty()) {
3933                 sort_track_selection ();
3934                 ts = selection->tracks;
3935         } else if (entered_track) {
3936                 ts.push_back (entered_track);
3937         }
3938
3939         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3940
3941                 /* undo/redo is handled by individual tracks */
3942
3943                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3944                         commit = true;
3945                 }
3946         }
3947         
3948         if (commit) {
3949                 commit_reversible_command ();
3950         }
3951 }
3952
3953 void
3954 Editor::paste_named_selection (float times)
3955 {
3956         TrackSelection::iterator t;
3957
3958         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3959
3960         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3961                 return;
3962         }
3963
3964         TreeModel::iterator i = selected->get_selected();
3965         NamedSelection* ns = (*i)[named_selection_columns.selection];
3966
3967         list<boost::shared_ptr<Playlist> >::iterator chunk;
3968         list<boost::shared_ptr<Playlist> >::iterator tmp;
3969
3970         chunk = ns->playlists.begin();
3971                 
3972         begin_reversible_command (_("paste chunk"));
3973         
3974         sort_track_selection ();
3975
3976         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3977                 
3978                 AudioTimeAxisView* atv;
3979                 boost::shared_ptr<Playlist> pl;
3980                 boost::shared_ptr<AudioPlaylist> apl;
3981
3982                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3983                         continue;
3984                 }
3985
3986                 if ((pl = atv->playlist()) == 0) {
3987                         continue;
3988                 }
3989                 
3990                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3991                         continue;
3992                 }
3993
3994                 tmp = chunk;
3995                 ++tmp;
3996
3997                 XMLNode &before = apl->get_state();
3998                 apl->paste (*chunk, get_preferred_edit_position(), times);
3999                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
4000
4001                 if (tmp != ns->playlists.end()) {
4002                         chunk = tmp;
4003                 }
4004         }
4005
4006         commit_reversible_command();
4007 }
4008
4009 void
4010 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4011 {
4012         boost::shared_ptr<Playlist> playlist; 
4013         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4014         RegionSelection foo;
4015
4016         begin_reversible_command (_("duplicate region"));
4017
4018         selection->clear_regions ();
4019
4020         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4021
4022                 boost::shared_ptr<Region> r ((*i)->region());
4023
4024                 TimeAxisView& tv = (*i)->get_time_axis_view();
4025                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
4026
4027                 latest_regionviews.clear ();
4028                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4029                 
4030                 playlist = (*i)->region()->playlist();
4031                 XMLNode &before = playlist->get_state();
4032                 playlist->duplicate (r, r->last_frame() + 1, times);
4033                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
4034
4035                 c.disconnect ();
4036                 
4037                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4038         }
4039
4040         commit_reversible_command ();
4041
4042         if (!foo.empty()) {
4043                 selection->set (foo);
4044         }
4045 }
4046
4047 void
4048 Editor::duplicate_selection (float times)
4049 {
4050         if (selection->time.empty() || selection->tracks.empty()) {
4051                 return;
4052         }
4053
4054         boost::shared_ptr<Playlist> playlist; 
4055         vector<boost::shared_ptr<AudioRegion> > new_regions;
4056         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
4057                 
4058         create_region_from_selection (new_regions);
4059
4060         if (new_regions.empty()) {
4061                 return;
4062         }
4063         
4064         begin_reversible_command (_("duplicate selection"));
4065
4066         ri = new_regions.begin();
4067
4068         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4069                 if ((playlist = (*i)->playlist()) == 0) {
4070                         continue;
4071                 }
4072                 XMLNode &before = playlist->get_state();
4073                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4074                 XMLNode &after = playlist->get_state();
4075                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4076
4077                 ++ri;
4078                 if (ri == new_regions.end()) {
4079                         --ri;
4080                 }
4081         }
4082
4083         commit_reversible_command ();
4084 }
4085
4086 void
4087 Editor::reset_point_selection ()
4088 {
4089         /* reset all selected points to the relevant default value */
4090
4091         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4092                 
4093                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
4094                 
4095                 if (atv) {
4096                         atv->reset_objects (selection->points);
4097                 } 
4098         }
4099 }
4100
4101 void
4102 Editor::center_playhead ()
4103 {
4104         float page = canvas_width * frames_per_unit;
4105         center_screen_internal (playhead_cursor->current_frame, page);
4106 }
4107
4108 void
4109 Editor::center_edit_point ()
4110 {
4111         float page = canvas_width * frames_per_unit;
4112         center_screen_internal (get_preferred_edit_position(), page);
4113 }
4114
4115 void
4116 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4117 {
4118         begin_reversible_command (_("clear playlist"));
4119         XMLNode &before = playlist->get_state();
4120         playlist->clear ();
4121         XMLNode &after = playlist->get_state();
4122         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4123         commit_reversible_command ();
4124 }
4125
4126 void
4127 Editor::nudge_track (bool use_edit, bool forwards)
4128 {
4129         boost::shared_ptr<Playlist> playlist; 
4130         nframes_t distance;
4131         nframes_t next_distance;
4132         nframes_t start;
4133
4134         if (use_edit) {
4135                 start = get_preferred_edit_position();
4136         } else {
4137                 start = 0;
4138         }
4139
4140         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4141                 return;
4142         }
4143         
4144         if (selection->tracks.empty()) {
4145                 return;
4146         }
4147         
4148         begin_reversible_command (_("nudge track"));
4149         
4150         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4151
4152                 if ((playlist = (*i)->playlist()) == 0) {
4153                         continue;
4154                 }               
4155                 
4156                 XMLNode &before = playlist->get_state();
4157                 playlist->nudge_after (start, distance, forwards);
4158                 XMLNode &after = playlist->get_state();
4159                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4160         }
4161         
4162         commit_reversible_command ();                   
4163 }
4164
4165 void
4166 Editor::remove_last_capture ()
4167 {
4168         vector<string> choices;
4169         string prompt;
4170         
4171         if (!session) {
4172                 return;
4173         }
4174
4175         if (Config->get_verify_remove_last_capture()) {
4176                 prompt  = _("Do you really want to destroy the last capture?"
4177                             "\n(This is destructive and cannot be undone)");
4178
4179                 choices.push_back (_("No, do nothing."));
4180                 choices.push_back (_("Yes, destroy it."));
4181                 
4182                 Gtkmm2ext::Choice prompter (prompt, choices);
4183                 
4184                 if (prompter.run () == 1) {
4185                         session->remove_last_capture ();
4186                 }
4187
4188         } else {
4189                 session->remove_last_capture();
4190         }
4191 }
4192
4193 void
4194 Editor::normalize_region ()
4195 {
4196         RegionSelection rs; 
4197
4198         get_regions_for_action (rs);
4199         
4200         if (!session) {
4201                 return;
4202         }
4203
4204         if (rs.empty()) {
4205                 return;
4206         }
4207
4208         begin_reversible_command (_("normalize"));
4209
4210         track_canvas->get_window()->set_cursor (*wait_cursor);
4211         gdk_flush ();
4212
4213         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4214                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4215                 if (!arv)
4216                         continue;
4217                 XMLNode &before = arv->region()->get_state();
4218                 arv->audio_region()->normalize_to (0.0f);
4219                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4220         }
4221
4222         commit_reversible_command ();
4223         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
4224 }
4225
4226
4227 void
4228 Editor::denormalize_region ()
4229 {
4230         if (!session) {
4231                 return;
4232         }
4233
4234         RegionSelection rs; 
4235
4236         get_regions_for_action (rs);
4237
4238         if (rs.empty()) {
4239                 return;
4240         }
4241
4242         begin_reversible_command ("denormalize");
4243
4244         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4245                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4246                 if (!arv)
4247                         continue;
4248                 XMLNode &before = arv->region()->get_state();
4249                 arv->audio_region()->set_scale_amplitude (1.0f);
4250                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4251         }
4252
4253         commit_reversible_command ();
4254 }
4255
4256 void
4257 Editor::adjust_region_scale_amplitude (bool up)
4258 {
4259         if (!session) {
4260                 return;
4261         }
4262
4263         RegionSelection rs; 
4264
4265         get_regions_for_action (rs);
4266
4267         if (rs.empty()) {
4268                 return;
4269         }
4270
4271         begin_reversible_command ("denormalize");
4272
4273         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4274                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4275                 if (!arv)
4276                         continue;
4277                 XMLNode &before = arv->region()->get_state();
4278                 
4279                 double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
4280                 
4281                 if (up) {
4282                         fraction += 0.05;
4283                         fraction = min (fraction, 1.0);
4284                 } else {
4285                         fraction -= 0.05;
4286                         fraction = max (fraction, 0.0);
4287                 }
4288
4289                 if (!up && fraction <= 0) {
4290                         continue;
4291                 }
4292
4293                 fraction = slider_position_to_gain (fraction);
4294                 fraction = coefficient_to_dB (fraction);
4295                 fraction = dB_to_coefficient (fraction);
4296
4297                 if (up && fraction >= 2.0) {
4298                         continue;
4299                 }
4300                 
4301                 arv->audio_region()->set_scale_amplitude (fraction);
4302                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4303         }
4304
4305         commit_reversible_command ();
4306 }
4307
4308
4309 void
4310 Editor::reverse_region ()
4311 {
4312         if (!session) {
4313                 return;
4314         }
4315
4316         Reverse rev (*session);
4317         apply_filter (rev, _("reverse regions"));
4318 }
4319
4320 void
4321 Editor::apply_filter (AudioFilter& filter, string command)
4322 {
4323         RegionSelection rs; 
4324
4325         get_regions_for_action (rs);
4326
4327         if (rs.empty()) {
4328                 return;
4329         }
4330
4331         begin_reversible_command (command);
4332
4333         track_canvas->get_window()->set_cursor (*wait_cursor);
4334         gdk_flush ();
4335
4336         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4337                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4338                 if (!arv)
4339                         continue;
4340
4341                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4342
4343                 RegionSelection::iterator tmp;
4344                 
4345                 tmp = r;
4346                 ++tmp;
4347
4348                 if (arv->audio_region()->apply (filter) == 0) {
4349
4350                         XMLNode &before = playlist->get_state();
4351                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4352                         XMLNode &after = playlist->get_state();
4353                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4354                 } else {
4355                         goto out;
4356                 }
4357
4358                 r = tmp;
4359         }
4360
4361         commit_reversible_command ();
4362         rs.clear ();
4363
4364   out:
4365         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
4366 }
4367
4368 void
4369 Editor::region_selection_op (void (Region::*pmf)(void))
4370 {
4371         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4372                 Region* region = (*i)->region().get();
4373                 (region->*pmf)();
4374         }
4375 }
4376
4377
4378 void
4379 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4380 {
4381         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4382                 Region* region = (*i)->region().get();
4383                 (region->*pmf)(arg);
4384         }
4385 }
4386
4387 void
4388 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4389 {
4390         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4391                 Region* region = (*i)->region().get();
4392                 (region->*pmf)(yn);
4393         }
4394 }
4395
4396 void
4397 Editor::external_edit_region ()
4398 {
4399         if (!clicked_regionview) {
4400                 return;
4401         }
4402
4403         /* more to come */
4404 }
4405
4406 void
4407 Editor::brush (nframes_t pos)
4408 {
4409         RegionSelection sel;
4410         RegionSelection rs; 
4411
4412         get_regions_for_action (rs);
4413
4414         snap_to (pos);
4415
4416         if (rs.empty()) {
4417                 return;
4418         }
4419
4420         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4421                 mouse_brush_insert_region ((*i), pos);
4422         }
4423 }
4424
4425 void
4426 Editor::reset_region_gain_envelopes ()
4427 {
4428         RegionSelection rs; 
4429
4430         get_regions_for_action (rs);
4431
4432         if (!session || rs.empty()) {
4433                 return;
4434         }
4435
4436         session->begin_reversible_command (_("reset region gain"));
4437
4438         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4439                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4440                 if (arv) {
4441                         AutomationList& alist (arv->audio_region()->envelope());
4442                         XMLNode& before (alist.get_state());
4443
4444                         arv->audio_region()->set_default_envelope ();
4445                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
4446                 }
4447         }
4448
4449         session->commit_reversible_command ();
4450 }
4451
4452 void
4453 Editor::toggle_gain_envelope_visibility ()
4454 {
4455         RegionSelection rs; 
4456
4457         get_regions_for_action (rs);
4458
4459         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4460                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4461                 if (arv) {
4462                         arv->set_envelope_visible (!arv->envelope_visible());
4463                 }
4464         }
4465 }
4466
4467 void
4468 Editor::toggle_gain_envelope_active ()
4469 {
4470         RegionSelection rs; 
4471
4472         get_regions_for_action (rs);
4473
4474         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4475                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4476                 if (arv) {
4477                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4478                 }
4479         }
4480 }
4481
4482 void
4483 Editor::toggle_region_lock ()
4484 {
4485         RegionSelection rs; 
4486
4487         get_regions_for_action (rs);
4488
4489         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4490                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4491                 if (arv) {
4492                         arv->audio_region()->set_locked (!arv->audio_region()->locked());
4493                 }
4494         }
4495 }
4496
4497 void
4498 Editor::set_region_lock_style (Region::PositionLockStyle ps)
4499 {
4500         RegionSelection rs; 
4501
4502         get_regions_for_action (rs);
4503
4504         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4505                 (*i)->region()->set_position_lock_style (ps);
4506         }
4507 }
4508
4509
4510 void
4511 Editor::toggle_region_mute ()
4512 {
4513         RegionSelection rs; 
4514
4515         get_regions_for_action (rs);
4516
4517         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4518                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4519                 if (arv) {
4520                         arv->audio_region()->set_muted (!arv->audio_region()->muted());
4521                 }
4522         }
4523 }
4524
4525 void
4526 Editor::toggle_region_opaque ()
4527 {
4528         RegionSelection rs; 
4529
4530         get_regions_for_action (rs);
4531
4532         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4533                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4534                 if (arv) {
4535                         arv->audio_region()->set_opaque (!arv->audio_region()->opaque());
4536                 }
4537         }
4538 }
4539
4540 void
4541 Editor::set_fade_length (bool in)
4542 {
4543         RegionSelection rs; 
4544
4545         get_regions_for_action (rs);
4546
4547         /* we need a region to measure the offset from the start */
4548
4549         RegionView* rv;
4550
4551         if (!rs.empty()) {
4552                 rv = rs.front();
4553         } else if (entered_regionview) {
4554                 rv = entered_regionview;
4555         } else {
4556                 return;
4557         }
4558
4559         nframes64_t pos = get_preferred_edit_position();
4560         nframes_t len;
4561         char* cmd;
4562         
4563         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4564                 /* edit point is outside the relevant region */
4565                 return;
4566         }
4567
4568         if (in) {
4569                 if (pos <= rv->region()->position()) {
4570                         /* can't do it */
4571                         return;
4572                 }
4573                 len = pos - rv->region()->position();
4574                 cmd = _("set fade in length");
4575         } else {
4576                 if (pos >= rv->region()->last_frame()) {
4577                         /* can't do it */
4578                         return;
4579                 }
4580                 len = rv->region()->last_frame() - pos;
4581                 cmd = _("set fade out length");
4582         }
4583
4584         begin_reversible_command (cmd);
4585
4586         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4587                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4588
4589                 if (!tmp) {
4590                         return;
4591                 }
4592
4593                 AutomationList* alist;
4594                 if (in) {
4595                         alist = &tmp->audio_region()->fade_in();
4596                 } else {
4597                         alist = &tmp->audio_region()->fade_out();
4598                 }
4599
4600                 XMLNode &before = alist->get_state();
4601
4602                 if (in) {
4603                         tmp->audio_region()->set_fade_in_length (len);
4604                         tmp->audio_region()->set_fade_in_active (true);
4605                 } else {
4606                         tmp->audio_region()->set_fade_out_length (len);
4607                         tmp->audio_region()->set_fade_out_active (true);
4608                 }
4609                 
4610                 XMLNode &after = alist->get_state();
4611                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4612         }
4613
4614         commit_reversible_command ();
4615 }
4616
4617
4618 void
4619 Editor::toggle_fade_active (bool in)
4620 {
4621         RegionSelection rs; 
4622
4623         get_regions_for_action (rs);
4624
4625         if (rs.empty()) {
4626                 return;
4627         }
4628
4629         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4630         bool have_switch = false;
4631         bool yn;
4632
4633         begin_reversible_command (cmd);
4634
4635         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4636                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4637                 
4638                 if (!tmp) {
4639                         return;
4640                 }
4641
4642                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4643
4644                 /* make the behaviour consistent across all regions */
4645                 
4646                 if (!have_switch) {
4647                         if (in) {
4648                                 yn = region->fade_in_active();
4649                         } else {
4650                                 yn = region->fade_out_active();
4651                         }
4652                         have_switch = true;
4653                 }
4654
4655                 XMLNode &before = region->get_state();
4656                 if (in) {
4657                         region->set_fade_in_active (!yn);
4658                 } else {
4659                         region->set_fade_out_active (!yn);
4660                 }
4661                 XMLNode &after = region->get_state();
4662                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4663         }
4664
4665         commit_reversible_command ();
4666 }
4667
4668 void
4669 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4670 {
4671         RegionSelection rs; 
4672
4673         get_regions_for_action (rs);
4674
4675         if (rs.empty()) {
4676                 return;
4677         }
4678
4679         begin_reversible_command (_("set fade in shape"));
4680
4681         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4682                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4683
4684                 if (!tmp) {
4685                         return;
4686                 }
4687
4688                 AutomationList& alist = tmp->audio_region()->fade_in();
4689                 XMLNode &before = alist.get_state();
4690
4691                 tmp->audio_region()->set_fade_in_shape (shape);
4692                 
4693                 XMLNode &after = alist.get_state();
4694                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4695         }
4696
4697         commit_reversible_command ();
4698                 
4699 }
4700
4701 void
4702 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4703 {
4704         RegionSelection rs; 
4705
4706         get_regions_for_action (rs);
4707
4708         if (rs.empty()) {
4709                 return;
4710         }
4711
4712         begin_reversible_command (_("set fade out shape"));
4713
4714         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4715                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4716
4717                 if (!tmp) {
4718                         return;
4719                 }
4720
4721                 AutomationList& alist = tmp->audio_region()->fade_out();
4722                 XMLNode &before = alist.get_state();
4723
4724                 tmp->audio_region()->set_fade_out_shape (shape);
4725                 
4726                 XMLNode &after = alist.get_state();
4727                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4728         }
4729
4730         commit_reversible_command ();
4731 }
4732
4733 void
4734 Editor::set_fade_in_active (bool yn)
4735 {
4736         RegionSelection rs; 
4737
4738         get_regions_for_action (rs);
4739
4740         if (rs.empty()) {
4741                 return;
4742         }
4743
4744         begin_reversible_command (_("set fade in active"));
4745
4746         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4747                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4748
4749                 if (!tmp) {
4750                         return;
4751                 }
4752
4753
4754                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4755
4756                 XMLNode &before = ar->get_state();
4757
4758                 ar->set_fade_in_active (yn);
4759                 
4760                 XMLNode &after = ar->get_state();
4761                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4762         }
4763
4764         commit_reversible_command ();
4765 }
4766
4767 void
4768 Editor::set_fade_out_active (bool yn)
4769 {
4770         RegionSelection rs; 
4771
4772         get_regions_for_action (rs);
4773
4774         if (rs.empty()) {
4775                 return;
4776         }
4777
4778         begin_reversible_command (_("set fade out active"));
4779
4780         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4781                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4782
4783                 if (!tmp) {
4784                         return;
4785                 }
4786
4787                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4788
4789                 XMLNode &before = ar->get_state();
4790
4791                 ar->set_fade_out_active (yn);
4792                 
4793                 XMLNode &after = ar->get_state();
4794                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4795         }
4796
4797         commit_reversible_command ();
4798 }
4799
4800
4801 /** Update crossfade visibility after its configuration has been changed */
4802 void
4803 Editor::update_xfade_visibility ()
4804 {
4805         _xfade_visibility = Config->get_xfades_visible ();
4806         
4807         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4808                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4809                 if (v) {
4810                         if (_xfade_visibility) {
4811                                 v->show_all_xfades ();
4812                         } else {
4813                                 v->hide_all_xfades ();
4814                         }
4815                 }
4816         }
4817 }
4818
4819 void
4820 Editor::set_edit_point ()
4821 {
4822         nframes64_t where;
4823         bool ignored;
4824
4825         if (!mouse_frame (where, ignored)) {
4826                 return;
4827         }
4828         
4829         snap_to (where);
4830
4831         if (selection->markers.empty()) {
4832                 
4833                 mouse_add_new_marker (where);
4834
4835         } else {
4836                 bool ignored;
4837
4838                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4839
4840                 if (loc) {
4841                         loc->move_to (where);
4842                 }
4843         }
4844 }
4845
4846 void
4847 Editor::set_playhead_cursor ()
4848 {
4849         if (entered_marker) {
4850                 session->request_locate (entered_marker->position(), session->transport_rolling());
4851         } else {
4852                 nframes64_t where;
4853                 bool ignored;
4854
4855                 if (!mouse_frame (where, ignored)) {
4856                         return;
4857                 }
4858                         
4859                 snap_to (where);
4860                 
4861                 if (session) {
4862                         session->request_locate (where, session->transport_rolling());
4863                 }
4864         }
4865 }
4866
4867 void
4868 Editor::split ()
4869 {
4870         RegionSelection rs; 
4871
4872         get_regions_for_action (rs);
4873
4874         nframes64_t where = get_preferred_edit_position();
4875
4876         if (rs.empty()) {
4877                 return;
4878         }
4879
4880         split_regions_at (where, rs);
4881 }
4882
4883 void
4884 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4885 {
4886         if (entered_track && mouse_mode == MouseObject) {
4887                 if (!selection->tracks.empty()) {
4888                         if (!selection->selected (entered_track)) {
4889                                 selection->add (entered_track);
4890                         }
4891                 } else {
4892                         /* there is no selection, but this operation requires/prefers selected objects */
4893
4894                         if (op_really_wants_one_track_if_none_are_selected) {
4895                                 selection->set (entered_track);
4896                         }
4897                 }
4898         }
4899 }
4900
4901 void
4902 Editor::trim_region_front ()
4903 {
4904         trim_region (true);
4905 }
4906
4907 void
4908 Editor::trim_region_back ()
4909 {
4910         trim_region (false);
4911 }
4912
4913 void
4914 Editor::trim_region (bool front)
4915 {
4916         nframes64_t where = get_preferred_edit_position();
4917         RegionSelection rs;
4918
4919         get_regions_for_action (rs);
4920
4921         if (rs.empty()) {
4922                 return;
4923         }
4924
4925         begin_reversible_command (front ? _("trim front") : _("trim back"));
4926
4927         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4928                 if (!(*i)->region()->locked()) {
4929                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4930                         XMLNode &before = pl->get_state();
4931                         if (front) {
4932                                 (*i)->region()->trim_front (where, this);       
4933                         } else {
4934                                 (*i)->region()->trim_end (where, this); 
4935                         }
4936                         XMLNode &after = pl->get_state();
4937                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4938                 }
4939         }
4940
4941         commit_reversible_command ();
4942 }
4943
4944 struct EditorOrderRouteSorter {
4945     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4946             /* use of ">" forces the correct sort order */
4947             return a->order_key ("editor") < b->order_key ("editor");
4948     }
4949 };
4950
4951 void
4952 Editor::select_next_route()
4953 {
4954         if (selection->tracks.empty()) {
4955                 selection->set (track_views.front());
4956                 return;
4957         }
4958
4959         TimeAxisView* current = selection->tracks.front();
4960
4961         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4962                 if (*i == current) {
4963                         ++i;
4964                         if (i != track_views.end()) {
4965                                 selection->set (*i);
4966                         } else {
4967                                 selection->set (*(track_views.begin()));
4968                         }
4969                         break;
4970                 }
4971         }
4972 }
4973
4974 void
4975 Editor::select_prev_route()
4976 {
4977         if (selection->tracks.empty()) {
4978                 selection->set (track_views.front());
4979                 return;
4980         }
4981
4982         TimeAxisView* current = selection->tracks.front();
4983
4984         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4985                 if (*i == current) {
4986                         ++i;
4987                         if (i != track_views.rend()) {
4988                                 selection->set (*i);
4989                         } else {
4990                                 selection->set (*(track_views.rbegin()));
4991                         }
4992                         break;
4993                 }
4994         }
4995 }
4996
4997 void
4998 Editor::set_loop_from_selection (bool play)
4999 {
5000         if (session == 0 || selection->time.empty()) {
5001                 return;
5002         }
5003
5004         nframes_t start = selection->time[clicked_selection].start;
5005         nframes_t end = selection->time[clicked_selection].end;
5006         
5007         set_loop_range (start, end,  _("set loop range from selection"));
5008
5009         if (play) {
5010                 session->request_play_loop (true);
5011                 session->request_locate (start, true);
5012         }
5013 }
5014
5015 void
5016 Editor::set_loop_from_edit_range (bool play)
5017 {
5018         if (session == 0) {
5019                 return;
5020         }
5021
5022         nframes64_t start;
5023         nframes64_t end;
5024         
5025         if (!get_edit_op_range (start, end)) {
5026                 return;
5027         }
5028
5029         set_loop_range (start, end,  _("set loop range from edit range"));
5030
5031         if (play) {
5032                 session->request_play_loop (true);
5033                 session->request_locate (start, true);
5034         }
5035 }
5036
5037 void
5038 Editor::set_loop_from_region (bool play)
5039 {
5040         nframes64_t start = max_frames;
5041         nframes64_t end = 0;
5042
5043         RegionSelection rs; 
5044
5045         get_regions_for_action (rs);
5046
5047         if (rs.empty()) {
5048                 return;
5049         }
5050
5051         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5052                 if ((*i)->region()->position() < start) {
5053                         start = (*i)->region()->position();
5054                 }
5055                 if ((*i)->region()->last_frame() + 1 > end) {
5056                         end = (*i)->region()->last_frame() + 1;
5057                 }
5058         }
5059
5060         set_loop_range (start, end, _("set loop range from region"));
5061
5062         if (play) {
5063                 session->request_play_loop (true);
5064                 session->request_locate (start, true);
5065         }
5066 }
5067
5068 void
5069 Editor::set_punch_from_selection ()
5070 {
5071         if (session == 0 || selection->time.empty()) {
5072                 return;
5073         }
5074
5075         nframes_t start = selection->time[clicked_selection].start;
5076         nframes_t end = selection->time[clicked_selection].end;
5077         
5078         set_punch_range (start, end,  _("set punch range from selection"));
5079 }
5080
5081 void
5082 Editor::set_punch_from_edit_range ()
5083 {
5084         if (session == 0) {
5085                 return;
5086         }
5087
5088         nframes64_t start;
5089         nframes64_t end;
5090         
5091         if (!get_edit_op_range (start, end)) {
5092                 return;
5093         }
5094
5095         set_punch_range (start, end,  _("set punch range from edit range"));
5096 }
5097
5098 void
5099 Editor::set_punch_from_region ()
5100 {
5101         nframes64_t start = max_frames;
5102         nframes64_t end = 0;
5103
5104         RegionSelection rs; 
5105
5106         get_regions_for_action (rs);
5107
5108         if (rs.empty()) {
5109                 return;
5110         }
5111
5112         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5113                 if ((*i)->region()->position() < start) {
5114                         start = (*i)->region()->position();
5115                 }
5116                 if ((*i)->region()->last_frame() + 1 > end) {
5117                         end = (*i)->region()->last_frame() + 1;
5118                 }
5119         }
5120
5121         set_punch_range (start, end, _("set punch range from region"));
5122 }
5123
5124 void
5125 Editor::pitch_shift_regions ()
5126 {
5127         RegionSelection rs; 
5128
5129         get_regions_for_action (rs);
5130         
5131         if (rs.empty()) {
5132                 return;
5133         }
5134
5135         pitch_shift (rs, 1.2);
5136 }
5137         
5138 void
5139 Editor::use_region_as_bar ()
5140 {
5141         if (!session) {
5142                 return;
5143         }
5144
5145         RegionSelection rs; 
5146
5147         get_regions_for_action (rs);
5148
5149         if (rs.empty()) {
5150                 return;
5151         }
5152
5153         RegionView* rv = rs.front();
5154
5155         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5156 }
5157
5158 void
5159 Editor::use_range_as_bar ()
5160 {
5161         nframes64_t start, end;
5162         if (get_edit_op_range (start, end)) {
5163                 define_one_bar (start, end);
5164         }
5165 }
5166
5167 void
5168 Editor::define_one_bar (nframes64_t start, nframes64_t end)
5169 {
5170         nframes64_t length = end - start;
5171         
5172         const Meter& m (session->tempo_map().meter_at (start));
5173
5174         /* length = 1 bar */
5175
5176         /* now we want frames per beat.
5177            we have frames per bar, and beats per bar, so ...
5178         */
5179
5180         double frames_per_beat = length / m.beats_per_bar();
5181         
5182         /* beats per minute = */
5183
5184         double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
5185
5186         /* now decide whether to:
5187
5188             (a) set global tempo 
5189             (b) add a new tempo marker
5190
5191         */
5192
5193         const TempoSection& t (session->tempo_map().tempo_section_at (start));
5194
5195         bool do_global = false;
5196
5197         if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) {
5198                 
5199                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5200                    at the start, or create a new marker
5201                 */
5202
5203                 vector<string> options;
5204                 options.push_back (_("Cancel"));
5205                 options.push_back (_("Add new marker"));
5206                 options.push_back (_("Set global tempo"));
5207                 Choice c (_("Do you want to set the global tempo or add new tempo marker?"),
5208                           options);
5209                 c.set_default_response (2);
5210
5211                 switch (c.run()) {
5212                 case 0:
5213                         return;
5214
5215                 case 2:
5216                         do_global = true;
5217                         break;
5218
5219                 default:
5220                         do_global = false;
5221                 }
5222
5223         } else {
5224
5225                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5226                    if the marker is at the region starter, change it, otherwise add
5227                    a new tempo marker 
5228                 */
5229         }
5230
5231         begin_reversible_command (_("set tempo from region"));
5232         XMLNode& before (session->tempo_map().get_state());
5233
5234         if (do_global) {
5235                 session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5236         } else if (t.frame() == start) {
5237                 session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5238         } else {
5239                 session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5240         }
5241
5242         XMLNode& after (session->tempo_map().get_state());
5243
5244         session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
5245         commit_reversible_command ();
5246 }
5247
5248 void
5249 Editor::split_region_at_transients ()
5250 {
5251         AnalysisFeatureList positions;
5252
5253         if (!session) {
5254                 return;
5255         }
5256
5257         RegionSelection rs; 
5258
5259         get_regions_for_action (rs);
5260
5261         if (rs.empty()) {
5262                 return;
5263         }
5264
5265         session->begin_reversible_command (_("split regions"));
5266
5267         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5268
5269                 RegionSelection::iterator tmp;
5270
5271                 tmp = i;
5272                 ++tmp;
5273
5274                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5275                 
5276                 if (ar && (ar->get_transients (positions) == 0)) {
5277                         split_region_at_points ((*i)->region(), positions, true);
5278                         positions.clear ();
5279                 }
5280                 
5281                 i = tmp;
5282         }
5283
5284         session->commit_reversible_command ();
5285
5286 }
5287
5288 void
5289 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret)
5290 {
5291         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
5292         bool use_rhythmic_rodent = false;
5293
5294         if (!ar) {
5295                 return;
5296         }
5297         
5298         boost::shared_ptr<Playlist> pl = ar->playlist();
5299         
5300         if (!pl) {
5301                 return;
5302         }
5303         
5304         if (positions.empty()) {
5305                 return;
5306         }
5307
5308
5309         if (positions.size() > 20) {
5310                 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);
5311                 MessageDialog msg (msgstr,
5312                                    false,
5313                                    Gtk::MESSAGE_INFO,
5314                                    Gtk::BUTTONS_OK_CANCEL);
5315
5316                 if (can_ferret) {
5317                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5318                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5319                 } else {
5320                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
5321                 }
5322
5323                 msg.set_title (_("Excessive split?"));
5324                 msg.present ();
5325
5326                 int response = msg.run();
5327                 msg.hide ();
5328                 switch (response) {
5329                 case RESPONSE_OK:
5330                         break;
5331                 case RESPONSE_APPLY:
5332                         use_rhythmic_rodent = true;
5333                         break;
5334                 default:
5335                         return;
5336                 }
5337         }
5338         
5339         if (use_rhythmic_rodent) {
5340                 show_rhythm_ferret ();
5341                 return;
5342         }
5343
5344         AnalysisFeatureList::const_iterator x;  
5345         
5346         nframes64_t pos = ar->position();
5347
5348         XMLNode& before (pl->get_state());
5349         
5350         x = positions.begin();
5351         
5352         while (x != positions.end()) {
5353                 if ((*x) > pos) {
5354                         break;
5355                 }
5356                 ++x;
5357         }
5358         
5359         if (x == positions.end()) {
5360                 return;
5361         }
5362         
5363         pl->freeze ();
5364         pl->remove_region (ar);
5365         
5366         while (x != positions.end()) {
5367                 
5368                 /* file start = original start + how far we from the initial position ? 
5369                  */
5370                 
5371                 nframes64_t file_start = ar->start() + (pos - ar->position());
5372
5373                 /* length = next position - current position
5374                  */
5375                 
5376                 nframes64_t len = (*x) - pos;
5377
5378                 /* XXX we do we really want to allow even single-sample regions?
5379                    shouldn't we have some kind of lower limit on region size?
5380                 */
5381
5382                 if (len <= 0) {
5383                         break;
5384                 }
5385                 
5386                 string new_name;
5387                 
5388                 if (session->region_name (new_name, ar->name())) {
5389                         break;
5390                 }
5391                 
5392                 /* do NOT announce new regions 1 by one, just wait till they are all done */
5393
5394                 boost::shared_ptr<Region> r = RegionFactory::create (ar->get_sources(), file_start, len, new_name, 0, Region::DefaultFlags, false);
5395                 pl->add_region (r, pos);
5396
5397                 pos += len;
5398                 ++x;
5399
5400                 if (*x > ar->last_frame()) {
5401
5402                         /* add final fragment */
5403                         
5404                         file_start = ar->start() + (pos - ar->position());
5405                         len = ar->last_frame() - pos;
5406
5407                         boost::shared_ptr<Region> r = RegionFactory::create (ar->get_sources(), file_start, len, new_name, 0, Region::DefaultFlags);
5408                         pl->add_region (r, pos);
5409
5410                         break;
5411                 }
5412         } 
5413
5414         pl->thaw ();
5415
5416         XMLNode& after (pl->get_state());
5417         
5418         session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
5419 }
5420
5421 void
5422 Editor::tab_to_transient (bool forward)
5423 {
5424         AnalysisFeatureList positions;
5425
5426         if (!session) {
5427                 return;
5428         }
5429
5430         nframes64_t pos = session->audible_frame ();
5431
5432         if (!selection->tracks.empty()) {
5433
5434                 for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
5435
5436                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
5437
5438                         if (rtv) {
5439                                 boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
5440                                 if (ds) {
5441                                         boost::shared_ptr<Playlist> pl = rtv->get_diskstream()->playlist ();
5442                                         if (pl) {
5443                                                 nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1);
5444                                                 
5445                                                 if (result >= 0) {
5446                                                         positions.push_back (result);
5447                                                 }
5448                                         }
5449                                 }
5450                         }
5451                 }
5452
5453         } else {
5454                 
5455                 RegionSelection rs; 
5456
5457                 get_regions_for_action (rs);
5458         
5459                 if (rs.empty()) {
5460                         return;
5461                 }
5462                 
5463                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5464                         (*r)->region()->get_transients (positions);
5465                 }
5466         }
5467
5468         TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0);
5469
5470         if (forward) {
5471                 AnalysisFeatureList::iterator x;
5472
5473                 for (x = positions.begin(); x != positions.end(); ++x) {
5474                         if ((*x) > pos) {
5475                                 break;
5476                         }
5477                 }
5478
5479                 if (x != positions.end ()) {
5480                         session->request_locate (*x);
5481                 }
5482
5483         } else {
5484                 AnalysisFeatureList::reverse_iterator x;
5485
5486                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
5487                         if ((*x) < pos) {
5488                                 break;
5489                         }
5490                 }
5491
5492                 if (x != positions.rend ()) {
5493                         session->request_locate (*x);
5494                 }
5495         }
5496 }
5497
5498 void
5499 Editor::playhead_forward_to_grid ()
5500 {
5501         if (!session) return;
5502         nframes64_t pos = playhead_cursor->current_frame;
5503         if (pos < max_frames) {
5504                 pos++;
5505                 snap_to_internal (pos, 1, false);
5506                 session->request_locate (pos);
5507         }
5508 }
5509
5510
5511 void
5512 Editor::playhead_backward_to_grid ()
5513 {
5514         if (!session) return;
5515         nframes64_t pos = playhead_cursor->current_frame;
5516         if (pos > 1) {
5517                 pos--;
5518                 snap_to_internal (pos, -1, false);
5519                 session->request_locate (pos);
5520         }
5521 }
5522
5523 void
5524 Editor::set_track_height (uint32_t h)
5525 {
5526         TrackSelection& ts (selection->tracks);
5527
5528         if (ts.empty()) {
5529                 return;
5530         }
5531
5532         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5533                 (*x)->set_height (h);
5534         }
5535 }
5536
5537 void
5538 Editor::set_track_height_largest ()
5539 {
5540         set_track_height (TimeAxisView::hLargest);
5541 }
5542 void
5543 Editor::set_track_height_large ()
5544 {
5545         set_track_height (TimeAxisView::hLarge);
5546 }
5547 void
5548 Editor::set_track_height_larger ()
5549 {
5550         set_track_height (TimeAxisView::hLarger);
5551 }
5552 void
5553 Editor::set_track_height_normal ()
5554 {
5555         set_track_height (TimeAxisView::hNormal);
5556 }
5557 void
5558 Editor::set_track_height_smaller ()
5559 {
5560         set_track_height (TimeAxisView::hSmaller);
5561 }
5562 void
5563 Editor::set_track_height_small ()
5564 {
5565         set_track_height (TimeAxisView::hSmall);
5566 }
5567
5568 void
5569 Editor::toggle_tracks_active ()
5570 {
5571         TrackSelection& ts (selection->tracks);
5572         bool first = true;
5573         bool target = false;
5574
5575         if (ts.empty()) {
5576                 return;
5577         }
5578
5579         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5580                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
5581
5582                 if (rtv) {
5583                         if (first) {
5584                                 target = !rtv->_route->active();
5585                                 first = false;
5586                         }
5587                         rtv->_route->set_active (target);
5588                 }
5589         }
5590 }
5591
5592 void
5593 Editor::remove_tracks ()
5594 {
5595         TrackSelection& ts (selection->tracks);
5596
5597         if (ts.empty()) {
5598                 return;
5599         }
5600
5601         vector<string> choices;
5602         string prompt;
5603         int ntracks = 0;
5604         int nbusses = 0;
5605         const char* trackstr;
5606         const char* busstr;
5607         vector<boost::shared_ptr<Route> > routes;
5608
5609         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5610                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
5611                 if (rtv) {
5612                         if (rtv->is_track()) {
5613                                 ntracks++;
5614                         } else {
5615                                 nbusses++;
5616                         }
5617                 }
5618                 routes.push_back (rtv->_route);
5619         }
5620         
5621         if (ntracks + nbusses == 0) {
5622                 return;
5623         }
5624
5625         if (ntracks > 1) {
5626                 trackstr = _("tracks");
5627         } else {
5628                 trackstr = _("track");
5629         }
5630
5631         if (nbusses > 1) {
5632                 busstr = _("busses");
5633         } else {
5634                 busstr = _("bus");
5635         }
5636
5637         if (ntracks) {
5638                 if (nbusses) {
5639                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
5640                                                     "(You may also lose the playlists associated with the %2)\n\n"
5641                                                     "This action cannot be undone!"),
5642                                                   ntracks, trackstr, nbusses, busstr);
5643                 } else {
5644                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
5645                                                     "(You may also lose the playlists associated with the %2)\n\n"
5646                                                     "This action cannot be undone!"),
5647                                                   ntracks, trackstr);
5648                 }
5649         } else if (nbusses) {
5650                 prompt  = string_compose (_("Do you really want to remove %1 %2?"),
5651                                           nbusses, busstr);
5652         }
5653
5654         choices.push_back (_("No, do nothing."));
5655         if (ntracks + nbusses > 1) {
5656                 choices.push_back (_("Yes, remove them."));
5657         } else {
5658                 choices.push_back (_("Yes, remove it."));
5659         }
5660
5661         Choice prompter (prompt, choices);
5662
5663         if (prompter.run () != 1) {
5664                 return;
5665         }
5666
5667         for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
5668                 session->remove_route (*x);
5669         }
5670 }
5671
5672 void
5673 Editor::set_waveform_scale (WaveformScale ws)
5674 {
5675         TrackSelection& ts (selection->tracks);
5676
5677         if (ts.empty()) {
5678                 return;
5679         }
5680
5681         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5682                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (*x);
5683                 if (atv) {
5684                         atv->set_waveform_scale (ws);
5685                 }
5686         }
5687 }       
5688
5689 void
5690 Editor::do_insert_time ()
5691 {
5692         if (selection->tracks.empty()) {
5693                 return;
5694         }
5695
5696         nframes64_t pos = get_preferred_edit_position ();
5697         ArdourDialog d (*this, _("Insert Time"));
5698         VButtonBox button_box;
5699         VBox option_box;
5700         RadioButtonGroup group;
5701         RadioButton leave_button (group, _("Stay in position"));
5702         RadioButton move_button (group, _("Move"));
5703         RadioButton split_button (group, _("Split & Later Section Moves"));
5704         Label intersect_option_label (_("Intersected regions should:"));
5705         ToggleButton glue_button (_("Move Glued Regions"));
5706         AudioClock clock ("insertTimeClock", true, X_("InsertTimeClock"), true, true, true);
5707         HBox clock_box;
5708
5709         clock.set (0);
5710         clock.set_session (session);
5711         clock.set_bbt_reference (pos);
5712
5713         clock_box.pack_start (clock, false, true);
5714
5715         option_box.set_spacing (6);
5716         option_box.pack_start (intersect_option_label, false, false);
5717         option_box.pack_start (button_box, false, false);
5718         option_box.pack_start (glue_button, false, false);
5719
5720         button_box.pack_start (leave_button, false, false);
5721         button_box.pack_start (move_button, false, false);
5722         button_box.pack_start (split_button, false, false);
5723                                       
5724         d.get_vbox()->set_border_width (12);
5725         d.get_vbox()->pack_start (clock_box, false, false);
5726         d.get_vbox()->pack_start (option_box, false, false);
5727         
5728         leave_button.show ();
5729         move_button.show ();
5730         split_button.show ();
5731         intersect_option_label.show ();
5732         option_box.show ();
5733         button_box.show ();
5734         glue_button.show ();
5735         clock.show_all();
5736         clock_box.show ();
5737
5738         d.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
5739         d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
5740         d.show ();
5741
5742         int response = d.run ();
5743
5744         if (response != RESPONSE_OK) {
5745                 return;
5746         }
5747         
5748         nframes_t distance = clock.current_duration (pos);
5749
5750         if (distance == 0) {
5751                 return;
5752         }
5753
5754         InsertTimeOption opt;
5755
5756         if (leave_button.get_active()) {
5757                 opt = LeaveIntersected;
5758         } else if (move_button.get_active()) {
5759                 opt = MoveIntersected;
5760         } else {
5761                 opt = SplitIntersected;
5762         }
5763
5764         insert_time (pos, distance, opt, glue_button.get_active());
5765 }
5766                                 
5767 void
5768 Editor::insert_time (nframes64_t pos, nframes64_t frames, InsertTimeOption opt, bool ignore_music_glue)
5769 {
5770         bool commit = false;
5771
5772         if (Config->get_edit_mode() == Lock) {
5773                 return;
5774         }
5775
5776         begin_reversible_command (_("insert time"));
5777
5778         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
5779                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
5780                 
5781                 if (!pl) {
5782                         continue;
5783                 }
5784
5785                 XMLNode &before = pl->get_state();
5786
5787                 if (opt == SplitIntersected) {
5788                         pl->split (pos);
5789                 }
5790                 
5791                 pl->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
5792
5793                 XMLNode &after = pl->get_state();
5794
5795                 session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
5796                 commit = true;
5797         }
5798
5799         if (commit) {
5800                 commit_reversible_command ();
5801         }
5802 }