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