6b48311b159bf04e1cc8fcb2f5650b9bca0bfefd
[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         current_interthread_info->process_thread.get_buffers ();
3397         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3398         current_interthread_info->done = true;
3399         current_interthread_info->process_thread.drop_buffers();
3400         return 0;
3401 }
3402
3403 void
3404 Editor::freeze_route ()
3405 {
3406         if (!_session) {
3407                 return;
3408         }
3409
3410         /* stop transport before we start. this is important */
3411
3412         _session->request_transport_speed (0.0);
3413         
3414         /* wait for just a little while, because the above call is asynchronous */
3415
3416         ::usleep (250000);
3417
3418         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3419                 return;
3420         }
3421
3422         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3423                 MessageDialog d (
3424                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3425                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3426                         );
3427                 d.set_title (_("Cannot freeze"));
3428                 d.run ();
3429                 return;
3430         }
3431
3432         if (clicked_routeview->track()->has_external_redirects()) {
3433                 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"
3434                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3435                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3436
3437                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3438                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3439                 d.set_title (_("Freeze Limits"));
3440
3441                 int response = d.run ();
3442
3443                 switch (response) {
3444                 case Gtk::RESPONSE_CANCEL:
3445                         return;
3446                 default:
3447                         break;
3448                 }
3449         }
3450
3451         InterThreadInfo itt;
3452         current_interthread_info = &itt;
3453
3454         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3455
3456         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3457
3458         set_canvas_cursor (_cursors->wait);
3459
3460         while (!itt.done && !itt.cancel) {
3461                 gtk_main_iteration ();
3462         }
3463
3464         current_interthread_info = 0;
3465         set_canvas_cursor (current_canvas_cursor);
3466 }
3467
3468 void
3469 Editor::bounce_range_selection (bool replace, bool enable_processing)
3470 {
3471         if (selection->time.empty()) {
3472                 return;
3473         }
3474
3475         TrackSelection views = selection->tracks;
3476
3477         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3478
3479                 if (enable_processing) {
3480
3481                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3482
3483                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3484                                 MessageDialog d (
3485                                         _("You can't perform this operation because the processing of the signal "
3486                                           "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3487                                           "You can do this without processing, which is a different operation.")
3488                                         );
3489                                 d.set_title (_("Cannot bounce"));
3490                                 d.run ();
3491                                 return;
3492                         }
3493                 }
3494         }
3495
3496         framepos_t start = selection->time[clicked_selection].start;
3497         framepos_t end = selection->time[clicked_selection].end;
3498         framepos_t cnt = end - start + 1;
3499
3500         begin_reversible_command (_("bounce range"));
3501
3502         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3503
3504                 RouteTimeAxisView* rtv;
3505
3506                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3507                         continue;
3508                 }
3509
3510                 boost::shared_ptr<Playlist> playlist;
3511
3512                 if ((playlist = rtv->playlist()) == 0) {
3513                         return;
3514                 }
3515
3516                 InterThreadInfo itt;
3517
3518                 playlist->clear_changes ();
3519                 playlist->clear_owned_changes ();
3520
3521                 boost::shared_ptr<Region> r;
3522
3523                 if (enable_processing) {
3524                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3525                 } else {
3526                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3527                 }
3528
3529                 if (!r) {
3530                         continue;
3531                 }
3532
3533                 if (replace) {
3534                         list<AudioRange> ranges;
3535                         ranges.push_back (AudioRange (start, start+cnt, 0));
3536                         playlist->cut (ranges); // discard result
3537                         playlist->add_region (r, start);
3538                 }
3539
3540                 vector<Command*> cmds;
3541                 playlist->rdiff (cmds);
3542                 _session->add_commands (cmds);
3543
3544                 _session->add_command (new StatefulDiffCommand (playlist));
3545         }
3546
3547         commit_reversible_command ();
3548 }
3549
3550 /** Delete selected regions, automation points or a time range */
3551 void
3552 Editor::delete_ ()
3553 {
3554         cut_copy (Delete);
3555 }
3556
3557 /** Cut selected regions, automation points or a time range */
3558 void
3559 Editor::cut ()
3560 {
3561         cut_copy (Cut);
3562 }
3563
3564 /** Copy selected regions, automation points or a time range */
3565 void
3566 Editor::copy ()
3567 {
3568         cut_copy (Copy);
3569 }
3570
3571
3572 /** @return true if a Cut, Copy or Clear is possible */
3573 bool
3574 Editor::can_cut_copy () const
3575 {
3576         switch (current_mouse_mode()) {
3577
3578         case MouseObject:
3579                 if (!selection->regions.empty() || !selection->points.empty()) {
3580                         return true;
3581                 }
3582                 break;
3583
3584         case MouseRange:
3585                 if (!selection->time.empty()) {
3586                         return true;
3587                 }
3588                 break;
3589
3590         default:
3591                 break;
3592         }
3593
3594         return false;
3595 }
3596
3597
3598 /** Cut, copy or clear selected regions, automation points or a time range.
3599  * @param op Operation (Cut, Copy or Clear)
3600  */
3601 void
3602 Editor::cut_copy (CutCopyOp op)
3603 {
3604         /* only cancel selection if cut/copy is successful.*/
3605
3606         string opname;
3607
3608         switch (op) {
3609         case Delete:
3610                 opname = _("delete");
3611                 break;
3612         case Cut:
3613                 opname = _("cut");
3614                 break;
3615         case Copy:
3616                 opname = _("copy");
3617                 break;
3618         case Clear:
3619                 opname = _("clear");
3620                 break;
3621         }
3622
3623         /* if we're deleting something, and the mouse is still pressed,
3624            the thing we started a drag for will be gone when we release
3625            the mouse button(s). avoid this. see part 2 at the end of
3626            this function.
3627         */
3628
3629         if (op == Delete || op == Cut || op == Clear) {
3630                 if (_drags->active ()) {
3631                         _drags->abort ();
3632                 }
3633         }
3634
3635         cut_buffer->clear ();
3636
3637         if (entered_marker) {
3638
3639                 /* cut/delete op while pointing at a marker */
3640
3641                 bool ignored;
3642                 Location* loc = find_location_from_marker (entered_marker, ignored);
3643
3644                 if (_session && loc) {
3645                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3646                 }
3647
3648                 _drags->abort ();
3649                 return;
3650         }
3651
3652         if (internal_editing()) {
3653
3654                 switch (current_mouse_mode()) {
3655                 case MouseObject:
3656                 case MouseRange:
3657                         cut_copy_midi (op);
3658                         break;
3659                 default:
3660                         break;
3661                 }
3662
3663         } else {
3664
3665                 RegionSelection rs;
3666
3667                 /* we only want to cut regions if some are selected */
3668
3669                 if (doing_object_stuff()) {
3670                         rs = get_regions_from_selection ();
3671                         if (!rs.empty() || !selection->points.empty()) {
3672
3673                                 begin_reversible_command (opname + _(" objects"));
3674
3675                                 if (!rs.empty()) {
3676                                         cut_copy_regions (op, rs);
3677
3678                                         if (op == Cut || op == Delete) {
3679                                                 selection->clear_regions ();
3680                                         }
3681                                 }
3682
3683                                 if (!selection->points.empty()) {
3684                                         cut_copy_points (op);
3685
3686                                         if (op == Cut || op == Delete) {
3687                                                 selection->clear_points ();
3688                                         }
3689                                 }
3690                                 commit_reversible_command ();
3691                                 goto out;
3692                         }
3693                         if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3694                                 /* don't cause suprises */
3695                                 goto out;
3696                         }
3697                 }
3698
3699                 if (doing_range_stuff()) {
3700                         if (selection->time.empty()) {
3701                                 framepos_t start, end;
3702                                 if (!get_edit_op_range (start, end)) {
3703                                         return;
3704                                 }
3705                                 selection->set (start, end);
3706                         }
3707
3708                         begin_reversible_command (opname + _(" range"));
3709                         cut_copy_ranges (op);
3710                         commit_reversible_command ();
3711
3712                         if (op == Cut || op == Delete) {
3713                                 selection->clear_time ();
3714                         }
3715                 }
3716         }
3717
3718   out:
3719         if (op == Delete || op == Cut || op == Clear) {
3720                 _drags->abort ();
3721         }
3722 }
3723
3724 /** Cut, copy or clear selected automation points.
3725  * @param op Operation (Cut, Copy or Clear)
3726  */
3727 void
3728 Editor::cut_copy_points (CutCopyOp op)
3729 {
3730         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3731
3732                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
3733                 _last_cut_copy_source_track = atv;
3734
3735                 if (atv) {
3736                         atv->cut_copy_clear_objects (selection->points, op);
3737                 }
3738         }
3739 }
3740
3741 /** Cut, copy or clear selected automation points.
3742  * @param op Operation (Cut, Copy or Clear)
3743  */
3744 void
3745 Editor::cut_copy_midi (CutCopyOp op)
3746 {
3747         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3748                 MidiRegionView* mrv = *i;
3749                 mrv->cut_copy_clear (op);
3750         }
3751 }
3752
3753
3754
3755 struct lt_playlist {
3756     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3757             return a.playlist < b.playlist;
3758     }
3759 };
3760
3761 struct PlaylistMapping {
3762     TimeAxisView* tv;
3763     boost::shared_ptr<Playlist> pl;
3764
3765     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3766 };
3767
3768 /** Remove `clicked_regionview' */
3769 void
3770 Editor::remove_clicked_region ()
3771 {
3772         if (clicked_routeview == 0 || clicked_regionview == 0) {
3773                 return;
3774         }
3775
3776         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3777
3778         begin_reversible_command (_("remove region"));
3779         playlist->clear_changes ();
3780         playlist->clear_owned_changes ();
3781         playlist->remove_region (clicked_regionview->region());
3782
3783         /* We might have removed regions, which alters other regions' layering_index,
3784            so we need to do a recursive diff here.
3785         */
3786         vector<Command*> cmds;
3787         playlist->rdiff (cmds);
3788         _session->add_commands (cmds);
3789         
3790         _session->add_command(new StatefulDiffCommand (playlist));
3791         commit_reversible_command ();
3792 }
3793
3794
3795 /** Remove the selected regions */
3796 void
3797 Editor::remove_selected_regions ()
3798 {
3799         RegionSelection rs = get_regions_from_selection_and_entered ();
3800
3801         if (!_session || rs.empty()) {
3802                 return;
3803         }
3804
3805         begin_reversible_command (_("remove region"));
3806
3807         list<boost::shared_ptr<Region> > regions_to_remove;
3808
3809         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3810                 // we can't just remove the region(s) in this loop because
3811                 // this removes them from the RegionSelection, and they thus
3812                 // disappear from underneath the iterator, and the ++i above
3813                 // SEGVs in a puzzling fashion.
3814
3815                 // so, first iterate over the regions to be removed from rs and
3816                 // add them to the regions_to_remove list, and then
3817                 // iterate over the list to actually remove them.
3818
3819                 regions_to_remove.push_back ((*i)->region());
3820         }
3821
3822         vector<boost::shared_ptr<Playlist> > playlists;
3823
3824         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3825
3826                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3827
3828                 if (!playlist) {
3829                         // is this check necessary?
3830                         continue;
3831                 }
3832
3833                 /* get_regions_from_selection_and_entered() guarantees that
3834                    the playlists involved are unique, so there is no need
3835                    to check here.
3836                 */
3837
3838                 playlists.push_back (playlist);
3839
3840                 playlist->clear_changes ();
3841                 playlist->clear_owned_changes ();
3842                 playlist->freeze ();
3843                 playlist->remove_region (*rl);
3844         }
3845
3846         vector<boost::shared_ptr<Playlist> >::iterator pl;
3847
3848         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3849                 (*pl)->thaw ();
3850
3851                 /* We might have removed regions, which alters other regions' layering_index,
3852                    so we need to do a recursive diff here.
3853                 */
3854                 vector<Command*> cmds;
3855                 (*pl)->rdiff (cmds);
3856                 _session->add_commands (cmds);
3857                 
3858                 _session->add_command(new StatefulDiffCommand (*pl));
3859         }
3860
3861         commit_reversible_command ();
3862 }
3863
3864 /** Cut, copy or clear selected regions.
3865  * @param op Operation (Cut, Copy or Clear)
3866  */
3867 void
3868 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3869 {
3870         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3871            a map when we want ordered access to both elements. i think.
3872         */
3873
3874         vector<PlaylistMapping> pmap;
3875
3876         framepos_t first_position = max_framepos;
3877
3878         typedef set<boost::shared_ptr<Playlist> > FreezeList;
3879         FreezeList freezelist;
3880
3881         /* get ordering correct before we cut/copy */
3882
3883         rs.sort_by_position_and_track ();
3884
3885         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3886
3887                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3888
3889                 if (op == Cut || op == Clear || op == Delete) {
3890                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3891
3892                         if (pl) {
3893                                 FreezeList::iterator fl;
3894
3895                                 // only take state if this is a new playlist.
3896                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3897                                         if ((*fl) == pl) {
3898                                                 break;
3899                                         }
3900                                 }
3901
3902                                 if (fl == freezelist.end()) {
3903                                         pl->clear_changes();
3904                                         pl->clear_owned_changes ();
3905                                         pl->freeze ();
3906                                         freezelist.insert (pl);
3907                                 }
3908                         }
3909                 }
3910
3911                 TimeAxisView* tv = &(*x)->get_time_axis_view();
3912                 vector<PlaylistMapping>::iterator z;
3913
3914                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3915                         if ((*z).tv == tv) {
3916                                 break;
3917                         }
3918                 }
3919
3920                 if (z == pmap.end()) {
3921                         pmap.push_back (PlaylistMapping (tv));
3922                 }
3923         }
3924
3925         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3926
3927                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3928
3929                 if (!pl) {
3930                         /* region not yet associated with a playlist (e.g. unfinished
3931                            capture pass.
3932                         */
3933                         ++x;
3934                         continue;
3935                 }
3936
3937                 TimeAxisView& tv = (*x)->get_time_axis_view();
3938                 boost::shared_ptr<Playlist> npl;
3939                 RegionSelection::iterator tmp;
3940
3941                 tmp = x;
3942                 ++tmp;
3943
3944                 if (op != Delete) {
3945
3946                         vector<PlaylistMapping>::iterator z;
3947                         
3948                         for (z = pmap.begin(); z != pmap.end(); ++z) {
3949                                 if ((*z).tv == &tv) {
3950                                         break;
3951                                 }
3952                         }
3953                         
3954                         assert (z != pmap.end());
3955                         
3956                         if (!(*z).pl) {
3957                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
3958                                 npl->freeze();
3959                                 (*z).pl = npl;
3960                         } else {
3961                                 npl = (*z).pl;
3962                         }
3963                 }
3964
3965                 boost::shared_ptr<Region> r = (*x)->region();
3966                 boost::shared_ptr<Region> _xx;
3967
3968                 assert (r != 0);
3969
3970                 switch (op) {
3971                 case Delete:
3972                         pl->remove_region (r);
3973                         break;
3974                         
3975                 case Cut:
3976                         _xx = RegionFactory::create (r);
3977                         npl->add_region (_xx, r->position() - first_position);
3978                         pl->remove_region (r);
3979                         break;
3980
3981                 case Copy:
3982                         /* copy region before adding, so we're not putting same object into two different playlists */
3983                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
3984                         break;
3985
3986                 case Clear:
3987                         pl->remove_region (r);  
3988                         break;
3989                 }
3990
3991                 x = tmp;
3992         }
3993
3994         if (op != Delete) {
3995
3996                 list<boost::shared_ptr<Playlist> > foo;
3997                 
3998                 /* the pmap is in the same order as the tracks in which selected regions occured */
3999                 
4000                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4001                         if ((*i).pl) {
4002                                 (*i).pl->thaw();
4003                                 foo.push_back ((*i).pl);
4004                         }
4005                 }
4006                 
4007                 if (!foo.empty()) {
4008                         cut_buffer->set (foo);
4009                 }
4010                 
4011                 if (pmap.empty()) {
4012                         _last_cut_copy_source_track = 0;
4013                 } else {
4014                         _last_cut_copy_source_track = pmap.front().tv;
4015                 }
4016         }
4017
4018         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4019                 (*pl)->thaw ();
4020
4021                 /* We might have removed regions, which alters other regions' layering_index,
4022                    so we need to do a recursive diff here.
4023                 */
4024                 vector<Command*> cmds;
4025                 (*pl)->rdiff (cmds);
4026                 _session->add_commands (cmds);
4027                 
4028                 _session->add_command (new StatefulDiffCommand (*pl));
4029         }
4030 }
4031
4032 void
4033 Editor::cut_copy_ranges (CutCopyOp op)
4034 {
4035         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4036
4037         /* Sort the track selection now, so that it if is used, the playlists
4038            selected by the calls below to cut_copy_clear are in the order that
4039            their tracks appear in the editor.  This makes things like paste
4040            of ranges work properly.
4041         */
4042
4043         sort_track_selection (ts);
4044
4045         if (ts.empty()) {
4046                 if (!entered_track) {
4047                         return;
4048                 }
4049                 ts.push_back (entered_track);
4050         } 
4051
4052         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4053                 (*i)->cut_copy_clear (*selection, op);
4054         }
4055 }
4056
4057 void
4058 Editor::paste (float times, bool from_context)
4059 {
4060         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4061
4062         paste_internal (get_preferred_edit_position (false, from_context), times);
4063 }
4064
4065 void
4066 Editor::mouse_paste ()
4067 {
4068         framepos_t where;
4069         bool ignored;
4070
4071         if (!mouse_frame (where, ignored)) {
4072                 return;
4073         }
4074
4075         snap_to (where);
4076         paste_internal (where, 1);
4077 }
4078
4079 void
4080 Editor::paste_internal (framepos_t position, float times)
4081 {
4082         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4083
4084         if (internal_editing()) {
4085                 if (cut_buffer->midi_notes.empty()) {
4086                         return;
4087                 }
4088         } else {
4089                 if (cut_buffer->empty()) {
4090                         return;
4091                 }
4092         }
4093
4094         if (position == max_framepos) {
4095                 position = get_preferred_edit_position();
4096                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4097         }
4098
4099         TrackViewList ts;
4100         TrackViewList::iterator i;
4101         size_t nth;
4102
4103         /* get everything in the correct order */
4104
4105         if (!selection->tracks.empty()) {
4106                 /* there are some selected tracks, so paste to them */
4107                 ts = selection->tracks.filter_to_unique_playlists ();
4108                 sort_track_selection (ts);
4109         } else if (_last_cut_copy_source_track) {
4110                 /* otherwise paste to the track that the cut/copy came from;
4111                    see discussion in mantis #3333.
4112                 */
4113                 ts.push_back (_last_cut_copy_source_track);
4114         }
4115
4116         if (internal_editing ()) {
4117
4118                 /* undo/redo is handled by individual tracks/regions */
4119
4120                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4121
4122                         RegionSelection rs;
4123                         RegionSelection::iterator r;
4124                         MidiNoteSelection::iterator cb;
4125
4126                         get_regions_at (rs, position, ts);
4127
4128                         for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4129                              cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4130                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4131                                 if (mrv) {
4132                                         mrv->paste (position, times, **cb);
4133                                         ++cb;
4134                                 }
4135                         }
4136                 }
4137
4138         } else {
4139
4140                 /* we do redo (do you do voodoo?) */
4141
4142                 begin_reversible_command (Operations::paste);
4143
4144                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4145                         (*i)->paste (position, times, *cut_buffer, nth);
4146                 }
4147
4148                 commit_reversible_command ();
4149         }
4150 }
4151
4152 void
4153 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4154 {
4155         boost::shared_ptr<Playlist> playlist;
4156         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4157         RegionSelection foo;
4158
4159         framepos_t const start_frame = regions.start ();
4160         framepos_t const end_frame = regions.end_frame ();
4161
4162         begin_reversible_command (Operations::duplicate_region);
4163
4164         selection->clear_regions ();
4165
4166         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4167
4168                 boost::shared_ptr<Region> r ((*i)->region());
4169
4170                 TimeAxisView& tv = (*i)->get_time_axis_view();
4171                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4172                 latest_regionviews.clear ();
4173                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4174
4175                 playlist = (*i)->region()->playlist();
4176                 playlist->clear_changes ();
4177                 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4178                 _session->add_command(new StatefulDiffCommand (playlist));
4179
4180                 c.disconnect ();
4181
4182                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4183         }
4184
4185         commit_reversible_command ();
4186
4187         if (!foo.empty()) {
4188                 selection->set (foo);
4189         }
4190 }
4191
4192 void
4193 Editor::duplicate_selection (float times)
4194 {
4195         if (selection->time.empty() || selection->tracks.empty()) {
4196                 return;
4197         }
4198
4199         boost::shared_ptr<Playlist> playlist;
4200         vector<boost::shared_ptr<Region> > new_regions;
4201         vector<boost::shared_ptr<Region> >::iterator ri;
4202
4203         create_region_from_selection (new_regions);
4204
4205         if (new_regions.empty()) {
4206                 return;
4207         }
4208
4209         begin_reversible_command (_("duplicate selection"));
4210
4211         ri = new_regions.begin();
4212
4213         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4214
4215         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4216                 if ((playlist = (*i)->playlist()) == 0) {
4217                         continue;
4218                 }
4219                 playlist->clear_changes ();
4220                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4221                 _session->add_command (new StatefulDiffCommand (playlist));
4222
4223                 ++ri;
4224                 if (ri == new_regions.end()) {
4225                         --ri;
4226                 }
4227         }
4228
4229         commit_reversible_command ();
4230 }
4231
4232 void
4233 Editor::reset_point_selection ()
4234 {
4235         /* reset all selected points to the relevant default value */
4236
4237         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4238
4239                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
4240
4241                 if (atv) {
4242                         atv->reset_objects (selection->points);
4243                 }
4244         }
4245 }
4246
4247 void
4248 Editor::center_playhead ()
4249 {
4250         float page = _canvas_width * frames_per_unit;
4251         center_screen_internal (playhead_cursor->current_frame, page);
4252 }
4253
4254 void
4255 Editor::center_edit_point ()
4256 {
4257         float page = _canvas_width * frames_per_unit;
4258         center_screen_internal (get_preferred_edit_position(), page);
4259 }
4260
4261 /** Caller must begin and commit a reversible command */
4262 void
4263 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4264 {
4265         playlist->clear_changes ();
4266         playlist->clear ();
4267         _session->add_command (new StatefulDiffCommand (playlist));
4268 }
4269
4270 void
4271 Editor::nudge_track (bool use_edit, bool forwards)
4272 {
4273         boost::shared_ptr<Playlist> playlist;
4274         framepos_t distance;
4275         framepos_t next_distance;
4276         framepos_t start;
4277
4278         if (use_edit) {
4279                 start = get_preferred_edit_position();
4280         } else {
4281                 start = 0;
4282         }
4283
4284         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4285                 return;
4286         }
4287
4288         if (selection->tracks.empty()) {
4289                 return;
4290         }
4291
4292         begin_reversible_command (_("nudge track"));
4293
4294         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4295
4296         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4297
4298                 if ((playlist = (*i)->playlist()) == 0) {
4299                         continue;
4300                 }
4301
4302                 playlist->clear_changes ();
4303                 playlist->clear_owned_changes ();
4304
4305                 playlist->nudge_after (start, distance, forwards);
4306
4307                 vector<Command*> cmds;
4308
4309                 playlist->rdiff (cmds);
4310                 _session->add_commands (cmds);
4311
4312                 _session->add_command (new StatefulDiffCommand (playlist));
4313         }
4314
4315         commit_reversible_command ();
4316 }
4317
4318 void
4319 Editor::remove_last_capture ()
4320 {
4321         vector<string> choices;
4322         string prompt;
4323
4324         if (!_session) {
4325                 return;
4326         }
4327
4328         if (Config->get_verify_remove_last_capture()) {
4329                 prompt  = _("Do you really want to destroy the last capture?"
4330                             "\n(This is destructive and cannot be undone)");
4331
4332                 choices.push_back (_("No, do nothing."));
4333                 choices.push_back (_("Yes, destroy it."));
4334
4335                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4336
4337                 if (prompter.run () == 1) {
4338                         _session->remove_last_capture ();
4339                         _regions->redisplay ();
4340                 }
4341
4342         } else {
4343                 _session->remove_last_capture();
4344                 _regions->redisplay ();
4345         }
4346 }
4347
4348 void
4349 Editor::normalize_region ()
4350 {
4351         if (!_session) {
4352                 return;
4353         }
4354
4355         RegionSelection rs = get_regions_from_selection_and_entered ();
4356
4357         if (rs.empty()) {
4358                 return;
4359         }
4360
4361         NormalizeDialog dialog (rs.size() > 1);
4362
4363         if (dialog.run () == RESPONSE_CANCEL) {
4364                 return;
4365         }
4366
4367         set_canvas_cursor (_cursors->wait);
4368         gdk_flush ();
4369
4370         /* XXX: should really only count audio regions here */
4371         int const regions = rs.size ();
4372
4373         /* Make a list of the selected audio regions' maximum amplitudes, and also
4374            obtain the maximum amplitude of them all.
4375         */
4376         list<double> max_amps;
4377         double max_amp = 0;
4378         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4379                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4380                 if (arv) {
4381                         dialog.descend (1.0 / regions);
4382                         double const a = arv->audio_region()->maximum_amplitude (&dialog);
4383
4384                         if (a == -1) {
4385                                 /* the user cancelled the operation */
4386                                 set_canvas_cursor (current_canvas_cursor);
4387                                 return;
4388                         }
4389
4390                         max_amps.push_back (a);
4391                         max_amp = max (max_amp, a);
4392                         dialog.ascend ();
4393                 }
4394         }
4395
4396         begin_reversible_command (_("normalize"));
4397
4398         list<double>::const_iterator a = max_amps.begin ();
4399
4400         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4401                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4402                 if (!arv) {
4403                         continue;
4404                 }
4405
4406                 arv->region()->clear_changes ();
4407
4408                 double const amp = dialog.normalize_individually() ? *a : max_amp;
4409
4410                 arv->audio_region()->normalize (amp, dialog.target ());
4411                 _session->add_command (new StatefulDiffCommand (arv->region()));
4412
4413                 ++a;
4414         }
4415
4416         commit_reversible_command ();
4417         set_canvas_cursor (current_canvas_cursor);
4418 }
4419
4420
4421 void
4422 Editor::reset_region_scale_amplitude ()
4423 {
4424         if (!_session) {
4425                 return;
4426         }
4427
4428         RegionSelection rs = get_regions_from_selection_and_entered ();
4429
4430         if (rs.empty()) {
4431                 return;
4432         }
4433
4434         begin_reversible_command ("reset gain");
4435
4436         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4437                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4438                 if (!arv)
4439                         continue;
4440                 arv->region()->clear_changes ();
4441                 arv->audio_region()->set_scale_amplitude (1.0f);
4442                 _session->add_command (new StatefulDiffCommand (arv->region()));
4443         }
4444
4445         commit_reversible_command ();
4446 }
4447
4448 void
4449 Editor::adjust_region_gain (bool up)
4450 {
4451         RegionSelection rs = get_regions_from_selection_and_entered ();
4452
4453         if (!_session || rs.empty()) {
4454                 return;
4455         }
4456
4457         begin_reversible_command ("adjust region gain");
4458
4459         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4460                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4461                 if (!arv) {
4462                         continue;
4463                 }
4464
4465                 arv->region()->clear_changes ();
4466
4467                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4468
4469                 if (up) {
4470                         dB += 1;
4471                 } else {
4472                         dB -= 1;
4473                 }
4474
4475                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4476                 _session->add_command (new StatefulDiffCommand (arv->region()));
4477         }
4478
4479         commit_reversible_command ();
4480 }
4481
4482
4483 void
4484 Editor::reverse_region ()
4485 {
4486         if (!_session) {
4487                 return;
4488         }
4489
4490         Reverse rev (*_session);
4491         apply_filter (rev, _("reverse regions"));
4492 }
4493
4494 void
4495 Editor::strip_region_silence ()
4496 {
4497         if (!_session) {
4498                 return;
4499         }
4500
4501         RegionSelection rs = get_regions_from_selection_and_entered ();
4502
4503         if (rs.empty()) {
4504                 return;
4505         }
4506
4507         std::list<RegionView*> audio_only;
4508
4509         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4510                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4511                 if (arv) {
4512                         audio_only.push_back (arv);
4513                 }
4514         }
4515
4516         StripSilenceDialog d (_session, audio_only);
4517         int const r = d.run ();
4518
4519         d.drop_rects ();
4520
4521         if (r == Gtk::RESPONSE_OK) {
4522                 ARDOUR::AudioIntervalMap silences;
4523                 d.silences (silences);
4524                 StripSilence s (*_session, silences, d.fade_length());
4525                 apply_filter (s, _("strip silence"), &d);
4526         }
4527 }
4528
4529 Command*
4530 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4531 {
4532         Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4533         mrv.selection_as_notelist (selected, true);
4534
4535         vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4536         v.push_back (selected);
4537
4538         framepos_t pos_frames = mrv.midi_region()->position();
4539         double     pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4540
4541         return op (mrv.midi_region()->model(), pos_beats, v);
4542 }
4543
4544 void
4545 Editor::apply_midi_note_edit_op (MidiOperator& op)
4546 {
4547         Command* cmd;
4548
4549         RegionSelection rs = get_regions_from_selection_and_entered ();
4550
4551         if (rs.empty()) {
4552                 return;
4553         }
4554
4555         begin_reversible_command (op.name ());
4556
4557         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4558                 RegionSelection::iterator tmp = r;
4559                 ++tmp;
4560
4561                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4562
4563                 if (mrv) {
4564                         cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4565                         if (cmd) {
4566                                 (*cmd)();
4567                                 _session->add_command (cmd);
4568                         }
4569                 }
4570
4571                 r = tmp;
4572         }
4573
4574         commit_reversible_command ();
4575 }
4576
4577 void
4578 Editor::fork_region ()
4579 {
4580         RegionSelection rs = get_regions_from_selection_and_entered ();
4581
4582         if (rs.empty()) {
4583                 return;
4584         }
4585
4586         begin_reversible_command (_("Fork Region(s)"));
4587
4588         set_canvas_cursor (_cursors->wait);
4589         gdk_flush ();
4590
4591         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4592                 RegionSelection::iterator tmp = r;
4593                 ++tmp;
4594
4595                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4596
4597                 if (mrv) {
4598                         boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4599                         boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4600
4601                         playlist->clear_changes ();
4602                         playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4603                         _session->add_command(new StatefulDiffCommand (playlist));
4604                 }
4605
4606                 r = tmp;
4607         }
4608
4609         commit_reversible_command ();
4610
4611         set_canvas_cursor (current_canvas_cursor);
4612 }
4613
4614 void
4615 Editor::quantize_region ()
4616 {
4617         int selected_midi_region_cnt = 0;
4618
4619         if (!_session) {
4620                 return;
4621         }
4622
4623         RegionSelection rs = get_regions_from_selection_and_entered ();
4624
4625         if (rs.empty()) {
4626                 return;
4627         }
4628
4629         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4630                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4631                 if (mrv) {
4632                         selected_midi_region_cnt++;
4633                 }
4634         }
4635
4636         if (selected_midi_region_cnt == 0) {
4637                 return;
4638         }
4639
4640         QuantizeDialog* qd = new QuantizeDialog (*this);
4641
4642         qd->present ();
4643         const int r = qd->run ();
4644         qd->hide ();
4645
4646         if (r == Gtk::RESPONSE_OK) {
4647                 Quantize quant (*_session, Plain,
4648                                 qd->snap_start(), qd->snap_end(),
4649                                 qd->start_grid_size(), qd->end_grid_size(),
4650                                 qd->strength(), qd->swing(), qd->threshold());
4651
4652                 apply_midi_note_edit_op (quant);
4653         }
4654 }
4655
4656 void
4657 Editor::insert_patch_change (bool from_context)
4658 {
4659         RegionSelection rs = get_regions_from_selection_and_entered ();
4660
4661         if (rs.empty ()) {
4662                 return;
4663         }
4664
4665         const framepos_t p = get_preferred_edit_position (false, from_context);
4666
4667         Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4668         PatchChangeDialog d (0, _session, empty, Gtk::Stock::ADD);
4669
4670         if (d.run() == RESPONSE_CANCEL) {
4671                 return;
4672         }
4673
4674         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4675                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4676                 if (mrv) {
4677                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4678                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4679                         }
4680                 }
4681         }
4682 }
4683
4684 void
4685 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4686 {
4687         RegionSelection rs = get_regions_from_selection_and_entered ();
4688
4689         if (rs.empty()) {
4690                 return;
4691         }
4692
4693         begin_reversible_command (command);
4694
4695         set_canvas_cursor (_cursors->wait);
4696         gdk_flush ();
4697
4698         int n = 0;
4699         int const N = rs.size ();
4700
4701         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4702                 RegionSelection::iterator tmp = r;
4703                 ++tmp;
4704
4705                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4706                 if (arv) {
4707                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4708
4709                         if (progress) {
4710                                 progress->descend (1.0 / N);
4711                         }
4712
4713                         if (arv->audio_region()->apply (filter, progress) == 0) {
4714
4715                                 playlist->clear_changes ();
4716                                 playlist->clear_owned_changes ();
4717
4718                                 if (filter.results.empty ()) {
4719
4720                                         /* no regions returned; remove the old one */
4721                                         playlist->remove_region (arv->region ());
4722
4723                                 } else {
4724
4725                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4726
4727                                         /* first region replaces the old one */
4728                                         playlist->replace_region (arv->region(), *res, (*res)->position());
4729                                         ++res;
4730
4731                                         /* add the rest */
4732                                         while (res != filter.results.end()) {
4733                                                 playlist->add_region (*res, (*res)->position());
4734                                                 ++res;
4735                                         }
4736
4737                                 }
4738
4739                                 /* We might have removed regions, which alters other regions' layering_index,
4740                                    so we need to do a recursive diff here.
4741                                 */
4742                                 vector<Command*> cmds;
4743                                 playlist->rdiff (cmds);
4744                                 _session->add_commands (cmds);
4745                                 
4746                                 _session->add_command(new StatefulDiffCommand (playlist));
4747                         } else {
4748                                 goto out;
4749                         }
4750
4751                         if (progress) {
4752                                 progress->ascend ();
4753                         }
4754                 }
4755
4756                 r = tmp;
4757                 ++n;
4758         }
4759
4760         commit_reversible_command ();
4761
4762   out:
4763         set_canvas_cursor (current_canvas_cursor);
4764 }
4765
4766 void
4767 Editor::external_edit_region ()
4768 {
4769         /* more to come */
4770 }
4771
4772 void
4773 Editor::reset_region_gain_envelopes ()
4774 {
4775         RegionSelection rs = get_regions_from_selection_and_entered ();
4776
4777         if (!_session || rs.empty()) {
4778                 return;
4779         }
4780
4781         _session->begin_reversible_command (_("reset region gain"));
4782
4783         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4784                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4785                 if (arv) {
4786                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4787                         XMLNode& before (alist->get_state());
4788
4789                         arv->audio_region()->set_default_envelope ();
4790                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4791                 }
4792         }
4793
4794         _session->commit_reversible_command ();
4795 }
4796
4797 void
4798 Editor::set_region_gain_visibility (RegionView* rv, bool yn)
4799 {
4800         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4801         if (arv) {
4802                 arv->set_envelope_visible (yn);
4803         }
4804 }
4805
4806 void
4807 Editor::set_gain_envelope_visibility (bool yn)
4808 {
4809         if (!_session) {
4810                 return;
4811         }
4812
4813         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4814                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4815                 if (v) {
4816                         v->audio_view()->foreach_regionview (sigc::bind (sigc::mem_fun (this, &Editor::set_region_gain_visibility), yn));
4817                 }
4818         }
4819 }
4820
4821 void
4822 Editor::toggle_gain_envelope_active ()
4823 {
4824         if (_ignore_region_action) {
4825                 return;
4826         }
4827
4828         RegionSelection rs = get_regions_from_selection_and_entered ();
4829
4830         if (!_session || rs.empty()) {
4831                 return;
4832         }
4833
4834         _session->begin_reversible_command (_("region gain envelope active"));
4835
4836         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4837                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4838                 if (arv) {
4839                         arv->region()->clear_changes ();
4840                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4841                         _session->add_command (new StatefulDiffCommand (arv->region()));
4842                 }
4843         }
4844
4845         _session->commit_reversible_command ();
4846 }
4847
4848 void
4849 Editor::toggle_region_lock ()
4850 {
4851         if (_ignore_region_action) {
4852                 return;
4853         }
4854
4855         RegionSelection rs = get_regions_from_selection_and_entered ();
4856
4857         if (!_session || rs.empty()) {
4858                 return;
4859         }
4860
4861         _session->begin_reversible_command (_("toggle region lock"));
4862
4863         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4864                 (*i)->region()->clear_changes ();
4865                 (*i)->region()->set_locked (!(*i)->region()->locked());
4866                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4867         }
4868
4869         _session->commit_reversible_command ();
4870 }
4871
4872 void
4873 Editor::toggle_region_lock_style ()
4874 {
4875         if (_ignore_region_action) {
4876                 return;
4877         }
4878
4879         RegionSelection rs = get_regions_from_selection_and_entered ();
4880
4881         if (!_session || rs.empty()) {
4882                 return;
4883         }
4884
4885         _session->begin_reversible_command (_("region lock style"));
4886
4887         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4888                 (*i)->region()->clear_changes ();
4889                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4890                 (*i)->region()->set_position_lock_style (ns);
4891                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4892         }
4893
4894         _session->commit_reversible_command ();
4895 }
4896
4897 void
4898 Editor::toggle_opaque_region ()
4899 {
4900         if (_ignore_region_action) {
4901                 return;
4902         }
4903
4904         RegionSelection rs = get_regions_from_selection_and_entered ();
4905
4906         if (!_session || rs.empty()) {
4907                 return;
4908         }
4909
4910         _session->begin_reversible_command (_("change region opacity"));
4911
4912         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4913                 (*i)->region()->clear_changes ();
4914                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
4915                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4916         }
4917
4918         _session->commit_reversible_command ();
4919 }
4920
4921 void
4922 Editor::toggle_record_enable ()
4923 {
4924         bool new_state = false;
4925         bool first = true;
4926         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4927                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4928                 if (!rtav)
4929                         continue;
4930                 if (!rtav->is_track())
4931                         continue;
4932
4933                 if (first) {
4934                         new_state = !rtav->track()->record_enabled();
4935                         first = false;
4936                 }
4937
4938                 rtav->track()->set_record_enabled (new_state, this);
4939         }
4940 }
4941
4942 void
4943 Editor::toggle_solo ()
4944 {
4945         bool new_state = false;
4946         bool first = true;
4947         boost::shared_ptr<RouteList> rl (new RouteList);
4948
4949         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4950                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4951
4952                 if (!rtav) {
4953                         continue;
4954                 }
4955
4956                 if (first) {
4957                         new_state = !rtav->route()->soloed ();
4958                         first = false;
4959                 }
4960
4961                 rl->push_back (rtav->route());
4962         }
4963
4964         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
4965 }
4966
4967 void
4968 Editor::toggle_mute ()
4969 {
4970         bool new_state = false;
4971         bool first = true;
4972         boost::shared_ptr<RouteList> rl (new RouteList);
4973
4974         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4975                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4976
4977                 if (!rtav) {
4978                         continue;
4979                 }
4980
4981                 if (first) {
4982                         new_state = !rtav->route()->muted();
4983                         first = false;
4984                 }
4985
4986                 rl->push_back (rtav->route());
4987         }
4988
4989         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
4990 }
4991
4992 void
4993 Editor::toggle_solo_isolate ()
4994 {
4995 }
4996
4997 void
4998 Editor::set_fade_length (bool in)
4999 {
5000         RegionSelection rs = get_regions_from_selection_and_entered ();
5001
5002         if (rs.empty()) {
5003                 return;
5004         }
5005
5006         /* we need a region to measure the offset from the start */
5007
5008         RegionView* rv = rs.front ();
5009
5010         framepos_t pos = get_preferred_edit_position();
5011         framepos_t len;
5012         char const * cmd;
5013
5014         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5015                 /* edit point is outside the relevant region */
5016                 return;
5017         }
5018
5019         if (in) {
5020                 if (pos <= rv->region()->position()) {
5021                         /* can't do it */
5022                         return;
5023                 }
5024                 len = pos - rv->region()->position();
5025                 cmd = _("set fade in length");
5026         } else {
5027                 if (pos >= rv->region()->last_frame()) {
5028                         /* can't do it */
5029                         return;
5030                 }
5031                 len = rv->region()->last_frame() - pos;
5032                 cmd = _("set fade out length");
5033         }
5034
5035         begin_reversible_command (cmd);
5036
5037         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5038                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5039
5040                 if (!tmp) {
5041                         return;
5042                 }
5043
5044                 boost::shared_ptr<AutomationList> alist;
5045                 if (in) {
5046                         alist = tmp->audio_region()->fade_in();
5047                 } else {
5048                         alist = tmp->audio_region()->fade_out();
5049                 }
5050
5051                 XMLNode &before = alist->get_state();
5052
5053                 if (in) {
5054                         tmp->audio_region()->set_fade_in_length (len);
5055                         tmp->audio_region()->set_fade_in_active (true);
5056                 } else {
5057                         tmp->audio_region()->set_fade_out_length (len);
5058                         tmp->audio_region()->set_fade_out_active (true);
5059                 }
5060
5061                 XMLNode &after = alist->get_state();
5062                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5063         }
5064
5065         commit_reversible_command ();
5066 }
5067
5068 void
5069 Editor::set_fade_in_shape (FadeShape shape)
5070 {
5071         RegionSelection rs = get_regions_from_selection_and_entered ();
5072
5073         if (rs.empty()) {
5074                 return;
5075         }
5076
5077         begin_reversible_command (_("set fade in shape"));
5078
5079         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5080                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5081
5082                 if (!tmp) {
5083                         return;
5084                 }
5085
5086                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5087                 XMLNode &before = alist->get_state();
5088
5089                 tmp->audio_region()->set_fade_in_shape (shape);
5090
5091                 XMLNode &after = alist->get_state();
5092                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5093         }
5094
5095         commit_reversible_command ();
5096
5097 }
5098
5099 void
5100 Editor::set_fade_out_shape (FadeShape shape)
5101 {
5102         RegionSelection rs = get_regions_from_selection_and_entered ();
5103
5104         if (rs.empty()) {
5105                 return;
5106         }
5107
5108         begin_reversible_command (_("set fade out shape"));
5109
5110         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5111                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5112
5113                 if (!tmp) {
5114                         return;
5115                 }
5116
5117                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5118                 XMLNode &before = alist->get_state();
5119
5120                 tmp->audio_region()->set_fade_out_shape (shape);
5121
5122                 XMLNode &after = alist->get_state();
5123                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5124         }
5125
5126         commit_reversible_command ();
5127 }
5128
5129 void
5130 Editor::set_fade_in_active (bool yn)
5131 {
5132         RegionSelection rs = get_regions_from_selection_and_entered ();
5133
5134         if (rs.empty()) {
5135                 return;
5136         }
5137
5138         begin_reversible_command (_("set fade in active"));
5139
5140         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5141                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5142
5143                 if (!tmp) {
5144                         return;
5145                 }
5146
5147
5148                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5149
5150                 ar->clear_changes ();
5151                 ar->set_fade_in_active (yn);
5152                 _session->add_command (new StatefulDiffCommand (ar));
5153         }
5154
5155         commit_reversible_command ();
5156 }
5157
5158 void
5159 Editor::set_fade_out_active (bool yn)
5160 {
5161         RegionSelection rs = get_regions_from_selection_and_entered ();
5162
5163         if (rs.empty()) {
5164                 return;
5165         }
5166
5167         begin_reversible_command (_("set fade out active"));
5168
5169         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5170                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5171
5172                 if (!tmp) {
5173                         return;
5174                 }
5175
5176                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5177
5178                 ar->clear_changes ();
5179                 ar->set_fade_out_active (yn);
5180                 _session->add_command(new StatefulDiffCommand (ar));
5181         }
5182
5183         commit_reversible_command ();
5184 }
5185
5186 void
5187 Editor::toggle_region_fades (int dir)
5188 {
5189         boost::shared_ptr<AudioRegion> ar;
5190         bool yn = false;
5191
5192         RegionSelection rs = get_regions_from_selection_and_entered ();
5193
5194         if (rs.empty()) {
5195                 return;
5196         }
5197
5198         RegionSelection::iterator i;
5199         for (i = rs.begin(); i != rs.end(); ++i) {
5200                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5201                         if (dir == -1) {
5202                                 yn = ar->fade_out_active ();
5203                         } else {
5204                                 yn = ar->fade_in_active ();
5205                         }
5206                         break;
5207                 }
5208         }
5209
5210         if (i == rs.end()) {
5211                 return;
5212         }
5213
5214         /* XXX should this undo-able? */
5215
5216         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5217                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5218                         continue;
5219                 }
5220                 if (dir == 1 || dir == 0) {
5221                         ar->set_fade_in_active (!yn);
5222                 }
5223
5224                 if (dir == -1 || dir == 0) {
5225                         ar->set_fade_out_active (!yn);
5226                 }
5227         }
5228 }
5229
5230
5231 /** Update region fade visibility after its configuration has been changed */
5232 void
5233 Editor::update_region_fade_visibility ()
5234 {
5235         bool _fade_visibility = _session->config.get_show_region_fades ();
5236
5237         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5238                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5239                 if (v) {
5240                         if (_fade_visibility) {
5241                                 v->audio_view()->show_all_fades ();
5242                         } else {
5243                                 v->audio_view()->hide_all_fades ();
5244                         }
5245                 }
5246         }
5247 }
5248
5249 void
5250 Editor::set_edit_point ()
5251 {
5252         framepos_t where;
5253         bool ignored;
5254
5255         if (!mouse_frame (where, ignored)) {
5256                 return;
5257         }
5258
5259         snap_to (where);
5260
5261         if (selection->markers.empty()) {
5262
5263                 mouse_add_new_marker (where);
5264
5265         } else {
5266                 bool ignored;
5267
5268                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5269
5270                 if (loc) {
5271                         loc->move_to (where);
5272                 }
5273         }
5274 }
5275
5276 void
5277 Editor::set_playhead_cursor ()
5278 {
5279         if (entered_marker) {
5280                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5281         } else {
5282                 framepos_t where;
5283                 bool ignored;
5284
5285                 if (!mouse_frame (where, ignored)) {
5286                         return;
5287                 }
5288
5289                 snap_to (where);
5290
5291                 if (_session) {
5292                         _session->request_locate (where, _session->transport_rolling());
5293                 }
5294         }
5295 }
5296
5297 void
5298 Editor::split_region ()
5299 {
5300         if (((mouse_mode == MouseRange) ||
5301              (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5302             !selection->time.empty()) {
5303                 separate_regions_between (selection->time);
5304                 return;
5305         }
5306
5307         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5308
5309         framepos_t where = get_preferred_edit_position ();
5310
5311         if (rs.empty()) {
5312                 return;
5313         }
5314
5315         split_regions_at (where, rs);
5316 }
5317
5318 void
5319 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
5320 {
5321         if (entered_track && mouse_mode == MouseObject) {
5322                 if (!selection->tracks.empty()) {
5323                         if (!selection->selected (entered_track)) {
5324                                 selection->add (entered_track);
5325                         }
5326                 } else {
5327                         /* there is no selection, but this operation requires/prefers selected objects */
5328
5329                         if (op_really_wants_one_track_if_none_are_selected) {
5330                                 selection->set (entered_track);
5331                         }
5332                 }
5333         }
5334 }
5335
5336 struct EditorOrderRouteSorter {
5337     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5338             /* use of ">" forces the correct sort order */
5339             return a->order_key ("editor") < b->order_key ("editor");
5340     }
5341 };
5342
5343 void
5344 Editor::select_next_route()
5345 {
5346         if (selection->tracks.empty()) {
5347                 selection->set (track_views.front());
5348                 return;
5349         }
5350
5351         TimeAxisView* current = selection->tracks.front();
5352
5353         RouteUI *rui;
5354         do {
5355                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5356                         if (*i == current) {
5357                                 ++i;
5358                                 if (i != track_views.end()) {
5359                                         current = (*i);
5360                                 } else {
5361                                         current = (*(track_views.begin()));
5362                                         //selection->set (*(track_views.begin()));
5363                                 }
5364                                 break;
5365                         }
5366                 }
5367                 rui = dynamic_cast<RouteUI *>(current);
5368         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5369
5370         selection->set(current);
5371
5372         ensure_track_visible(current);
5373 }
5374
5375 void
5376 Editor::select_prev_route()
5377 {
5378         if (selection->tracks.empty()) {
5379                 selection->set (track_views.front());
5380                 return;
5381         }
5382
5383         TimeAxisView* current = selection->tracks.front();
5384
5385         RouteUI *rui;
5386         do {
5387                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5388                         if (*i == current) {
5389                                 ++i;
5390                                 if (i != track_views.rend()) {
5391                                         current = (*i);
5392                                 } else {
5393                                         current = *(track_views.rbegin());
5394                                 }
5395                                 break;
5396                         }
5397                 }
5398                 rui = dynamic_cast<RouteUI *>(current);
5399         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5400
5401         selection->set (current);
5402
5403         ensure_track_visible(current);
5404 }
5405
5406 void
5407 Editor::ensure_track_visible(TimeAxisView *track)
5408 {
5409         if (track->hidden())
5410                 return;
5411
5412         double const current_view_min_y = vertical_adjustment.get_value();
5413         double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5414
5415         double const track_min_y = track->y_position ();
5416         double const track_max_y = track->y_position () + track->effective_height ();
5417
5418         if (track_min_y >= current_view_min_y &&
5419             track_max_y <= current_view_max_y) {
5420                 return;
5421         }
5422
5423         double new_value;
5424
5425         if (track_min_y < current_view_min_y) {
5426                 // Track is above the current view
5427                 new_value = track_min_y;
5428         } else {
5429                 // Track is below the current view
5430                 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5431         }
5432
5433         vertical_adjustment.set_value(new_value);
5434 }
5435
5436 void
5437 Editor::set_loop_from_selection (bool play)
5438 {
5439         if (_session == 0 || selection->time.empty()) {
5440                 return;
5441         }
5442
5443         framepos_t start = selection->time[clicked_selection].start;
5444         framepos_t end = selection->time[clicked_selection].end;
5445
5446         set_loop_range (start, end,  _("set loop range from selection"));
5447
5448         if (play) {
5449                 _session->request_play_loop (true);
5450                 _session->request_locate (start, true);
5451         }
5452 }
5453
5454 void
5455 Editor::set_loop_from_edit_range (bool play)
5456 {
5457         if (_session == 0) {
5458                 return;
5459         }
5460
5461         framepos_t start;
5462         framepos_t end;
5463
5464         if (!get_edit_op_range (start, end)) {
5465                 return;
5466         }
5467
5468         set_loop_range (start, end,  _("set loop range from edit range"));
5469
5470         if (play) {
5471                 _session->request_play_loop (true);
5472                 _session->request_locate (start, true);
5473         }
5474 }
5475
5476 void
5477 Editor::set_loop_from_region (bool play)
5478 {
5479         framepos_t start = max_framepos;
5480         framepos_t end = 0;
5481
5482         RegionSelection rs = get_regions_from_selection_and_entered ();
5483
5484         if (rs.empty()) {
5485                 return;
5486         }
5487
5488         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5489                 if ((*i)->region()->position() < start) {
5490                         start = (*i)->region()->position();
5491                 }
5492                 if ((*i)->region()->last_frame() + 1 > end) {
5493                         end = (*i)->region()->last_frame() + 1;
5494                 }
5495         }
5496
5497         set_loop_range (start, end, _("set loop range from region"));
5498
5499         if (play) {
5500                 _session->request_play_loop (true);
5501                 _session->request_locate (start, true);
5502         }
5503 }
5504
5505 void
5506 Editor::set_punch_from_selection ()
5507 {
5508         if (_session == 0 || selection->time.empty()) {
5509                 return;
5510         }
5511
5512         framepos_t start = selection->time[clicked_selection].start;
5513         framepos_t end = selection->time[clicked_selection].end;
5514
5515         set_punch_range (start, end,  _("set punch range from selection"));
5516 }
5517
5518 void
5519 Editor::set_punch_from_edit_range ()
5520 {
5521         if (_session == 0) {
5522                 return;
5523         }
5524
5525         framepos_t start;
5526         framepos_t end;
5527
5528         if (!get_edit_op_range (start, end)) {
5529                 return;
5530         }
5531
5532         set_punch_range (start, end,  _("set punch range from edit range"));
5533 }
5534
5535 void
5536 Editor::set_punch_from_region ()
5537 {
5538         framepos_t start = max_framepos;
5539         framepos_t end = 0;
5540
5541         RegionSelection rs = get_regions_from_selection_and_entered ();
5542
5543         if (rs.empty()) {
5544                 return;
5545         }
5546
5547         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5548                 if ((*i)->region()->position() < start) {
5549                         start = (*i)->region()->position();
5550                 }
5551                 if ((*i)->region()->last_frame() + 1 > end) {
5552                         end = (*i)->region()->last_frame() + 1;
5553                 }
5554         }
5555
5556         set_punch_range (start, end, _("set punch range from region"));
5557 }
5558
5559 void
5560 Editor::pitch_shift_region ()
5561 {
5562         RegionSelection rs = get_regions_from_selection_and_entered ();
5563
5564         RegionSelection audio_rs;
5565         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5566                 if (dynamic_cast<AudioRegionView*> (*i)) {
5567                         audio_rs.push_back (*i);
5568                 }
5569         }
5570
5571         if (audio_rs.empty()) {
5572                 return;
5573         }
5574
5575         pitch_shift (audio_rs, 1.2);
5576 }
5577
5578 void
5579 Editor::transpose_region ()
5580 {
5581         RegionSelection rs = get_regions_from_selection_and_entered ();
5582
5583         list<MidiRegionView*> midi_region_views;
5584         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5585                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5586                 if (mrv) {
5587                         midi_region_views.push_back (mrv);
5588                 }
5589         }
5590
5591         TransposeDialog d;
5592         int const r = d.run ();
5593         if (r != RESPONSE_ACCEPT) {
5594                 return;
5595         }
5596
5597         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5598                 (*i)->midi_region()->transpose (d.semitones ());
5599         }
5600 }
5601
5602 void
5603 Editor::set_tempo_from_region ()
5604 {
5605         RegionSelection rs = get_regions_from_selection_and_entered ();
5606
5607         if (!_session || rs.empty()) {
5608                 return;
5609         }
5610
5611         RegionView* rv = rs.front();
5612
5613         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5614 }
5615
5616 void
5617 Editor::use_range_as_bar ()
5618 {
5619         framepos_t start, end;
5620         if (get_edit_op_range (start, end)) {
5621                 define_one_bar (start, end);
5622         }
5623 }
5624
5625 void
5626 Editor::define_one_bar (framepos_t start, framepos_t end)
5627 {
5628         framepos_t length = end - start;
5629
5630         const Meter& m (_session->tempo_map().meter_at (start));
5631
5632         /* length = 1 bar */
5633
5634         /* now we want frames per beat.
5635            we have frames per bar, and beats per bar, so ...
5636         */
5637
5638         /* XXXX METER MATH */
5639
5640         double frames_per_beat = length / m.divisions_per_bar();
5641
5642         /* beats per minute = */
5643
5644         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5645
5646         /* now decide whether to:
5647
5648             (a) set global tempo
5649             (b) add a new tempo marker
5650
5651         */
5652
5653         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5654
5655         bool do_global = false;
5656
5657         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5658
5659                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5660                    at the start, or create a new marker
5661                 */
5662
5663                 vector<string> options;
5664                 options.push_back (_("Cancel"));
5665                 options.push_back (_("Add new marker"));
5666                 options.push_back (_("Set global tempo"));
5667
5668                 Choice c (
5669                         _("Define one bar"),
5670                         _("Do you want to set the global tempo or add a new tempo marker?"),
5671                         options
5672                         );
5673
5674                 c.set_default_response (2);
5675
5676                 switch (c.run()) {
5677                 case 0:
5678                         return;
5679
5680                 case 2:
5681                         do_global = true;
5682                         break;
5683
5684                 default:
5685                         do_global = false;
5686                 }
5687
5688         } else {
5689
5690                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5691                    if the marker is at the region starter, change it, otherwise add
5692                    a new tempo marker
5693                 */
5694         }
5695
5696         begin_reversible_command (_("set tempo from region"));
5697         XMLNode& before (_session->tempo_map().get_state());
5698
5699         if (do_global) {
5700                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5701         } else if (t.frame() == start) {
5702                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5703         } else {
5704                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5705         }
5706
5707         XMLNode& after (_session->tempo_map().get_state());
5708
5709         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5710         commit_reversible_command ();
5711 }
5712
5713 void
5714 Editor::split_region_at_transients ()
5715 {
5716         AnalysisFeatureList positions;
5717
5718         RegionSelection rs = get_regions_from_selection_and_entered ();
5719
5720         if (!_session || rs.empty()) {
5721                 return;
5722         }
5723
5724         _session->begin_reversible_command (_("split regions"));
5725
5726         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5727
5728                 RegionSelection::iterator tmp;
5729
5730                 tmp = i;
5731                 ++tmp;
5732
5733                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5734
5735                 if (ar && (ar->get_transients (positions) == 0)) {
5736                         split_region_at_points ((*i)->region(), positions, true);
5737                         positions.clear ();
5738                 }
5739
5740                 i = tmp;
5741         }
5742
5743         _session->commit_reversible_command ();
5744
5745 }
5746
5747 void
5748 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5749 {
5750         bool use_rhythmic_rodent = false;
5751
5752         boost::shared_ptr<Playlist> pl = r->playlist();
5753
5754         list<boost::shared_ptr<Region> > new_regions;
5755
5756         if (!pl) {
5757                 return;
5758         }
5759
5760         if (positions.empty()) {
5761                 return;
5762         }
5763
5764
5765         if (positions.size() > 20 && can_ferret) {
5766                 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);
5767                 MessageDialog msg (msgstr,
5768                                    false,
5769                                    Gtk::MESSAGE_INFO,
5770                                    Gtk::BUTTONS_OK_CANCEL);
5771
5772                 if (can_ferret) {
5773                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5774                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5775                 } else {
5776                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
5777                 }
5778
5779                 msg.set_title (_("Excessive split?"));
5780                 msg.present ();
5781
5782                 int response = msg.run();
5783                 msg.hide ();
5784
5785                 switch (response) {
5786                 case RESPONSE_OK:
5787                         break;
5788                 case RESPONSE_APPLY:
5789                         use_rhythmic_rodent = true;
5790                         break;
5791                 default:
5792                         return;
5793                 }
5794         }
5795
5796         if (use_rhythmic_rodent) {
5797                 show_rhythm_ferret ();
5798                 return;
5799         }
5800
5801         AnalysisFeatureList::const_iterator x;
5802
5803         pl->clear_changes ();
5804         pl->clear_owned_changes ();
5805
5806         x = positions.begin();
5807
5808         if (x == positions.end()) {
5809                 return;
5810         }
5811
5812         pl->freeze ();
5813         pl->remove_region (r);
5814
5815         framepos_t pos = 0;
5816
5817         while (x != positions.end()) {
5818
5819                 /* deal with positons that are out of scope of present region bounds */
5820                 if (*x <= 0 || *x > r->length()) {
5821                         ++x;
5822                         continue;
5823                 }
5824
5825                 /* file start = original start + how far we from the initial position ?
5826                  */
5827
5828                 framepos_t file_start = r->start() + pos;
5829
5830                 /* length = next position - current position
5831                  */
5832
5833                 framepos_t len = (*x) - pos;
5834
5835                 /* XXX we do we really want to allow even single-sample regions?
5836                    shouldn't we have some kind of lower limit on region size?
5837                 */
5838
5839                 if (len <= 0) {
5840                         break;
5841                 }
5842
5843                 string new_name;
5844
5845                 if (RegionFactory::region_name (new_name, r->name())) {
5846                         break;
5847                 }
5848
5849                 /* do NOT announce new regions 1 by one, just wait till they are all done */
5850
5851                 PropertyList plist;
5852
5853                 plist.add (ARDOUR::Properties::start, file_start);
5854                 plist.add (ARDOUR::Properties::length, len);
5855                 plist.add (ARDOUR::Properties::name, new_name);
5856                 plist.add (ARDOUR::Properties::layer, 0);
5857
5858                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5859
5860                 pl->add_region (nr, r->position() + pos);
5861
5862                 if (select_new) {
5863                         new_regions.push_front(nr);
5864                 }
5865
5866                 pos += len;
5867                 ++x;
5868         }
5869
5870         string new_name;
5871
5872         RegionFactory::region_name (new_name, r->name());
5873
5874         /* Add the final region */
5875         PropertyList plist;
5876
5877         plist.add (ARDOUR::Properties::start, r->start() + pos);
5878         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5879         plist.add (ARDOUR::Properties::name, new_name);
5880         plist.add (ARDOUR::Properties::layer, 0);
5881
5882         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5883         pl->add_region (nr, r->position() + pos);
5884
5885         if (select_new) {
5886                 new_regions.push_front(nr);
5887         }
5888
5889         pl->thaw ();
5890
5891         /* We might have removed regions, which alters other regions' layering_index,
5892            so we need to do a recursive diff here.
5893         */
5894         vector<Command*> cmds;
5895         pl->rdiff (cmds);
5896         _session->add_commands (cmds);
5897         
5898         _session->add_command (new StatefulDiffCommand (pl));
5899
5900         if (select_new) {
5901
5902                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5903                         set_selected_regionview_from_region_list ((*i), Selection::Add);
5904                 }
5905         }
5906 }
5907
5908 void
5909 Editor::place_transient()
5910 {
5911         if (!_session) {
5912                 return;
5913         }
5914
5915         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5916
5917         if (rs.empty()) {
5918                 return;
5919         }
5920
5921         framepos_t where = get_preferred_edit_position();
5922
5923         _session->begin_reversible_command (_("place transient"));
5924
5925         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5926                 framepos_t position = (*r)->region()->position();
5927                 (*r)->region()->add_transient(where - position);
5928         }
5929
5930         _session->commit_reversible_command ();
5931 }
5932
5933 void
5934 Editor::remove_transient(ArdourCanvas::Item* item)
5935 {
5936         if (!_session) {
5937                 return;
5938         }
5939
5940         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
5941         assert (_line);
5942
5943         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
5944         _arv->remove_transient (*(float*) _line->get_data ("position"));
5945 }
5946
5947 void
5948 Editor::snap_regions_to_grid ()
5949 {
5950         list <boost::shared_ptr<Playlist > > used_playlists;
5951
5952         RegionSelection rs = get_regions_from_selection_and_entered ();
5953
5954         if (!_session || rs.empty()) {
5955                 return;
5956         }
5957
5958         _session->begin_reversible_command (_("snap regions to grid"));
5959
5960         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5961
5962                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
5963
5964                 if (!pl->frozen()) {
5965                         /* we haven't seen this playlist before */
5966
5967                         /* remember used playlists so we can thaw them later */
5968                         used_playlists.push_back(pl);
5969                         pl->freeze();
5970                 }
5971
5972                 framepos_t start_frame = (*r)->region()->first_frame ();
5973                 snap_to (start_frame);
5974                 (*r)->region()->set_position (start_frame);
5975         }
5976
5977         while (used_playlists.size() > 0) {
5978                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
5979                 (*i)->thaw();
5980                 used_playlists.pop_front();
5981         }
5982
5983         _session->commit_reversible_command ();
5984 }
5985
5986 void
5987 Editor::close_region_gaps ()
5988 {
5989         list <boost::shared_ptr<Playlist > > used_playlists;
5990
5991         RegionSelection rs = get_regions_from_selection_and_entered ();
5992
5993         if (!_session || rs.empty()) {
5994                 return;
5995         }
5996
5997         Dialog dialog (_("Close Region Gaps"));
5998
5999         Table table (2, 3);
6000         table.set_spacings (12);
6001         table.set_border_width (12);
6002         Label* l = manage (new Label (_("Crossfade length")));
6003         l->set_alignment (0, 0.5);
6004         table.attach (*l, 0, 1, 0, 1);
6005
6006         SpinButton spin_crossfade (1, 0);
6007         spin_crossfade.set_range (0, 15);
6008         spin_crossfade.set_increments (1, 1);
6009         spin_crossfade.set_value (5);
6010         table.attach (spin_crossfade, 1, 2, 0, 1);
6011
6012         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6013
6014         l = manage (new Label (_("Pull-back length")));
6015         l->set_alignment (0, 0.5);
6016         table.attach (*l, 0, 1, 1, 2);
6017
6018         SpinButton spin_pullback (1, 0);
6019         spin_pullback.set_range (0, 100);
6020         spin_pullback.set_increments (1, 1);
6021         spin_pullback.set_value(30);
6022         table.attach (spin_pullback, 1, 2, 1, 2);
6023
6024         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6025
6026         dialog.get_vbox()->pack_start (table);
6027         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6028         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6029         dialog.show_all ();
6030
6031         if (dialog.run () == RESPONSE_CANCEL) {
6032                 return;
6033         }
6034
6035         framepos_t crossfade_len = spin_crossfade.get_value();
6036         framepos_t pull_back_frames = spin_pullback.get_value();
6037
6038         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6039         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6040
6041         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6042
6043         _session->begin_reversible_command (_("close region gaps"));
6044
6045         int idx = 0;
6046         boost::shared_ptr<Region> last_region;
6047
6048         rs.sort_by_position_and_track();
6049
6050         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6051
6052                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6053
6054                 if (!pl->frozen()) {
6055                         /* we haven't seen this playlist before */
6056
6057                         /* remember used playlists so we can thaw them later */
6058                         used_playlists.push_back(pl);
6059                         pl->freeze();
6060                 }
6061
6062                 framepos_t position = (*r)->region()->position();
6063
6064                 if (idx == 0 || position < last_region->position()){
6065                         last_region = (*r)->region();
6066                         idx++;
6067                         continue;
6068                 }
6069
6070                 (*r)->region()->trim_front( (position - pull_back_frames));
6071                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6072
6073                 last_region = (*r)->region();
6074
6075                 idx++;
6076         }
6077
6078         while (used_playlists.size() > 0) {
6079                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6080                 (*i)->thaw();
6081                 used_playlists.pop_front();
6082         }
6083
6084         _session->commit_reversible_command ();
6085 }
6086
6087 void
6088 Editor::tab_to_transient (bool forward)
6089 {
6090         AnalysisFeatureList positions;
6091
6092         RegionSelection rs = get_regions_from_selection_and_entered ();
6093
6094         if (!_session) {
6095                 return;
6096         }
6097
6098         framepos_t pos = _session->audible_frame ();
6099
6100         if (!selection->tracks.empty()) {
6101
6102                 /* don't waste time searching for transients in duplicate playlists.
6103                  */
6104
6105                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6106
6107                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6108
6109                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6110
6111                         if (rtv) {
6112                                 boost::shared_ptr<Track> tr = rtv->track();
6113                                 if (tr) {
6114                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6115                                         if (pl) {
6116                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6117
6118                                                 if (result >= 0) {
6119                                                         positions.push_back (result);
6120                                                 }
6121                                         }
6122                                 }
6123                         }
6124                 }
6125
6126         } else {
6127
6128                 if (rs.empty()) {
6129                         return;
6130                 }
6131
6132                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6133                         (*r)->region()->get_transients (positions);
6134                 }
6135         }
6136
6137         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6138
6139         if (forward) {
6140                 AnalysisFeatureList::iterator x;
6141
6142                 for (x = positions.begin(); x != positions.end(); ++x) {
6143                         if ((*x) > pos) {
6144                                 break;
6145                         }
6146                 }
6147
6148                 if (x != positions.end ()) {
6149                         _session->request_locate (*x);
6150                 }
6151
6152         } else {
6153                 AnalysisFeatureList::reverse_iterator x;
6154
6155                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6156                         if ((*x) < pos) {
6157                                 break;
6158                         }
6159                 }
6160
6161                 if (x != positions.rend ()) {
6162                         _session->request_locate (*x);
6163                 }
6164         }
6165 }
6166
6167 void
6168 Editor::playhead_forward_to_grid ()
6169 {
6170         if (!_session) return;
6171         framepos_t pos = playhead_cursor->current_frame;
6172         if (pos < max_framepos - 1) {
6173                 pos += 2;
6174                 snap_to_internal (pos, 1, false);
6175                 _session->request_locate (pos);
6176         }
6177 }
6178
6179
6180 void
6181 Editor::playhead_backward_to_grid ()
6182 {
6183         if (!_session) return;
6184         framepos_t pos = playhead_cursor->current_frame;
6185         if (pos > 2) {
6186                 pos -= 2;
6187                 snap_to_internal (pos, -1, false);
6188                 _session->request_locate (pos);
6189         }
6190 }
6191
6192 void
6193 Editor::set_track_height (Height h)
6194 {
6195         TrackSelection& ts (selection->tracks);
6196
6197         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6198                 (*x)->set_height_enum (h);
6199         }
6200 }
6201
6202 void
6203 Editor::toggle_tracks_active ()
6204 {
6205         TrackSelection& ts (selection->tracks);
6206         bool first = true;
6207         bool target = false;
6208
6209         if (ts.empty()) {
6210                 return;
6211         }
6212
6213         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6214                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6215
6216                 if (rtv) {
6217                         if (first) {
6218                                 target = !rtv->_route->active();
6219                                 first = false;
6220                         }
6221                         rtv->_route->set_active (target, this);
6222                 }
6223         }
6224 }
6225
6226 void
6227 Editor::remove_tracks ()
6228 {
6229         TrackSelection& ts (selection->tracks);
6230
6231         if (ts.empty()) {
6232                 return;
6233         }
6234
6235         vector<string> choices;
6236         string prompt;
6237         int ntracks = 0;
6238         int nbusses = 0;
6239         const char* trackstr;
6240         const char* busstr;
6241         vector<boost::shared_ptr<Route> > routes;
6242         bool special_bus = false;
6243
6244         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6245                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6246                 if (rtv) {
6247                         if (rtv->is_track()) {
6248                                 ntracks++;
6249                         } else {
6250                                 nbusses++;
6251                         }
6252                 }
6253                 routes.push_back (rtv->_route);
6254
6255                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6256                         special_bus = true;
6257                 }
6258         }
6259
6260         if (special_bus && !Config->get_allow_special_bus_removal()) {
6261                 MessageDialog msg (_("That would be bad news ...."),
6262                                    false,
6263                                    Gtk::MESSAGE_INFO,
6264                                    Gtk::BUTTONS_OK);
6265                 msg.set_secondary_text (string_compose (_(
6266                                                                 "Removing the master or monitor bus is such a bad idea\n\
6267 that %1 is not going to allow it.\n\
6268 \n\
6269 If you really want to do this sort of thing\n\
6270 edit your ardour.rc file to set the\n\
6271 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6272
6273                 msg.present ();
6274                 msg.run ();
6275                 return;
6276         }
6277
6278         if (ntracks + nbusses == 0) {
6279                 return;
6280         }
6281
6282         if (ntracks > 1) {
6283                 trackstr = _("tracks");
6284         } else {
6285                 trackstr = _("track");
6286         }
6287
6288         if (nbusses > 1) {
6289                 busstr = _("busses");
6290         } else {
6291                 busstr = _("bus");
6292         }
6293
6294         if (ntracks) {
6295                 if (nbusses) {
6296                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6297                                                     "(You may also lose the playlists associated with the %2)\n\n"
6298                                                     "This action cannot be undone, and the session file will be overwritten!"),
6299                                                   ntracks, trackstr, nbusses, busstr);
6300                 } else {
6301                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
6302                                                     "(You may also lose the playlists associated with the %2)\n\n"
6303                                                     "This action cannot be undone, and the session file will be overwritten!"),
6304                                                   ntracks, trackstr);
6305                 }
6306         } else if (nbusses) {
6307                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
6308                                             "This action cannot be undon, and the session file will be overwritten"),
6309                                           nbusses, busstr);
6310         }
6311
6312         choices.push_back (_("No, do nothing."));
6313         if (ntracks + nbusses > 1) {
6314                 choices.push_back (_("Yes, remove them."));
6315         } else {
6316                 choices.push_back (_("Yes, remove it."));
6317         }
6318
6319         string title;
6320         if (ntracks) {
6321                 title = string_compose (_("Remove %1"), trackstr);
6322         } else {
6323                 title = string_compose (_("Remove %1"), busstr);
6324         }
6325
6326         Choice prompter (title, prompt, choices);
6327
6328         if (prompter.run () != 1) {
6329                 return;
6330         }
6331
6332         for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6333                 _session->remove_route (*x);
6334         }
6335 }
6336
6337 void
6338 Editor::do_insert_time ()
6339 {
6340         if (selection->tracks.empty()) {
6341                 return;
6342         }
6343
6344         InsertTimeDialog d (*this);
6345         int response = d.run ();
6346
6347         if (response != RESPONSE_OK) {
6348                 return;
6349         }
6350
6351         if (d.distance() == 0) {
6352                 return;
6353         }
6354
6355         InsertTimeOption opt = d.intersected_region_action ();
6356
6357         insert_time (
6358                 get_preferred_edit_position(),
6359                 d.distance(),
6360                 opt,
6361                 d.all_playlists(),
6362                 d.move_glued(),
6363                 d.move_markers(),
6364                 d.move_glued_markers(),
6365                 d.move_locked_markers(),
6366                 d.move_tempos()
6367                 );
6368 }
6369
6370 void
6371 Editor::insert_time (
6372         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6373         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6374         )
6375 {
6376         bool commit = false;
6377
6378         if (Config->get_edit_mode() == Lock) {
6379                 return;
6380         }
6381
6382         begin_reversible_command (_("insert time"));
6383
6384         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6385
6386         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6387
6388                 /* regions */
6389
6390                 /* don't operate on any playlist more than once, which could
6391                  * happen if "all playlists" is enabled, but there is more
6392                  * than 1 track using playlists "from" a given track.
6393                  */
6394
6395                 set<boost::shared_ptr<Playlist> > pl;
6396
6397                 if (all_playlists) {
6398                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6399                         if (rtav) {
6400                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6401                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6402                                         pl.insert (*p);
6403                                 }
6404                         }
6405                 } else {
6406                         if ((*x)->playlist ()) {
6407                                 pl.insert ((*x)->playlist ());
6408                         }
6409                 }
6410                 
6411                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6412
6413                         (*i)->clear_changes ();
6414                         (*i)->clear_owned_changes ();
6415
6416                         if (opt == SplitIntersected) {
6417                                 (*i)->split (pos);
6418                         }
6419
6420                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6421
6422                         vector<Command*> cmds;
6423                         (*i)->rdiff (cmds);
6424                         _session->add_commands (cmds);
6425
6426                         _session->add_command (new StatefulDiffCommand (*i));
6427                         commit = true;
6428                 }
6429
6430                 /* automation */
6431                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6432                 if (rtav) {
6433                         rtav->route ()->shift (pos, frames);
6434                         commit = true;
6435                 }
6436         }
6437
6438         /* markers */
6439         if (markers_too) {
6440                 bool moved = false;
6441                 XMLNode& before (_session->locations()->get_state());
6442                 Locations::LocationList copy (_session->locations()->list());
6443
6444                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6445
6446                         Locations::LocationList::const_iterator tmp;
6447
6448                         bool const was_locked = (*i)->locked ();
6449                         if (locked_markers_too) {
6450                                 (*i)->unlock ();
6451                         }
6452
6453                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6454
6455                                 if ((*i)->start() >= pos) {
6456                                         (*i)->set_start ((*i)->start() + frames);
6457                                         if (!(*i)->is_mark()) {
6458                                                 (*i)->set_end ((*i)->end() + frames);
6459                                         }
6460                                         moved = true;
6461                                 }
6462
6463                         }
6464
6465                         if (was_locked) {
6466                                 (*i)->lock ();
6467                         }
6468                 }
6469
6470                 if (moved) {
6471                         XMLNode& after (_session->locations()->get_state());
6472                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6473                 }
6474         }
6475
6476         if (tempo_too) {
6477                 _session->tempo_map().insert_time (pos, frames);
6478         }
6479
6480         if (commit) {
6481                 commit_reversible_command ();
6482         }
6483 }
6484
6485 void
6486 Editor::fit_selected_tracks ()
6487 {
6488         if (!selection->tracks.empty()) {
6489                 fit_tracks (selection->tracks);
6490         } else {
6491                 TrackViewList tvl;
6492
6493                 /* no selected tracks - use tracks with selected regions */
6494
6495                 if (!selection->regions.empty()) {
6496                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6497                                 tvl.push_back (&(*r)->get_time_axis_view ());
6498                         }
6499
6500                         if (!tvl.empty()) {
6501                                 fit_tracks (tvl);
6502                         }
6503                 } else if (internal_editing()) {
6504                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6505                            the entered track
6506                         */
6507                         if (entered_track) {
6508                                 tvl.push_back (entered_track);
6509                                 fit_tracks (tvl);
6510                         }
6511                 }
6512         }
6513 }
6514
6515 void
6516 Editor::fit_tracks (TrackViewList & tracks)
6517 {
6518         if (tracks.empty()) {
6519                 return;
6520         }
6521
6522         uint32_t child_heights = 0;
6523         int visible_tracks = 0;
6524
6525         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6526
6527                 if (!(*t)->marked_for_display()) {
6528                         continue;
6529                 }
6530
6531                 child_heights += (*t)->effective_height() - (*t)->current_height();
6532                 ++visible_tracks;
6533         }
6534
6535         uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6536         double first_y_pos = DBL_MAX;
6537
6538         if (h < TimeAxisView::preset_height (HeightSmall)) {
6539                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6540                 /* too small to be displayed */
6541                 return;
6542         }
6543
6544         undo_visual_stack.push_back (current_visual_state (true));
6545         no_save_visual = true;
6546
6547         /* build a list of all tracks, including children */
6548
6549         TrackViewList all;
6550         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6551                 all.push_back (*i);
6552                 TimeAxisView::Children c = (*i)->get_child_list ();
6553                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6554                         all.push_back (j->get());
6555                 }
6556         }
6557
6558         /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6559
6560         bool prev_was_selected = false;
6561         bool is_selected = tracks.contains (all.front());
6562         bool next_is_selected;
6563
6564         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6565
6566                 TrackViewList::iterator next;
6567
6568                 next = t;
6569                 ++next;
6570
6571                 if (next != all.end()) {
6572                         next_is_selected = tracks.contains (*next);
6573                 } else {
6574                         next_is_selected = false;
6575                 }
6576
6577                 if ((*t)->marked_for_display ()) {
6578                         if (is_selected) {
6579                                 (*t)->set_height (h);
6580                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6581                         } else {
6582                                 if (prev_was_selected && next_is_selected) {
6583                                         hide_track_in_display (*t);
6584                                 }
6585                         }
6586                 }
6587
6588                 prev_was_selected = is_selected;
6589                 is_selected = next_is_selected;
6590         }
6591
6592         /*
6593            set the controls_layout height now, because waiting for its size
6594            request signal handler will cause the vertical adjustment setting to fail
6595         */
6596
6597         controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6598         vertical_adjustment.set_value (first_y_pos);
6599
6600         redo_visual_stack.push_back (current_visual_state (true));
6601 }
6602
6603 void
6604 Editor::save_visual_state (uint32_t n)
6605 {
6606         while (visual_states.size() <= n) {
6607                 visual_states.push_back (0);
6608         }
6609
6610         if (visual_states[n] != 0) {
6611                 delete visual_states[n];
6612         }
6613
6614         visual_states[n] = current_visual_state (true);
6615         gdk_beep ();
6616 }
6617
6618 void
6619 Editor::goto_visual_state (uint32_t n)
6620 {
6621         if (visual_states.size() <= n) {
6622                 return;
6623         }
6624
6625         if (visual_states[n] == 0) {
6626                 return;
6627         }
6628
6629         use_visual_state (*visual_states[n]);
6630 }
6631
6632 void
6633 Editor::start_visual_state_op (uint32_t n)
6634 {
6635         save_visual_state (n);
6636         
6637         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6638         char buf[32];
6639         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6640         pup->set_text (buf);
6641         pup->touch();
6642 }
6643
6644 void
6645 Editor::cancel_visual_state_op (uint32_t n)
6646 {
6647         goto_visual_state (n);
6648 }
6649
6650 void
6651 Editor::toggle_region_mute ()
6652 {
6653         if (_ignore_region_action) {
6654                 return;
6655         }
6656
6657         RegionSelection rs = get_regions_from_selection_and_entered ();
6658
6659         if (rs.empty ()) {
6660                 return;
6661         }
6662
6663         if (rs.size() > 1) {
6664                 begin_reversible_command (_("mute regions"));
6665         } else {
6666                 begin_reversible_command (_("mute region"));
6667         }
6668
6669         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6670
6671                 (*i)->region()->playlist()->clear_changes ();
6672                 (*i)->region()->set_muted (!(*i)->region()->muted ());
6673                 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6674
6675         }
6676
6677         commit_reversible_command ();
6678 }
6679
6680 void
6681 Editor::combine_regions ()
6682 {
6683         /* foreach track with selected regions, take all selected regions
6684            and join them into a new region containing the subregions (as a
6685            playlist)
6686         */
6687
6688         typedef set<RouteTimeAxisView*> RTVS;
6689         RTVS tracks;
6690
6691         if (selection->regions.empty()) {
6692                 return;
6693         }
6694
6695         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6696                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6697
6698                 if (rtv) {
6699                         tracks.insert (rtv);
6700                 }
6701         }
6702
6703         begin_reversible_command (_("combine regions"));
6704
6705         vector<RegionView*> new_selection;
6706
6707         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6708                 RegionView* rv;
6709
6710                 if ((rv = (*i)->combine_regions ()) != 0) {
6711                         new_selection.push_back (rv);
6712                 }
6713         }
6714
6715         selection->clear_regions ();
6716         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6717                 selection->add (*i);
6718         }
6719
6720         commit_reversible_command ();
6721 }
6722
6723 void
6724 Editor::uncombine_regions ()
6725 {
6726         typedef set<RouteTimeAxisView*> RTVS;
6727         RTVS tracks;
6728
6729         if (selection->regions.empty()) {
6730                 return;
6731         }
6732
6733         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6734                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6735
6736                 if (rtv) {
6737                         tracks.insert (rtv);
6738                 }
6739         }
6740
6741         begin_reversible_command (_("uncombine regions"));
6742
6743         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6744                 (*i)->uncombine_regions ();
6745         }
6746
6747         commit_reversible_command ();
6748 }
6749