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