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