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