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