Fix crash when removing time from track with automation
[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 <limits>
28 #include <map>
29 #include <set>
30
31 #include "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
38
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
42
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
62
63 #include "canvas/canvas.h"
64
65 #include "actions.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
72 #include "debug.h"
73 #include "editing.h"
74 #include "editor.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
83 #include "keyboard.h"
84 #include "midi_region_view.h"
85 #include "mixer_ui.h"
86 #include "mixer_strip.h"
87 #include "mouse_cursors.h"
88 #include "normalize_dialog.h"
89 #include "note.h"
90 #include "paste_context.h"
91 #include "patch_change_dialog.h"
92 #include "quantize_dialog.h"
93 #include "region_gain_line.h"
94 #include "rgb_macros.h"
95 #include "route_time_axis.h"
96 #include "selection.h"
97 #include "selection_templates.h"
98 #include "streamview.h"
99 #include "strip_silence_dialog.h"
100 #include "time_axis_view.h"
101 #include "timers.h"
102 #include "transpose_dialog.h"
103 #include "transform_dialog.h"
104 #include "ui_config.h"
105
106 #include "pbd/i18n.h"
107
108 using namespace std;
109 using namespace ARDOUR;
110 using namespace PBD;
111 using namespace Gtk;
112 using namespace Gtkmm2ext;
113 using namespace Editing;
114 using Gtkmm2ext::Keyboard;
115
116 /***********************************************************************
117   Editor operations
118  ***********************************************************************/
119
120 void
121 Editor::undo (uint32_t n)
122 {
123         if (_session && _session->actively_recording()) {
124                 /* no undo allowed while recording. Session will check also,
125                    but we don't even want to get to that.
126                 */
127                 return;
128         }
129
130         if (_drags->active ()) {
131                 _drags->abort ();
132         }
133
134         if (_session) {
135                 _session->undo (n);
136                 if (_session->undo_depth() == 0) {
137                         undo_action->set_sensitive(false);
138                 }
139                 redo_action->set_sensitive(true);
140                 begin_selection_op_history ();
141         }
142 }
143
144 void
145 Editor::redo (uint32_t n)
146 {
147         if (_session && _session->actively_recording()) {
148                 /* no redo allowed while recording. Session will check also,
149                    but we don't even want to get to that.
150                 */
151                 return;
152         }
153
154         if (_drags->active ()) {
155                 _drags->abort ();
156         }
157
158         if (_session) {
159         _session->redo (n);
160                 if (_session->redo_depth() == 0) {
161                         redo_action->set_sensitive(false);
162                 }
163                 undo_action->set_sensitive(true);
164                 begin_selection_op_history ();
165         }
166 }
167
168 void
169 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
170 {
171         bool frozen = false;
172
173         RegionSelection pre_selected_regions = selection->regions;
174         bool working_on_selection = !pre_selected_regions.empty();
175
176         list<boost::shared_ptr<Playlist> > used_playlists;
177         list<RouteTimeAxisView*> used_trackviews;
178
179         if (regions.empty()) {
180                 return;
181         }
182
183         begin_reversible_command (_("split"));
184
185         // if splitting a single region, and snap-to is using
186         // region boundaries, don't pay attention to them
187
188         if (regions.size() == 1) {
189                 switch (_snap_type) {
190                 case SnapToRegionStart:
191                 case SnapToRegionSync:
192                 case SnapToRegionEnd:
193                         break;
194                 default:
195                         if (snap_frame) {
196                                 snap_to (where);
197                         }
198                 }
199         } else {
200                 if (snap_frame) {
201                         snap_to (where);
202                 }
203
204                 frozen = true;
205                 EditorFreeze(); /* Emit Signal */
206         }
207
208         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
209
210                 RegionSelection::iterator tmp;
211
212                 /* XXX this test needs to be more complicated, to make sure we really
213                    have something to split.
214                 */
215
216                 if (!(*a)->region()->covers (where.frame)) {
217                         ++a;
218                         continue;
219                 }
220
221                 tmp = a;
222                 ++tmp;
223
224                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
225
226                 if (!pl) {
227                         a = tmp;
228                         continue;
229                 }
230
231                 if (!pl->frozen()) {
232                         /* we haven't seen this playlist before */
233
234                         /* remember used playlists so we can thaw them later */
235                         used_playlists.push_back(pl);
236
237                         TimeAxisView& tv = (*a)->get_time_axis_view();
238                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
239                         if (rtv) {
240                                 used_trackviews.push_back (rtv);
241                         }
242                         pl->freeze();
243                 }
244
245
246                 if (pl) {
247                         pl->clear_changes ();
248                         pl->split_region ((*a)->region(), where);
249                         _session->add_command (new StatefulDiffCommand (pl));
250                 }
251
252                 a = tmp;
253         }
254
255         latest_regionviews.clear ();
256
257         vector<sigc::connection> region_added_connections;
258
259         for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
260                 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
261         }
262
263         while (used_playlists.size() > 0) {
264                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
265                 (*i)->thaw();
266                 used_playlists.pop_front();
267         }
268
269         for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
270                 (*c).disconnect ();
271         }
272
273         if (frozen){
274                 EditorThaw(); /* Emit Signal */
275         }
276
277         if (working_on_selection) {
278                 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
279
280                 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
281                 /* There are three classes of regions that we might want selected after
282                    splitting selected regions:
283                     - regions selected before the split operation, and unaffected by it
284                     - newly-created regions before the split
285                     - newly-created regions after the split
286                  */
287
288                 if (rsas & Existing) {
289                         // region selections that existed before the split.
290                         selection->add ( pre_selected_regions );
291                 }
292
293                 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
294                         if ((*ri)->region()->position() < where.frame) {
295                                 // new regions created before the split
296                                 if (rsas & NewlyCreatedLeft) {
297                                         selection->add (*ri);
298                                 }
299                         } else {
300                                 // new regions created after the split
301                                 if (rsas & NewlyCreatedRight) {
302                                         selection->add (*ri);
303                                 }
304                         }
305                 }
306         } else {
307                 if( working_on_selection ) {
308                         selection->add (latest_regionviews);  //these are the new regions created after the split
309                 }
310         }
311
312         commit_reversible_command ();
313 }
314
315 /** Move one extreme of the current range selection.  If more than one range is selected,
316  *  the start of the earliest range or the end of the latest range is moved.
317  *
318  *  @param move_end true to move the end of the current range selection, false to move
319  *  the start.
320  *  @param next true to move the extreme to the next region boundary, false to move to
321  *  the previous.
322  */
323 void
324 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
325 {
326         if (selection->time.start() == selection->time.end_frame()) {
327                 return;
328         }
329
330         framepos_t start = selection->time.start ();
331         framepos_t end = selection->time.end_frame ();
332
333         /* the position of the thing we may move */
334         framepos_t pos = move_end ? end : start;
335         int dir = next ? 1 : -1;
336
337         /* so we don't find the current region again */
338         if (dir > 0 || pos > 0) {
339                 pos += dir;
340         }
341
342         framepos_t const target = get_region_boundary (pos, dir, true, false);
343         if (target < 0) {
344                 return;
345         }
346
347         if (move_end) {
348                 end = target;
349         } else {
350                 start = target;
351         }
352
353         if (end < start) {
354                 return;
355         }
356
357         begin_reversible_selection_op (_("alter selection"));
358         selection->set_preserving_all_ranges (start, end);
359         commit_reversible_selection_op ();
360 }
361
362 bool
363 Editor::nudge_forward_release (GdkEventButton* ev)
364 {
365         if (ev->state & Keyboard::PrimaryModifier) {
366                 nudge_forward (false, true);
367         } else {
368                 nudge_forward (false, false);
369         }
370         return false;
371 }
372
373 bool
374 Editor::nudge_backward_release (GdkEventButton* ev)
375 {
376         if (ev->state & Keyboard::PrimaryModifier) {
377                 nudge_backward (false, true);
378         } else {
379                 nudge_backward (false, false);
380         }
381         return false;
382 }
383
384
385 void
386 Editor::nudge_forward (bool next, bool force_playhead)
387 {
388         framepos_t distance;
389         framepos_t next_distance;
390
391         if (!_session) {
392                 return;
393         }
394
395         RegionSelection rs = get_regions_from_selection_and_entered ();
396
397         if (!force_playhead && !rs.empty()) {
398
399                 begin_reversible_command (_("nudge regions forward"));
400
401                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
402                         boost::shared_ptr<Region> r ((*i)->region());
403
404                         distance = get_nudge_distance (r->position(), next_distance);
405
406                         if (next) {
407                                 distance = next_distance;
408                         }
409
410                         r->clear_changes ();
411                         r->set_position (r->position() + distance);
412                         _session->add_command (new StatefulDiffCommand (r));
413                 }
414
415                 commit_reversible_command ();
416
417
418         } else if (!force_playhead && !selection->markers.empty()) {
419
420                 bool is_start;
421                 bool in_command = false;
422                 const int32_t divisions = get_grid_music_divisions (0);
423
424                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
425
426                         Location* loc = find_location_from_marker ((*i), is_start);
427
428                         if (loc) {
429
430                                 XMLNode& before (loc->get_state());
431
432                                 if (is_start) {
433                                         distance = get_nudge_distance (loc->start(), next_distance);
434                                         if (next) {
435                                                 distance = next_distance;
436                                         }
437                                         if (max_framepos - distance > loc->start() + loc->length()) {
438                                                 loc->set_start (loc->start() + distance, false, true, divisions);
439                                         } else {
440                                                 loc->set_start (max_framepos - loc->length(), false, true, divisions);
441                                         }
442                                 } else {
443                                         distance = get_nudge_distance (loc->end(), next_distance);
444                                         if (next) {
445                                                 distance = next_distance;
446                                         }
447                                         if (max_framepos - distance > loc->end()) {
448                                                 loc->set_end (loc->end() + distance, false, true, divisions);
449                                         } else {
450                                                 loc->set_end (max_framepos, false, true, divisions);
451                                         }
452                                         if (loc->is_session_range()) {
453                                                 _session->set_end_is_free (false);
454                                         }
455                                 }
456                                 if (!in_command) {
457                                         begin_reversible_command (_("nudge location forward"));
458                                         in_command = true;
459                                 }
460                                 XMLNode& after (loc->get_state());
461                                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
462                         }
463                 }
464
465                 if (in_command) {
466                         commit_reversible_command ();
467                 }
468         } else {
469                 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
470                 _session->request_locate (playhead_cursor->current_frame () + distance);
471         }
472 }
473
474 void
475 Editor::nudge_backward (bool next, bool force_playhead)
476 {
477         framepos_t distance;
478         framepos_t next_distance;
479
480         if (!_session) {
481                 return;
482         }
483
484         RegionSelection rs = get_regions_from_selection_and_entered ();
485
486         if (!force_playhead && !rs.empty()) {
487
488                 begin_reversible_command (_("nudge regions backward"));
489
490                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
491                         boost::shared_ptr<Region> r ((*i)->region());
492
493                         distance = get_nudge_distance (r->position(), next_distance);
494
495                         if (next) {
496                                 distance = next_distance;
497                         }
498
499                         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         } else if (!force_playhead && !selection->markers.empty()) {
512
513                 bool is_start;
514                 bool in_command = false;
515
516                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
517
518                         Location* loc = find_location_from_marker ((*i), is_start);
519
520                         if (loc) {
521
522                                 XMLNode& before (loc->get_state());
523
524                                 if (is_start) {
525                                         distance = get_nudge_distance (loc->start(), next_distance);
526                                         if (next) {
527                                                 distance = next_distance;
528                                         }
529                                         if (distance < loc->start()) {
530                                                 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
531                                         } else {
532                                                 loc->set_start (0, false, true, get_grid_music_divisions(0));
533                                         }
534                                 } else {
535                                         distance = get_nudge_distance (loc->end(), next_distance);
536
537                                         if (next) {
538                                                 distance = next_distance;
539                                         }
540
541                                         if (distance < loc->end() - loc->length()) {
542                                                 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
543                                         } else {
544                                                 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
545                                         }
546                                         if (loc->is_session_range()) {
547                                                 _session->set_end_is_free (false);
548                                         }
549                                 }
550                                 if (!in_command) {
551                                         begin_reversible_command (_("nudge location forward"));
552                                         in_command = true;
553                                 }
554                                 XMLNode& after (loc->get_state());
555                                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
556                         }
557                 }
558                 if (in_command) {
559                         commit_reversible_command ();
560                 }
561
562         } else {
563
564                 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
565
566                 if (playhead_cursor->current_frame () > distance) {
567                         _session->request_locate (playhead_cursor->current_frame () - distance);
568                 } else {
569                         _session->goto_start();
570                 }
571         }
572 }
573
574 void
575 Editor::nudge_forward_capture_offset ()
576 {
577         RegionSelection rs = get_regions_from_selection_and_entered ();
578
579         if (!_session || rs.empty()) {
580                 return;
581         }
582
583         begin_reversible_command (_("nudge forward"));
584
585         framepos_t const distance = _session->worst_output_latency();
586
587         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
588                 boost::shared_ptr<Region> r ((*i)->region());
589
590                 r->clear_changes ();
591                 r->set_position (r->position() + distance);
592                 _session->add_command(new StatefulDiffCommand (r));
593         }
594
595         commit_reversible_command ();
596 }
597
598 void
599 Editor::nudge_backward_capture_offset ()
600 {
601         RegionSelection rs = get_regions_from_selection_and_entered ();
602
603         if (!_session || rs.empty()) {
604                 return;
605         }
606
607         begin_reversible_command (_("nudge backward"));
608
609         framepos_t const distance = _session->worst_output_latency();
610
611         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
612                 boost::shared_ptr<Region> r ((*i)->region());
613
614                 r->clear_changes ();
615
616                 if (r->position() > distance) {
617                         r->set_position (r->position() - distance);
618                 } else {
619                         r->set_position (0);
620                 }
621                 _session->add_command(new StatefulDiffCommand (r));
622         }
623
624         commit_reversible_command ();
625 }
626
627 struct RegionSelectionPositionSorter {
628         bool operator() (RegionView* a, RegionView* b) {
629                 return a->region()->position() < b->region()->position();
630         }
631 };
632
633 void
634 Editor::sequence_regions ()
635 {
636         framepos_t r_end;
637         framepos_t r_end_prev;
638
639         int iCount=0;
640
641         if (!_session) {
642                 return;
643         }
644
645         RegionSelection rs = get_regions_from_selection_and_entered ();
646         rs.sort(RegionSelectionPositionSorter());
647
648         if (!rs.empty()) {
649
650                 bool in_command = false;
651
652                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
653                         boost::shared_ptr<Region> r ((*i)->region());
654
655                         r->clear_changes();
656
657                         if(r->locked())
658                         {
659                                 continue;
660                         }
661                         if(r->position_locked())
662                         {
663                                 continue;
664                         }
665                         if(iCount>0)
666                         {
667                                 r_end_prev=r_end;
668                                 r->set_position(r_end_prev);
669                         }
670
671                         if (!in_command) {
672                                 begin_reversible_command (_("sequence regions"));
673                                 in_command = true;
674                         }
675                         _session->add_command (new StatefulDiffCommand (r));
676
677                         r_end=r->position() + r->length();
678
679                         iCount++;
680                 }
681
682                 if (in_command) {
683                         commit_reversible_command ();
684                 }
685         }
686 }
687
688
689 /* DISPLAY MOTION */
690
691 void
692 Editor::move_to_start ()
693 {
694         _session->goto_start ();
695 }
696
697 void
698 Editor::move_to_end ()
699 {
700
701         _session->request_locate (_session->current_end_frame());
702 }
703
704 void
705 Editor::build_region_boundary_cache ()
706 {
707         framepos_t pos = 0;
708         vector<RegionPoint> interesting_points;
709         boost::shared_ptr<Region> r;
710         TrackViewList tracks;
711         bool at_end = false;
712
713         region_boundary_cache.clear ();
714
715         if (_session == 0) {
716                 return;
717         }
718
719         switch (_snap_type) {
720         case SnapToRegionStart:
721                 interesting_points.push_back (Start);
722                 break;
723         case SnapToRegionEnd:
724                 interesting_points.push_back (End);
725                 break;
726         case SnapToRegionSync:
727                 interesting_points.push_back (SyncPoint);
728                 break;
729         case SnapToRegionBoundary:
730                 interesting_points.push_back (Start);
731                 interesting_points.push_back (End);
732                 break;
733         default:
734                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
735                 abort(); /*NOTREACHED*/
736                 return;
737         }
738
739         TimeAxisView *ontrack = 0;
740         TrackViewList tlist;
741
742         if (!selection->tracks.empty()) {
743                 tlist = selection->tracks.filter_to_unique_playlists ();
744         } else {
745                 tlist = track_views.filter_to_unique_playlists ();
746         }
747
748         while (pos < _session->current_end_frame() && !at_end) {
749
750                 framepos_t rpos;
751                 framepos_t lpos = max_framepos;
752
753                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
754
755                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
756                                 if (*p == interesting_points.back()) {
757                                         at_end = true;
758                                 }
759                                 /* move to next point type */
760                                 continue;
761                         }
762
763                         switch (*p) {
764                         case Start:
765                                 rpos = r->first_frame();
766                                 break;
767
768                         case End:
769                                 rpos = r->last_frame();
770                                 break;
771
772                         case SyncPoint:
773                                 rpos = r->sync_position ();
774                                 break;
775
776                         default:
777                                 break;
778                         }
779
780                         float speed = 1.0f;
781                         RouteTimeAxisView *rtav;
782
783                         if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
784                                 if (rtav->track() != 0) {
785                                         speed = rtav->track()->speed();
786                                 }
787                         }
788
789                         rpos = track_frame_to_session_frame (rpos, speed);
790
791                         if (rpos < lpos) {
792                                 lpos = rpos;
793                         }
794
795                         /* prevent duplicates, but we don't use set<> because we want to be able
796                            to sort later.
797                         */
798
799                         vector<framepos_t>::iterator ri;
800
801                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
802                                 if (*ri == rpos) {
803                                         break;
804                                 }
805                         }
806
807                         if (ri == region_boundary_cache.end()) {
808                                 region_boundary_cache.push_back (rpos);
809                         }
810                 }
811
812                 pos = lpos + 1;
813         }
814
815         /* finally sort to be sure that the order is correct */
816
817         sort (region_boundary_cache.begin(), region_boundary_cache.end());
818 }
819
820 boost::shared_ptr<Region>
821 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
822 {
823         TrackViewList::iterator i;
824         framepos_t closest = max_framepos;
825         boost::shared_ptr<Region> ret;
826         framepos_t rpos = 0;
827
828         float track_speed;
829         framepos_t track_frame;
830         RouteTimeAxisView *rtav;
831
832         for (i = tracks.begin(); i != tracks.end(); ++i) {
833
834                 framecnt_t distance;
835                 boost::shared_ptr<Region> r;
836
837                 track_speed = 1.0f;
838                 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
839                         if (rtav->track()!=0)
840                                 track_speed = rtav->track()->speed();
841                 }
842
843                 track_frame = session_frame_to_track_frame(frame, track_speed);
844
845                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
846                         continue;
847                 }
848
849                 switch (point) {
850                 case Start:
851                         rpos = r->first_frame ();
852                         break;
853
854                 case End:
855                         rpos = r->last_frame ();
856                         break;
857
858                 case SyncPoint:
859                         rpos = r->sync_position ();
860                         break;
861                 }
862
863                 // rpos is a "track frame", converting it to "_session frame"
864                 rpos = track_frame_to_session_frame(rpos, track_speed);
865
866                 if (rpos > frame) {
867                         distance = rpos - frame;
868                 } else {
869                         distance = frame - rpos;
870                 }
871
872                 if (distance < closest) {
873                         closest = distance;
874                         if (ontrack != 0)
875                                 *ontrack = (*i);
876                         ret = r;
877                 }
878         }
879
880         return ret;
881 }
882
883 framepos_t
884 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
885 {
886         framecnt_t distance = max_framepos;
887         framepos_t current_nearest = -1;
888
889         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
890                 framepos_t contender;
891                 framecnt_t d;
892
893                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
894
895                 if (!rtv) {
896                         continue;
897                 }
898
899                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
900                         continue;
901                 }
902
903                 d = ::llabs (pos - contender);
904
905                 if (d < distance) {
906                         current_nearest = contender;
907                         distance = d;
908                 }
909         }
910
911         return current_nearest;
912 }
913
914 framepos_t
915 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
916 {
917         framepos_t target;
918         TrackViewList tvl;
919
920         if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
921
922                 if (!selection->tracks.empty()) {
923
924                         target = find_next_region_boundary (pos, dir, selection->tracks);
925
926                 } else {
927
928                         if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
929                                 get_onscreen_tracks (tvl);
930                                 target = find_next_region_boundary (pos, dir, tvl);
931                         } else {
932                                 target = find_next_region_boundary (pos, dir, track_views);
933                         }
934                 }
935
936         } else {
937
938                 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
939                         get_onscreen_tracks (tvl);
940                         target = find_next_region_boundary (pos, dir, tvl);
941                 } else {
942                         target = find_next_region_boundary (pos, dir, track_views);
943                 }
944         }
945
946         return target;
947 }
948
949 void
950 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
951 {
952         framepos_t pos = playhead_cursor->current_frame ();
953         framepos_t target;
954
955         if (!_session) {
956                 return;
957         }
958
959         // so we don't find the current region again..
960         if (dir > 0 || pos > 0) {
961                 pos += dir;
962         }
963
964         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
965                 return;
966         }
967
968         _session->request_locate (target);
969 }
970
971 void
972 Editor::cursor_to_next_region_boundary (bool with_selection)
973 {
974         cursor_to_region_boundary (with_selection, 1);
975 }
976
977 void
978 Editor::cursor_to_previous_region_boundary (bool with_selection)
979 {
980         cursor_to_region_boundary (with_selection, -1);
981 }
982
983 void
984 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
985 {
986         boost::shared_ptr<Region> r;
987         framepos_t pos = cursor->current_frame ();
988
989         if (!_session) {
990                 return;
991         }
992
993         TimeAxisView *ontrack = 0;
994
995         // so we don't find the current region again..
996         if (dir>0 || pos>0)
997                 pos+=dir;
998
999         if (!selection->tracks.empty()) {
1000
1001                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1002
1003         } else if (clicked_axisview) {
1004
1005                 TrackViewList t;
1006                 t.push_back (clicked_axisview);
1007
1008                 r = find_next_region (pos, point, dir, t, &ontrack);
1009
1010         } else {
1011
1012                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1013         }
1014
1015         if (r == 0) {
1016                 return;
1017         }
1018
1019         switch (point) {
1020         case Start:
1021                 pos = r->first_frame ();
1022                 break;
1023
1024         case End:
1025                 pos = r->last_frame ();
1026                 break;
1027
1028         case SyncPoint:
1029                 pos = r->sync_position ();
1030                 break;
1031         }
1032
1033         float speed = 1.0f;
1034         RouteTimeAxisView *rtav;
1035
1036         if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1037                 if (rtav->track() != 0) {
1038                         speed = rtav->track()->speed();
1039                 }
1040         }
1041
1042         pos = track_frame_to_session_frame(pos, speed);
1043
1044         if (cursor == playhead_cursor) {
1045                 _session->request_locate (pos);
1046         } else {
1047                 cursor->set_position (pos);
1048         }
1049 }
1050
1051 void
1052 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1053 {
1054         cursor_to_region_point (cursor, point, 1);
1055 }
1056
1057 void
1058 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1059 {
1060         cursor_to_region_point (cursor, point, -1);
1061 }
1062
1063 void
1064 Editor::cursor_to_selection_start (EditorCursor *cursor)
1065 {
1066         framepos_t pos = 0;
1067
1068         switch (mouse_mode) {
1069         case MouseObject:
1070                 if (!selection->regions.empty()) {
1071                         pos = selection->regions.start();
1072                 }
1073                 break;
1074
1075         case MouseRange:
1076                 if (!selection->time.empty()) {
1077                         pos = selection->time.start ();
1078                 }
1079                 break;
1080
1081         default:
1082                 return;
1083         }
1084
1085         if (cursor == playhead_cursor) {
1086                 _session->request_locate (pos);
1087         } else {
1088                 cursor->set_position (pos);
1089         }
1090 }
1091
1092 void
1093 Editor::cursor_to_selection_end (EditorCursor *cursor)
1094 {
1095         framepos_t pos = 0;
1096
1097         switch (mouse_mode) {
1098         case MouseObject:
1099                 if (!selection->regions.empty()) {
1100                         pos = selection->regions.end_frame();
1101                 }
1102                 break;
1103
1104         case MouseRange:
1105                 if (!selection->time.empty()) {
1106                         pos = selection->time.end_frame ();
1107                 }
1108                 break;
1109
1110         default:
1111                 return;
1112         }
1113
1114         if (cursor == playhead_cursor) {
1115                 _session->request_locate (pos);
1116         } else {
1117                 cursor->set_position (pos);
1118         }
1119 }
1120
1121 void
1122 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1123 {
1124         framepos_t target;
1125         Location* loc;
1126         bool ignored;
1127
1128         if (!_session) {
1129                 return;
1130         }
1131
1132         if (selection->markers.empty()) {
1133                 framepos_t mouse;
1134                 bool ignored;
1135
1136                 if (!mouse_frame (mouse, ignored)) {
1137                         return;
1138                 }
1139
1140                 add_location_mark (mouse);
1141         }
1142
1143         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1144                 return;
1145         }
1146
1147         framepos_t pos = loc->start();
1148
1149         // so we don't find the current region again..
1150         if (dir > 0 || pos > 0) {
1151                 pos += dir;
1152         }
1153
1154         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1155                 return;
1156         }
1157
1158         loc->move_to (target, 0);
1159 }
1160
1161 void
1162 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1163 {
1164         selected_marker_to_region_boundary (with_selection, 1);
1165 }
1166
1167 void
1168 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1169 {
1170         selected_marker_to_region_boundary (with_selection, -1);
1171 }
1172
1173 void
1174 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1175 {
1176         boost::shared_ptr<Region> r;
1177         framepos_t pos;
1178         Location* loc;
1179         bool ignored;
1180
1181         if (!_session || selection->markers.empty()) {
1182                 return;
1183         }
1184
1185         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1186                 return;
1187         }
1188
1189         TimeAxisView *ontrack = 0;
1190
1191         pos = loc->start();
1192
1193         // so we don't find the current region again..
1194         if (dir>0 || pos>0)
1195                 pos+=dir;
1196
1197         if (!selection->tracks.empty()) {
1198
1199                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1200
1201         } else {
1202
1203                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1204         }
1205
1206         if (r == 0) {
1207                 return;
1208         }
1209
1210         switch (point) {
1211         case Start:
1212                 pos = r->first_frame ();
1213                 break;
1214
1215         case End:
1216                 pos = r->last_frame ();
1217                 break;
1218
1219         case SyncPoint:
1220                 pos = r->adjust_to_sync (r->first_frame());
1221                 break;
1222         }
1223
1224         float speed = 1.0f;
1225         RouteTimeAxisView *rtav;
1226
1227         if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1228                 if (rtav->track() != 0) {
1229                         speed = rtav->track()->speed();
1230                 }
1231         }
1232
1233         pos = track_frame_to_session_frame(pos, speed);
1234
1235         loc->move_to (pos, 0);
1236 }
1237
1238 void
1239 Editor::selected_marker_to_next_region_point (RegionPoint point)
1240 {
1241         selected_marker_to_region_point (point, 1);
1242 }
1243
1244 void
1245 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1246 {
1247         selected_marker_to_region_point (point, -1);
1248 }
1249
1250 void
1251 Editor::selected_marker_to_selection_start ()
1252 {
1253         framepos_t pos = 0;
1254         Location* loc;
1255         bool ignored;
1256
1257         if (!_session || selection->markers.empty()) {
1258                 return;
1259         }
1260
1261         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1262                 return;
1263         }
1264
1265         switch (mouse_mode) {
1266         case MouseObject:
1267                 if (!selection->regions.empty()) {
1268                         pos = selection->regions.start();
1269                 }
1270                 break;
1271
1272         case MouseRange:
1273                 if (!selection->time.empty()) {
1274                         pos = selection->time.start ();
1275                 }
1276                 break;
1277
1278         default:
1279                 return;
1280         }
1281
1282         loc->move_to (pos, 0);
1283 }
1284
1285 void
1286 Editor::selected_marker_to_selection_end ()
1287 {
1288         framepos_t pos = 0;
1289         Location* loc;
1290         bool ignored;
1291
1292         if (!_session || selection->markers.empty()) {
1293                 return;
1294         }
1295
1296         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1297                 return;
1298         }
1299
1300         switch (mouse_mode) {
1301         case MouseObject:
1302                 if (!selection->regions.empty()) {
1303                         pos = selection->regions.end_frame();
1304                 }
1305                 break;
1306
1307         case MouseRange:
1308                 if (!selection->time.empty()) {
1309                         pos = selection->time.end_frame ();
1310                 }
1311                 break;
1312
1313         default:
1314                 return;
1315         }
1316
1317         loc->move_to (pos, 0);
1318 }
1319
1320 void
1321 Editor::scroll_playhead (bool forward)
1322 {
1323         framepos_t pos = playhead_cursor->current_frame ();
1324         framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1325
1326         if (forward) {
1327                 if (pos == max_framepos) {
1328                         return;
1329                 }
1330
1331                 if (pos < max_framepos - delta) {
1332                         pos += delta ;
1333                 } else {
1334                         pos = max_framepos;
1335                 }
1336
1337         } else {
1338
1339                 if (pos == 0) {
1340                         return;
1341                 }
1342
1343                 if (pos > delta) {
1344                         pos -= delta;
1345                 } else {
1346                         pos = 0;
1347                 }
1348         }
1349
1350         _session->request_locate (pos);
1351 }
1352
1353 void
1354 Editor::cursor_align (bool playhead_to_edit)
1355 {
1356         if (!_session) {
1357                 return;
1358         }
1359
1360         if (playhead_to_edit) {
1361
1362                 if (selection->markers.empty()) {
1363                         return;
1364                 }
1365
1366                 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1367
1368         } else {
1369                 const int32_t divisions = get_grid_music_divisions (0);
1370                 /* move selected markers to playhead */
1371
1372                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1373                         bool ignored;
1374
1375                         Location* loc = find_location_from_marker (*i, ignored);
1376
1377                         if (loc->is_mark()) {
1378                                 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1379                         } else {
1380                                 loc->set (playhead_cursor->current_frame (),
1381                                           playhead_cursor->current_frame () + loc->length(), true, divisions);
1382                         }
1383                 }
1384         }
1385 }
1386
1387 void
1388 Editor::scroll_backward (float pages)
1389 {
1390         framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1391         framepos_t const cnt = (framepos_t) floor (pages * one_page);
1392
1393         framepos_t frame;
1394         if (leftmost_frame < cnt) {
1395                 frame = 0;
1396         } else {
1397                 frame = leftmost_frame - cnt;
1398         }
1399
1400         reset_x_origin (frame);
1401 }
1402
1403 void
1404 Editor::scroll_forward (float pages)
1405 {
1406         framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1407         framepos_t const cnt = (framepos_t) floor (pages * one_page);
1408
1409         framepos_t frame;
1410         if (max_framepos - cnt < leftmost_frame) {
1411                 frame = max_framepos - cnt;
1412         } else {
1413                 frame = leftmost_frame + cnt;
1414         }
1415
1416         reset_x_origin (frame);
1417 }
1418
1419 void
1420 Editor::scroll_tracks_down ()
1421 {
1422         double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1423         if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1424                 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1425         }
1426
1427         vertical_adjustment.set_value (vert_value);
1428 }
1429
1430 void
1431 Editor::scroll_tracks_up ()
1432 {
1433         vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1434 }
1435
1436 void
1437 Editor::scroll_tracks_down_line ()
1438 {
1439         double vert_value = vertical_adjustment.get_value() + 60;
1440
1441         if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1442                 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1443         }
1444
1445         vertical_adjustment.set_value (vert_value);
1446 }
1447
1448 void
1449 Editor::scroll_tracks_up_line ()
1450 {
1451         reset_y_origin (vertical_adjustment.get_value() - 60);
1452 }
1453
1454 bool
1455 Editor::scroll_down_one_track (bool skip_child_views)
1456 {
1457         TrackViewList::reverse_iterator next = track_views.rend();
1458         const double top_of_trackviews = vertical_adjustment.get_value();
1459
1460         for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1461                 if ((*t)->hidden()) {
1462                         continue;
1463                 }
1464
1465                 /* If this is the upper-most visible trackview, we want to display
1466                  * the one above it (next)
1467                  *
1468                  * Note that covers_y_position() is recursive and includes child views
1469                  */
1470                 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1471
1472                 if (res.first) {
1473                         if (skip_child_views) {
1474                                 break;
1475                         }
1476                         /* automation lane (one level, non-recursive)
1477                          *
1478                          * - if no automation lane exists -> move to next tack
1479                          * - if the first (here: bottom-most) matches -> move to next tack
1480                          * - if no y-axis match is found -> the current track is at the top
1481                          *     -> move to last (here: top-most) automation lane
1482                          */
1483                         TimeAxisView::Children kids = (*t)->get_child_list();
1484                         TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1485
1486                         for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1487                                 if ((*ci)->hidden()) {
1488                                         continue;
1489                                 }
1490
1491                                 std::pair<TimeAxisView*,double> dev;
1492                                 dev = (*ci)->covers_y_position (top_of_trackviews);
1493                                 if (dev.first) {
1494                                         /* some automation lane is currently at the top */
1495                                         if (ci == kids.rbegin()) {
1496                                                 /* first (bottom-most) autmation lane is at the top.
1497                                                  * -> move to next track
1498                                                  */
1499                                                 nkid = kids.rend();
1500                                         }
1501                                         break;
1502                                 }
1503                                 nkid = ci;
1504                         }
1505
1506                         if (nkid != kids.rend()) {
1507                                 ensure_time_axis_view_is_visible (**nkid, true);
1508                                 return true;
1509                         }
1510                         break;
1511                 }
1512                 next = t;
1513         }
1514
1515         /* move to the track below the first one that covers the */
1516
1517         if (next != track_views.rend()) {
1518                 ensure_time_axis_view_is_visible (**next, true);
1519                 return true;
1520         }
1521
1522         return false;
1523 }
1524
1525 bool
1526 Editor::scroll_up_one_track (bool skip_child_views)
1527 {
1528         TrackViewList::iterator prev = track_views.end();
1529         double top_of_trackviews = vertical_adjustment.get_value ();
1530
1531         for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1532
1533                 if ((*t)->hidden()) {
1534                         continue;
1535                 }
1536
1537                 /* find the trackview at the top of the trackview group
1538                  *
1539                  * Note that covers_y_position() is recursive and includes child views
1540                  */
1541                 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1542
1543                 if (res.first) {
1544                         if (skip_child_views) {
1545                                 break;
1546                         }
1547                         /* automation lane (one level, non-recursive)
1548                          *
1549                          * - if no automation lane exists -> move to prev tack
1550                          * - if no y-axis match is found -> the current track is at the top -> move to prev track
1551                          *     (actually last automation lane of previous track, see below)
1552                          * - if first (top-most) lane is at the top -> move to this track
1553                          * - else move up one lane
1554                          */
1555                         TimeAxisView::Children kids = (*t)->get_child_list();
1556                         TimeAxisView::Children::iterator pkid = kids.end();
1557
1558                         for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1559                                 if ((*ci)->hidden()) {
1560                                         continue;
1561                                 }
1562
1563                                 std::pair<TimeAxisView*,double> dev;
1564                                 dev = (*ci)->covers_y_position (top_of_trackviews);
1565                                 if (dev.first) {
1566                                         /* some automation lane is currently at the top */
1567                                         if (ci == kids.begin()) {
1568                                                 /* first (top-most) autmation lane is at the top.
1569                                                  * jump directly to this track's top
1570                                                  */
1571                                                 ensure_time_axis_view_is_visible (**t, true);
1572                                                 return true;
1573                                         }
1574                                         else if (pkid != kids.end()) {
1575                                                 /* some other automation lane is at the top.
1576                                                  * move up to prev automation lane.
1577                                                  */
1578                                                 ensure_time_axis_view_is_visible (**pkid, true);
1579                                                 return true;
1580                                         }
1581                                         assert(0); // not reached
1582                                         break;
1583                                 }
1584                                 pkid = ci;
1585                         }
1586                         break;
1587                 }
1588
1589                 prev = t;
1590         }
1591
1592         if (prev != track_views.end()) {
1593                 // move to bottom-most automation-lane of the previous track
1594                 TimeAxisView::Children kids = (*prev)->get_child_list();
1595                 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1596                 if (!skip_child_views) {
1597                         // find the last visible lane
1598                         for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1599                                 if (!(*ci)->hidden()) {
1600                                         pkid = ci;
1601                                         break;
1602                                 }
1603                         }
1604                 }
1605                 if (pkid != kids.rend()) {
1606                         ensure_time_axis_view_is_visible (**pkid, true);
1607                 } else  {
1608                         ensure_time_axis_view_is_visible (**prev, true);
1609                 }
1610                 return true;
1611         }
1612
1613         return false;
1614 }
1615
1616 void
1617 Editor::scroll_left_step ()
1618 {
1619         framepos_t xdelta = (current_page_samples() / 8);
1620
1621         if (leftmost_frame > xdelta) {
1622                 reset_x_origin (leftmost_frame - xdelta);
1623         } else {
1624                 reset_x_origin (0);
1625         }
1626 }
1627
1628
1629 void
1630 Editor::scroll_right_step ()
1631 {
1632         framepos_t xdelta = (current_page_samples() / 8);
1633
1634         if (max_framepos - xdelta > leftmost_frame) {
1635                 reset_x_origin (leftmost_frame + xdelta);
1636         } else {
1637                 reset_x_origin (max_framepos - current_page_samples());
1638         }
1639 }
1640
1641 void
1642 Editor::scroll_left_half_page ()
1643 {
1644         framepos_t xdelta = (current_page_samples() / 2);
1645         if (leftmost_frame > xdelta) {
1646                 reset_x_origin (leftmost_frame - xdelta);
1647         } else {
1648                 reset_x_origin (0);
1649         }
1650 }
1651
1652 void
1653 Editor::scroll_right_half_page ()
1654 {
1655         framepos_t xdelta = (current_page_samples() / 2);
1656         if (max_framepos - xdelta > leftmost_frame) {
1657                 reset_x_origin (leftmost_frame + xdelta);
1658         } else {
1659                 reset_x_origin (max_framepos - current_page_samples());
1660         }
1661 }
1662
1663 /* ZOOM */
1664
1665 void
1666 Editor::tav_zoom_step (bool coarser)
1667 {
1668         DisplaySuspender ds;
1669
1670         TrackViewList* ts;
1671
1672         if (selection->tracks.empty()) {
1673                 ts = &track_views;
1674         } else {
1675                 ts = &selection->tracks;
1676         }
1677
1678         for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1679                 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1680                         tv->step_height (coarser);
1681         }
1682 }
1683
1684 void
1685 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1686 {
1687         DisplaySuspender ds;
1688
1689         TrackViewList* ts;
1690
1691         if (selection->tracks.empty() || force_all) {
1692                 ts = &track_views;
1693         } else {
1694                 ts = &selection->tracks;
1695         }
1696
1697         for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1698                 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1699                 uint32_t h = tv->current_height ();
1700
1701                 if (coarser) {
1702                         if (h > 5) {
1703                                 h -= 5; // pixels
1704                                 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1705                                         tv->set_height (h);
1706                                 }
1707                         }
1708                 } else {
1709                         tv->set_height (h + 5);
1710                 }
1711         }
1712 }
1713
1714 void
1715 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1716 {
1717         Editing::ZoomFocus temp_focus = zoom_focus;
1718         zoom_focus = Editing::ZoomFocusMouse;
1719         temporal_zoom_step_scale (zoom_out, scale);
1720         zoom_focus = temp_focus;
1721 }
1722
1723 void
1724 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1725 {
1726         temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1727 }
1728
1729 void
1730 Editor::temporal_zoom_step (bool zoom_out)
1731 {
1732         temporal_zoom_step_scale (zoom_out, 2.0);
1733 }
1734
1735 void
1736 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1737 {
1738         ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1739
1740         framecnt_t nspp = samples_per_pixel;
1741
1742         if (zoom_out) {
1743                 nspp *= scale;
1744                 if (nspp == samples_per_pixel) {
1745                         nspp *= 2.0;
1746                 }
1747         } else {
1748                 nspp /= scale;
1749                 if (nspp == samples_per_pixel) {
1750                         nspp /= 2.0;
1751                 }
1752         }
1753
1754         temporal_zoom (nspp);
1755 }
1756
1757 void
1758 Editor::temporal_zoom (framecnt_t fpp)
1759 {
1760         if (!_session) {
1761                 return;
1762         }
1763
1764         framepos_t current_page = current_page_samples();
1765         framepos_t current_leftmost = leftmost_frame;
1766         framepos_t current_rightmost;
1767         framepos_t current_center;
1768         framepos_t new_page_size;
1769         framepos_t half_page_size;
1770         framepos_t leftmost_after_zoom = 0;
1771         framepos_t where;
1772         bool in_track_canvas;
1773         bool use_mouse_frame = true;
1774         framecnt_t nfpp;
1775         double l;
1776
1777         if (fpp == samples_per_pixel) {
1778                 return;
1779         }
1780
1781         // Imposing an arbitrary limit to zoom out as too much zoom out produces
1782         // segfaults for lack of memory. If somebody decides this is not high enough I
1783         // believe it can be raisen to higher values but some limit must be in place.
1784         //
1785         // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1786         // all of which is used for the editor track displays. The whole day
1787         // would be 4147200000 samples, so 2592000 samples per pixel.
1788
1789         nfpp = min (fpp, (framecnt_t) 2592000);
1790         nfpp = max ((framecnt_t) 1, nfpp);
1791
1792         new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1793         half_page_size = new_page_size / 2;
1794
1795         switch (zoom_focus) {
1796         case ZoomFocusLeft:
1797                 leftmost_after_zoom = current_leftmost;
1798                 break;
1799
1800         case ZoomFocusRight:
1801                 current_rightmost = leftmost_frame + current_page;
1802                 if (current_rightmost < new_page_size) {
1803                         leftmost_after_zoom = 0;
1804                 } else {
1805                         leftmost_after_zoom = current_rightmost - new_page_size;
1806                 }
1807                 break;
1808
1809         case ZoomFocusCenter:
1810                 current_center = current_leftmost + (current_page/2);
1811                 if (current_center < half_page_size) {
1812                         leftmost_after_zoom = 0;
1813                 } else {
1814                         leftmost_after_zoom = current_center - half_page_size;
1815                 }
1816                 break;
1817
1818         case ZoomFocusPlayhead:
1819                 /* centre playhead */
1820                 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1821
1822                 if (l < 0) {
1823                         leftmost_after_zoom = 0;
1824                 } else if (l > max_framepos) {
1825                         leftmost_after_zoom = max_framepos - new_page_size;
1826                 } else {
1827                         leftmost_after_zoom = (framepos_t) l;
1828                 }
1829                 break;
1830
1831         case ZoomFocusMouse:
1832                 /* try to keep the mouse over the same point in the display */
1833
1834                 if (_drags->active()) {
1835                         where = _drags->current_pointer_frame ();
1836                 } else if (!mouse_frame (where, in_track_canvas)) {
1837                         use_mouse_frame = false;
1838                 }
1839
1840                 if (use_mouse_frame) {
1841                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1842
1843                         if (l < 0) {
1844                                 leftmost_after_zoom = 0;
1845                         } else if (l > max_framepos) {
1846                                 leftmost_after_zoom = max_framepos - new_page_size;
1847                         } else {
1848                                 leftmost_after_zoom = (framepos_t) l;
1849                         }
1850                 } else {
1851                         /* use playhead instead */
1852                         where = playhead_cursor->current_frame ();
1853
1854                         if (where < half_page_size) {
1855                                 leftmost_after_zoom = 0;
1856                         } else {
1857                                 leftmost_after_zoom = where - half_page_size;
1858                         }
1859                 }
1860                 break;
1861
1862         case ZoomFocusEdit:
1863                 /* try to keep the edit point in the same place */
1864                 where = get_preferred_edit_position ();
1865
1866                 if (where > 0) {
1867
1868                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1869
1870                         if (l < 0) {
1871                                 leftmost_after_zoom = 0;
1872                         } else if (l > max_framepos) {
1873                                 leftmost_after_zoom = max_framepos - new_page_size;
1874                         } else {
1875                                 leftmost_after_zoom = (framepos_t) l;
1876                         }
1877
1878                 } else {
1879                         /* edit point not defined */
1880                         return;
1881                 }
1882                 break;
1883
1884         }
1885
1886         // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1887
1888         reposition_and_zoom (leftmost_after_zoom, nfpp);
1889 }
1890
1891 void
1892 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1893 {
1894         /* this func helps make sure we leave a little space
1895            at each end of the editor so that the zoom doesn't fit the region
1896            precisely to the screen.
1897         */
1898
1899         GdkScreen* screen = gdk_screen_get_default ();
1900         const gint pixwidth = gdk_screen_get_width (screen);
1901         const gint mmwidth = gdk_screen_get_width_mm (screen);
1902         const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1903         const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1904
1905         const framepos_t range = end - start;
1906         const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1907         const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1908
1909         if (start > extra_samples) {
1910                 start -= extra_samples;
1911         } else {
1912                 start = 0;
1913         }
1914
1915         if (max_framepos - extra_samples > end) {
1916                 end += extra_samples;
1917         } else {
1918                 end = max_framepos;
1919         }
1920 }
1921
1922 bool
1923 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1924 {
1925         start = max_framepos;
1926         end = 0;
1927         bool ret = true;
1928
1929         //ToDo:  if notes are selected, set extents to that selection
1930
1931         //ToDo:  if control points are selected, set extents to that selection
1932
1933         if ( !selection->regions.empty() ) {
1934                 RegionSelection rs = get_regions_from_selection_and_entered ();
1935
1936                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1937
1938                         if ((*i)->region()->position() < start) {
1939                                 start = (*i)->region()->position();
1940                         }
1941
1942                         if ((*i)->region()->last_frame() + 1 > end) {
1943                                 end = (*i)->region()->last_frame() + 1;
1944                         }
1945                 }
1946
1947         } else if (!selection->time.empty()) {
1948                 start = selection->time.start();
1949                 end = selection->time.end_frame();
1950         } else
1951                 ret = false;  //no selection found
1952
1953         //range check
1954         if ((start == 0 && end == 0) || end < start) {
1955                 ret = false;
1956         }
1957
1958         return ret;
1959 }
1960
1961
1962 void
1963 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1964 {
1965         if (!selection) return;
1966
1967         //ToDo:  if notes are selected, zoom to that
1968
1969         //ToDo:  if control points are selected, zoom to that
1970
1971         if (axes == Horizontal || axes == Both) {
1972
1973                 framepos_t start, end;
1974                 if (get_selection_extents (start, end)) {
1975                         calc_extra_zoom_edges (start, end);
1976                         temporal_zoom_by_frame (start, end);
1977                 }
1978         }
1979
1980         if (axes == Vertical || axes == Both) {
1981                 fit_selection ();
1982         }
1983 }
1984
1985 void
1986 Editor::temporal_zoom_session ()
1987 {
1988         ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1989
1990         if (_session) {
1991                 framecnt_t start = _session->current_start_frame();
1992                 framecnt_t end = _session->current_end_frame();
1993
1994                 if (_session->actively_recording () ) {
1995                         framepos_t cur = playhead_cursor->current_frame ();
1996                         if (cur > end) {
1997                                 /* recording beyond the end marker; zoom out
1998                                  * by 5 seconds more so that if 'follow
1999                                  * playhead' is active we don't immediately
2000                                  * scroll.
2001                                  */
2002                                 end = cur + _session->frame_rate() * 5;
2003                         }
2004                 }
2005
2006                 if ((start == 0 && end == 0) || end < start) {
2007                         return;
2008                 }
2009
2010                 calc_extra_zoom_edges(start, end);
2011
2012                 temporal_zoom_by_frame (start, end);
2013         }
2014 }
2015
2016 void
2017 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2018 {
2019         if (!_session) return;
2020
2021         if ((start == 0 && end == 0) || end < start) {
2022                 return;
2023         }
2024
2025         framepos_t range = end - start;
2026
2027         const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2028
2029         framepos_t new_page = range;
2030         framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2031         framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2032
2033         if (new_leftmost > middle) {
2034                 new_leftmost = 0;
2035         }
2036
2037         if (new_leftmost < 0) {
2038                 new_leftmost = 0;
2039         }
2040
2041         reposition_and_zoom (new_leftmost, new_fpp);
2042 }
2043
2044 void
2045 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2046 {
2047         if (!_session) {
2048                 return;
2049         }
2050
2051         framecnt_t range_before = frame - leftmost_frame;
2052         framecnt_t new_spp;
2053
2054         if (coarser) {
2055                 if (samples_per_pixel <= 1) {
2056                         new_spp = 2;
2057                 } else {
2058                         new_spp = samples_per_pixel + (samples_per_pixel/2);
2059                 }
2060                 range_before += range_before/2;
2061         } else {
2062                 if (samples_per_pixel >= 1) {
2063                         new_spp = samples_per_pixel - (samples_per_pixel/2);
2064                 } else {
2065                         /* could bail out here since we cannot zoom any finer,
2066                            but leave that to the equality test below
2067                         */
2068                         new_spp = samples_per_pixel;
2069                 }
2070
2071                 range_before -= range_before/2;
2072         }
2073
2074         if (new_spp == samples_per_pixel)  {
2075                 return;
2076         }
2077
2078         /* zoom focus is automatically taken as @param frame when this
2079            method is used.
2080         */
2081
2082         framepos_t new_leftmost = frame - (framepos_t)range_before;
2083
2084         if (new_leftmost > frame) {
2085                 new_leftmost = 0;
2086         }
2087
2088         if (new_leftmost < 0) {
2089                 new_leftmost = 0;
2090         }
2091
2092         reposition_and_zoom (new_leftmost, new_spp);
2093 }
2094
2095
2096 bool
2097 Editor::choose_new_marker_name(string &name) {
2098
2099         if (!UIConfiguration::instance().get_name_new_markers()) {
2100                 /* don't prompt user for a new name */
2101                 return true;
2102         }
2103
2104         ArdourPrompter dialog (true);
2105
2106         dialog.set_prompt (_("New Name:"));
2107
2108         dialog.set_title (_("New Location Marker"));
2109
2110         dialog.set_name ("MarkNameWindow");
2111         dialog.set_size_request (250, -1);
2112         dialog.set_position (Gtk::WIN_POS_MOUSE);
2113
2114         dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2115         dialog.set_initial_text (name);
2116
2117         dialog.show ();
2118
2119         switch (dialog.run ()) {
2120         case RESPONSE_ACCEPT:
2121                 break;
2122         default:
2123                 return false;
2124         }
2125
2126         dialog.get_result(name);
2127         return true;
2128
2129 }
2130
2131
2132 void
2133 Editor::add_location_from_selection ()
2134 {
2135         string rangename;
2136
2137         if (selection->time.empty()) {
2138                 return;
2139         }
2140
2141         if (_session == 0 || clicked_axisview == 0) {
2142                 return;
2143         }
2144
2145         framepos_t start = selection->time[clicked_selection].start;
2146         framepos_t end = selection->time[clicked_selection].end;
2147
2148         _session->locations()->next_available_name(rangename,"selection");
2149         Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2150
2151         begin_reversible_command (_("add marker"));
2152
2153         XMLNode &before = _session->locations()->get_state();
2154         _session->locations()->add (location, true);
2155         XMLNode &after = _session->locations()->get_state();
2156         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2157
2158         commit_reversible_command ();
2159 }
2160
2161 void
2162 Editor::add_location_mark (framepos_t where)
2163 {
2164         string markername;
2165
2166         select_new_marker = true;
2167
2168         _session->locations()->next_available_name(markername,"mark");
2169         if (!choose_new_marker_name(markername)) {
2170                 return;
2171         }
2172         Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2173         begin_reversible_command (_("add marker"));
2174
2175         XMLNode &before = _session->locations()->get_state();
2176         _session->locations()->add (location, true);
2177         XMLNode &after = _session->locations()->get_state();
2178         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2179
2180         commit_reversible_command ();
2181 }
2182
2183 void
2184 Editor::set_session_start_from_playhead ()
2185 {
2186         if (!_session)
2187                 return;
2188
2189         Location* loc;
2190         if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
2191                 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2192         } else {
2193                 XMLNode &before = loc->get_state();
2194
2195                 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2196
2197                 XMLNode &after = loc->get_state();
2198
2199                 begin_reversible_command (_("Set session start"));
2200
2201                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2202
2203                 commit_reversible_command ();
2204         }
2205 }
2206
2207 void
2208 Editor::set_session_end_from_playhead ()
2209 {
2210         if (!_session)
2211                 return;
2212
2213         Location* loc;
2214         if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
2215                 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2216         } else {
2217                 XMLNode &before = loc->get_state();
2218
2219                 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2220
2221                 XMLNode &after = loc->get_state();
2222
2223                 begin_reversible_command (_("Set session start"));
2224
2225                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2226
2227                 commit_reversible_command ();
2228         }
2229
2230         _session->set_end_is_free (false);
2231 }
2232
2233
2234 void
2235 Editor::toggle_location_at_playhead_cursor ()
2236 {
2237         if (!do_remove_location_at_playhead_cursor())
2238         {
2239                 add_location_from_playhead_cursor();
2240         }
2241 }
2242
2243 void
2244 Editor::add_location_from_playhead_cursor ()
2245 {
2246         add_location_mark (_session->audible_frame());
2247 }
2248
2249 bool
2250 Editor::do_remove_location_at_playhead_cursor ()
2251 {
2252         bool removed = false;
2253         if (_session) {
2254                 //set up for undo
2255                 XMLNode &before = _session->locations()->get_state();
2256
2257                 //find location(s) at this time
2258                 Locations::LocationList locs;
2259                 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2260                 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2261                         if ((*i)->is_mark()) {
2262                                 _session->locations()->remove (*i);
2263                                 removed = true;
2264                         }
2265                 }
2266
2267                 //store undo
2268                 if (removed) {
2269                         begin_reversible_command (_("remove marker"));
2270                         XMLNode &after = _session->locations()->get_state();
2271                         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2272                         commit_reversible_command ();
2273                 }
2274         }
2275         return removed;
2276 }
2277
2278 void
2279 Editor::remove_location_at_playhead_cursor ()
2280 {
2281         do_remove_location_at_playhead_cursor ();
2282 }
2283
2284 /** Add a range marker around each selected region */
2285 void
2286 Editor::add_locations_from_region ()
2287 {
2288         RegionSelection rs = get_regions_from_selection_and_entered ();
2289
2290         if (rs.empty()) {
2291                 return;
2292         }
2293         bool commit = false;
2294
2295         XMLNode &before = _session->locations()->get_state();
2296
2297         for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2298
2299                 boost::shared_ptr<Region> region = (*i)->region ();
2300
2301                 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2302
2303                 _session->locations()->add (location, true);
2304                 commit = true;
2305         }
2306
2307         if (commit) {
2308                 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2309                 XMLNode &after = _session->locations()->get_state();
2310                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2311                 commit_reversible_command ();
2312         }
2313 }
2314
2315 /** Add a single range marker around all selected regions */
2316 void
2317 Editor::add_location_from_region ()
2318 {
2319         RegionSelection rs = get_regions_from_selection_and_entered ();
2320
2321         if (rs.empty()) {
2322                 return;
2323         }
2324
2325         XMLNode &before = _session->locations()->get_state();
2326
2327         string markername;
2328
2329         if (rs.size() > 1) {
2330                 _session->locations()->next_available_name(markername, "regions");
2331         } else {
2332                 RegionView* rv = *(rs.begin());
2333                 boost::shared_ptr<Region> region = rv->region();
2334                 markername = region->name();
2335         }
2336
2337         if (!choose_new_marker_name(markername)) {
2338                 return;
2339         }
2340
2341         // single range spanning all selected
2342         Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2343         _session->locations()->add (location, true);
2344
2345         begin_reversible_command (_("add marker"));
2346         XMLNode &after = _session->locations()->get_state();
2347         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2348         commit_reversible_command ();
2349 }
2350
2351 /* MARKS */
2352
2353 void
2354 Editor::jump_forward_to_mark ()
2355 {
2356         if (!_session) {
2357                 return;
2358         }
2359
2360         framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2361
2362         if (pos < 0) {
2363                 return;
2364         }
2365
2366         _session->request_locate (pos, _session->transport_rolling());
2367 }
2368
2369 void
2370 Editor::jump_backward_to_mark ()
2371 {
2372         if (!_session) {
2373                 return;
2374         }
2375
2376         framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2377
2378         //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2379         if ( _session->transport_rolling() ) {
2380                 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2381                         framepos_t prior = _session->locations()->first_mark_before ( pos );
2382                         pos = prior;
2383                 }
2384         }
2385
2386         if (pos < 0) {
2387                 return;
2388         }
2389
2390         _session->request_locate (pos, _session->transport_rolling());
2391 }
2392
2393 void
2394 Editor::set_mark ()
2395 {
2396         framepos_t const pos = _session->audible_frame ();
2397
2398         string markername;
2399         _session->locations()->next_available_name (markername, "mark");
2400
2401         if (!choose_new_marker_name (markername)) {
2402                 return;
2403         }
2404
2405         _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2406 }
2407
2408 void
2409 Editor::clear_markers ()
2410 {
2411         if (_session) {
2412                 begin_reversible_command (_("clear markers"));
2413
2414                 XMLNode &before = _session->locations()->get_state();
2415                 _session->locations()->clear_markers ();
2416                 XMLNode &after = _session->locations()->get_state();
2417                 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2418
2419                 commit_reversible_command ();
2420         }
2421 }
2422
2423 void
2424 Editor::clear_ranges ()
2425 {
2426         if (_session) {
2427                 begin_reversible_command (_("clear ranges"));
2428
2429                 XMLNode &before = _session->locations()->get_state();
2430
2431                 _session->locations()->clear_ranges ();
2432
2433                 XMLNode &after = _session->locations()->get_state();
2434                 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2435
2436                 commit_reversible_command ();
2437         }
2438 }
2439
2440 void
2441 Editor::clear_locations ()
2442 {
2443         begin_reversible_command (_("clear locations"));
2444
2445         XMLNode &before = _session->locations()->get_state();
2446         _session->locations()->clear ();
2447         XMLNode &after = _session->locations()->get_state();
2448         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2449
2450         commit_reversible_command ();
2451 }
2452
2453 void
2454 Editor::unhide_markers ()
2455 {
2456         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2457                 Location *l = (*i).first;
2458                 if (l->is_hidden() && l->is_mark()) {
2459                         l->set_hidden(false, this);
2460                 }
2461         }
2462 }
2463
2464 void
2465 Editor::unhide_ranges ()
2466 {
2467         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2468                 Location *l = (*i).first;
2469                 if (l->is_hidden() && l->is_range_marker()) {
2470                         l->set_hidden(false, this);
2471                 }
2472         }
2473 }
2474
2475 /* INSERT/REPLACE */
2476
2477 void
2478 Editor::insert_region_list_selection (float times)
2479 {
2480         RouteTimeAxisView *tv = 0;
2481         boost::shared_ptr<Playlist> playlist;
2482
2483         if (clicked_routeview != 0) {
2484                 tv = clicked_routeview;
2485         } else if (!selection->tracks.empty()) {
2486                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2487                         return;
2488                 }
2489         } else if (entered_track != 0) {
2490                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2491                         return;
2492                 }
2493         } else {
2494                 return;
2495         }
2496
2497         if ((playlist = tv->playlist()) == 0) {
2498                 return;
2499         }
2500
2501         boost::shared_ptr<Region> region = _regions->get_single_selection ();
2502         if (region == 0) {
2503                 return;
2504         }
2505
2506         begin_reversible_command (_("insert region"));
2507         playlist->clear_changes ();
2508         playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2509         if (Config->get_edit_mode() == Ripple)
2510                 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2511
2512         _session->add_command(new StatefulDiffCommand (playlist));
2513         commit_reversible_command ();
2514 }
2515
2516 /* BUILT-IN EFFECTS */
2517
2518 void
2519 Editor::reverse_selection ()
2520 {
2521
2522 }
2523
2524 /* GAIN ENVELOPE EDITING */
2525
2526 void
2527 Editor::edit_envelope ()
2528 {
2529 }
2530
2531 /* PLAYBACK */
2532
2533 void
2534 Editor::transition_to_rolling (bool fwd)
2535 {
2536         if (!_session) {
2537                 return;
2538         }
2539
2540         if (_session->config.get_external_sync()) {
2541                 switch (Config->get_sync_source()) {
2542                 case Engine:
2543                         break;
2544                 default:
2545                         /* transport controlled by the master */
2546                         return;
2547                 }
2548         }
2549
2550         if (_session->is_auditioning()) {
2551                 _session->cancel_audition ();
2552                 return;
2553         }
2554
2555         _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2556 }
2557
2558 void
2559 Editor::play_from_start ()
2560 {
2561         _session->request_locate (_session->current_start_frame(), true);
2562 }
2563
2564 void
2565 Editor::play_from_edit_point ()
2566 {
2567         _session->request_locate (get_preferred_edit_position(), true);
2568 }
2569
2570 void
2571 Editor::play_from_edit_point_and_return ()
2572 {
2573         framepos_t start_frame;
2574         framepos_t return_frame;
2575
2576         start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2577
2578         if (_session->transport_rolling()) {
2579                 _session->request_locate (start_frame, false);
2580                 return;
2581         }
2582
2583         /* don't reset the return frame if its already set */
2584
2585         if ((return_frame = _session->requested_return_frame()) < 0) {
2586                 return_frame = _session->audible_frame();
2587         }
2588
2589         if (start_frame >= 0) {
2590                 _session->request_roll_at_and_return (start_frame, return_frame);
2591         }
2592 }
2593
2594 void
2595 Editor::play_selection ()
2596 {
2597         framepos_t start, end;
2598         if (!get_selection_extents ( start, end))
2599                 return;
2600
2601         AudioRange ar (start, end, 0);
2602         list<AudioRange> lar;
2603         lar.push_back (ar);
2604
2605         _session->request_play_range (&lar, true);
2606 }
2607
2608
2609 void
2610 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2611 {
2612         if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2613                 return;
2614
2615         location -= _session->preroll_samples (location);
2616
2617         //don't try to locate before the beginning of time
2618         if (location < 0) {
2619                 location = 0;
2620         }
2621
2622         //if follow_playhead is on, keep the playhead on the screen
2623         if ( _follow_playhead )
2624                 if ( location < leftmost_frame )
2625                         location = leftmost_frame;
2626
2627         _session->request_locate( location );
2628 }
2629
2630 void
2631 Editor::play_with_preroll ()
2632 {
2633         framepos_t start, end;
2634         if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2635                 const framepos_t preroll = _session->preroll_samples (start);
2636
2637                 framepos_t ret = start;
2638
2639                 if (start > preroll) {
2640                         start = start - preroll;
2641                 }
2642
2643                 end = end + preroll;  //"post-roll"
2644
2645                 AudioRange ar (start, end, 0);
2646                 list<AudioRange> lar;
2647                 lar.push_back (ar);
2648
2649                 _session->request_play_range (&lar, true);
2650                 _session->set_requested_return_frame (ret);  //force auto-return to return to range start, without the preroll
2651         } else {
2652                 framepos_t ph = playhead_cursor->current_frame ();
2653                 const framepos_t preroll = _session->preroll_samples (ph);
2654                 framepos_t start;
2655                 if (ph > preroll) {
2656                         start = ph - preroll;
2657                 } else {
2658                         start = 0;
2659                 }
2660                 _session->request_locate (start, true);
2661                 _session->set_requested_return_frame (ph);  //force auto-return to return to playhead location, without the preroll
2662         }
2663 }
2664
2665 void
2666 Editor::rec_with_preroll ()
2667 {
2668         framepos_t ph = playhead_cursor->current_frame ();
2669         framepos_t preroll = _session->preroll_samples (ph);
2670         _session->request_preroll_record_trim (ph, preroll);
2671 }
2672
2673 void
2674 Editor::rec_with_count_in ()
2675 {
2676         _session->request_count_in_record ();
2677 }
2678
2679 void
2680 Editor::play_location (Location& location)
2681 {
2682         if (location.start() <= location.end()) {
2683                 return;
2684         }
2685
2686         _session->request_bounded_roll (location.start(), location.end());
2687 }
2688
2689 void
2690 Editor::loop_location (Location& location)
2691 {
2692         if (location.start() <= location.end()) {
2693                 return;
2694         }
2695
2696         Location* tll;
2697
2698         if ((tll = transport_loop_location()) != 0) {
2699                 tll->set (location.start(), location.end());
2700
2701                 // enable looping, reposition and start rolling
2702                 _session->request_locate (tll->start(), true);
2703                 _session->request_play_loop (true);
2704         }
2705 }
2706
2707 void
2708 Editor::do_layer_operation (LayerOperation op)
2709 {
2710         if (selection->regions.empty ()) {
2711                 return;
2712         }
2713
2714         bool const multiple = selection->regions.size() > 1;
2715         switch (op) {
2716         case Raise:
2717                 if (multiple) {
2718                         begin_reversible_command (_("raise regions"));
2719                 } else {
2720                         begin_reversible_command (_("raise region"));
2721                 }
2722                 break;
2723
2724         case RaiseToTop:
2725                 if (multiple) {
2726                         begin_reversible_command (_("raise regions to top"));
2727                 } else {
2728                         begin_reversible_command (_("raise region to top"));
2729                 }
2730                 break;
2731
2732         case Lower:
2733                 if (multiple) {
2734                         begin_reversible_command (_("lower regions"));
2735                 } else {
2736                         begin_reversible_command (_("lower region"));
2737                 }
2738                 break;
2739
2740         case LowerToBottom:
2741                 if (multiple) {
2742                         begin_reversible_command (_("lower regions to bottom"));
2743                 } else {
2744                         begin_reversible_command (_("lower region"));
2745                 }
2746                 break;
2747         }
2748
2749         set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2750         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2751                 (*i)->clear_owned_changes ();
2752         }
2753
2754         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2755                 boost::shared_ptr<Region> r = (*i)->region ();
2756                 switch (op) {
2757                 case Raise:
2758                         r->raise ();
2759                         break;
2760                 case RaiseToTop:
2761                         r->raise_to_top ();
2762                         break;
2763                 case Lower:
2764                         r->lower ();
2765                         break;
2766                 case LowerToBottom:
2767                         r->lower_to_bottom ();
2768                 }
2769         }
2770
2771         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2772                 vector<Command*> cmds;
2773                 (*i)->rdiff (cmds);
2774                 _session->add_commands (cmds);
2775         }
2776
2777         commit_reversible_command ();
2778 }
2779
2780 void
2781 Editor::raise_region ()
2782 {
2783         do_layer_operation (Raise);
2784 }
2785
2786 void
2787 Editor::raise_region_to_top ()
2788 {
2789         do_layer_operation (RaiseToTop);
2790 }
2791
2792 void
2793 Editor::lower_region ()
2794 {
2795         do_layer_operation (Lower);
2796 }
2797
2798 void
2799 Editor::lower_region_to_bottom ()
2800 {
2801         do_layer_operation (LowerToBottom);
2802 }
2803
2804 /** Show the region editor for the selected regions */
2805 void
2806 Editor::show_region_properties ()
2807 {
2808         selection->foreach_regionview (&RegionView::show_region_editor);
2809 }
2810
2811 /** Show the midi list editor for the selected MIDI regions */
2812 void
2813 Editor::show_midi_list_editor ()
2814 {
2815         selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2816 }
2817
2818 void
2819 Editor::rename_region ()
2820 {
2821         RegionSelection rs = get_regions_from_selection_and_entered ();
2822
2823         if (rs.empty()) {
2824                 return;
2825         }
2826
2827         ArdourDialog d (_("Rename Region"), true, false);
2828         Entry entry;
2829         Label label (_("New name:"));
2830         HBox hbox;
2831
2832         hbox.set_spacing (6);
2833         hbox.pack_start (label, false, false);
2834         hbox.pack_start (entry, true, true);
2835
2836         d.get_vbox()->set_border_width (12);
2837         d.get_vbox()->pack_start (hbox, false, false);
2838
2839         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2840         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2841
2842         d.set_size_request (300, -1);
2843
2844         entry.set_text (rs.front()->region()->name());
2845         entry.select_region (0, -1);
2846
2847         entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2848
2849         d.show_all ();
2850
2851         entry.grab_focus();
2852
2853         int const ret = d.run();
2854
2855         d.hide ();
2856
2857         if (ret != RESPONSE_OK) {
2858                 return;
2859         }
2860
2861         std::string str = entry.get_text();
2862         strip_whitespace_edges (str);
2863         if (!str.empty()) {
2864                 rs.front()->region()->set_name (str);
2865                 _regions->redisplay ();
2866         }
2867 }
2868
2869 /** Start an audition of the first selected region */
2870 void
2871 Editor::play_edit_range ()
2872 {
2873         framepos_t start, end;
2874
2875         if (get_edit_op_range (start, end)) {
2876                 _session->request_bounded_roll (start, end);
2877         }
2878 }
2879
2880 void
2881 Editor::play_selected_region ()
2882 {
2883         framepos_t start = max_framepos;
2884         framepos_t end = 0;
2885
2886         RegionSelection rs = get_regions_from_selection_and_entered ();
2887
2888         if (rs.empty()) {
2889                 return;
2890         }
2891
2892         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2893                 if ((*i)->region()->position() < start) {
2894                         start = (*i)->region()->position();
2895                 }
2896                 if ((*i)->region()->last_frame() + 1 > end) {
2897                         end = (*i)->region()->last_frame() + 1;
2898                 }
2899         }
2900
2901         _session->request_bounded_roll (start, end);
2902 }
2903
2904 void
2905 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2906 {
2907         _session->audition_region (region);
2908 }
2909
2910 void
2911 Editor::region_from_selection ()
2912 {
2913         if (clicked_axisview == 0) {
2914                 return;
2915         }
2916
2917         if (selection->time.empty()) {
2918                 return;
2919         }
2920
2921         framepos_t start = selection->time[clicked_selection].start;
2922         framepos_t end = selection->time[clicked_selection].end;
2923
2924         TrackViewList tracks = get_tracks_for_range_action ();
2925
2926         framepos_t selection_cnt = end - start + 1;
2927
2928         for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2929                 boost::shared_ptr<Region> current;
2930                 boost::shared_ptr<Playlist> pl;
2931                 framepos_t internal_start;
2932                 string new_name;
2933
2934                 if ((pl = (*i)->playlist()) == 0) {
2935                         continue;
2936                 }
2937
2938                 if ((current = pl->top_region_at (start)) == 0) {
2939                         continue;
2940                 }
2941
2942                 internal_start = start - current->position();
2943                 RegionFactory::region_name (new_name, current->name(), true);
2944
2945                 PropertyList plist;
2946
2947                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2948                 plist.add (ARDOUR::Properties::length, selection_cnt);
2949                 plist.add (ARDOUR::Properties::name, new_name);
2950                 plist.add (ARDOUR::Properties::layer, 0);
2951
2952                 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2953         }
2954 }
2955
2956 void
2957 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2958 {
2959         if (selection->time.empty() || selection->tracks.empty()) {
2960                 return;
2961         }
2962
2963         framepos_t start, end;
2964         if (clicked_selection) {
2965                 start = selection->time[clicked_selection].start;
2966                 end = selection->time[clicked_selection].end;
2967         } else {
2968                 start = selection->time.start();
2969                 end = selection->time.end_frame();
2970         }
2971
2972         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2973         sort_track_selection (ts);
2974
2975         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2976                 boost::shared_ptr<Region> current;
2977                 boost::shared_ptr<Playlist> playlist;
2978                 framepos_t internal_start;
2979                 string new_name;
2980
2981                 if ((playlist = (*i)->playlist()) == 0) {
2982                         continue;
2983                 }
2984
2985                 if ((current = playlist->top_region_at(start)) == 0) {
2986                         continue;
2987                 }
2988
2989                 internal_start = start - current->position();
2990                 RegionFactory::region_name (new_name, current->name(), true);
2991
2992                 PropertyList plist;
2993
2994                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2995                 plist.add (ARDOUR::Properties::length, end - start + 1);
2996                 plist.add (ARDOUR::Properties::name, new_name);
2997
2998                 new_regions.push_back (RegionFactory::create (current, plist));
2999         }
3000 }
3001
3002 void
3003 Editor::split_multichannel_region ()
3004 {
3005         RegionSelection rs = get_regions_from_selection_and_entered ();
3006
3007         if (rs.empty()) {
3008                 return;
3009         }
3010
3011         vector< boost::shared_ptr<Region> > v;
3012
3013         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3014                 (*x)->region()->separate_by_channel (*_session, v);
3015         }
3016 }
3017
3018 void
3019 Editor::new_region_from_selection ()
3020 {
3021         region_from_selection ();
3022         cancel_selection ();
3023 }
3024
3025 static void
3026 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3027 {
3028         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3029         // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3030         case Evoral::OverlapNone:
3031                 break;
3032         default:
3033                 rs->push_back (rv);
3034         }
3035 }
3036
3037 /** Return either:
3038  *    - selected tracks, or if there are none...
3039  *    - tracks containing selected regions, or if there are none...
3040  *    - all tracks
3041  * @return tracks.
3042  */
3043 TrackViewList
3044 Editor::get_tracks_for_range_action () const
3045 {
3046         TrackViewList t;
3047
3048         if (selection->tracks.empty()) {
3049
3050                 /* use tracks with selected regions */
3051
3052                 RegionSelection rs = selection->regions;
3053
3054                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3055                         TimeAxisView* tv = &(*i)->get_time_axis_view();
3056
3057                         if (!t.contains (tv)) {
3058                                 t.push_back (tv);
3059                         }
3060                 }
3061
3062                 if (t.empty()) {
3063                         /* no regions and no tracks: use all tracks */
3064                         t = track_views;
3065                 }
3066
3067         } else {
3068
3069                 t = selection->tracks;
3070         }
3071
3072         return t.filter_to_unique_playlists();
3073 }
3074
3075 void
3076 Editor::separate_regions_between (const TimeSelection& ts)
3077 {
3078         bool in_command = false;
3079         boost::shared_ptr<Playlist> playlist;
3080         RegionSelection new_selection;
3081
3082         TrackViewList tmptracks = get_tracks_for_range_action ();
3083         sort_track_selection (tmptracks);
3084
3085         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3086
3087                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3088
3089                 if (!rtv) {
3090                         continue;
3091                 }
3092
3093                 if (!rtv->is_track()) {
3094                         continue;
3095                 }
3096
3097                 /* no edits to destructive tracks */
3098
3099                 if (rtv->track()->destructive()) {
3100                         continue;
3101                 }
3102
3103                 if ((playlist = rtv->playlist()) != 0) {
3104
3105                         playlist->clear_changes ();
3106
3107                         /* XXX need to consider musical time selections here at some point */
3108
3109                         double speed = rtv->track()->speed();
3110
3111                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3112
3113                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3114                                         sigc::mem_fun(*this, &Editor::collect_new_region_view));
3115
3116                                 latest_regionviews.clear ();
3117
3118                                 playlist->partition ((framepos_t)((*t).start * speed),
3119                                                      (framepos_t)((*t).end * speed), false);
3120
3121                                 c.disconnect ();
3122
3123                                 if (!latest_regionviews.empty()) {
3124
3125                                         rtv->view()->foreach_regionview (sigc::bind (
3126                                                                                  sigc::ptr_fun (add_if_covered),
3127                                                                                  &(*t), &new_selection));
3128
3129                                         if (!in_command) {
3130                                                 begin_reversible_command (_("separate"));
3131                                                 in_command = true;
3132                                         }
3133
3134                                         /* pick up changes to existing regions */
3135
3136                                         vector<Command*> cmds;
3137                                         playlist->rdiff (cmds);
3138                                         _session->add_commands (cmds);
3139
3140                                         /* pick up changes to the playlist itself (adds/removes)
3141                                          */
3142
3143                                         _session->add_command(new StatefulDiffCommand (playlist));
3144                                 }
3145                         }
3146                 }
3147         }
3148
3149         if (in_command) {
3150 //              selection->set (new_selection);
3151
3152                 commit_reversible_command ();
3153         }
3154 }
3155
3156 struct PlaylistState {
3157     boost::shared_ptr<Playlist> playlist;
3158     XMLNode*  before;
3159 };
3160
3161 /** Take tracks from get_tracks_for_range_action and cut any regions
3162  *  on those tracks so that the tracks are empty over the time
3163  *  selection.
3164  */
3165 void
3166 Editor::separate_region_from_selection ()
3167 {
3168         /* preferentially use *all* ranges in the time selection if we're in range mode
3169            to allow discontiguous operation, since get_edit_op_range() currently
3170            returns a single range.
3171         */
3172
3173         if (!selection->time.empty()) {
3174
3175                 separate_regions_between (selection->time);
3176
3177         } else {
3178
3179                 framepos_t start;
3180                 framepos_t end;
3181
3182                 if (get_edit_op_range (start, end)) {
3183
3184                         AudioRange ar (start, end, 1);
3185                         TimeSelection ts;
3186                         ts.push_back (ar);
3187
3188                         separate_regions_between (ts);
3189                 }
3190         }
3191 }
3192
3193 void
3194 Editor::separate_region_from_punch ()
3195 {
3196         Location* loc  = _session->locations()->auto_punch_location();
3197         if (loc) {
3198                 separate_regions_using_location (*loc);
3199         }
3200 }
3201
3202 void
3203 Editor::separate_region_from_loop ()
3204 {
3205         Location* loc  = _session->locations()->auto_loop_location();
3206         if (loc) {
3207                 separate_regions_using_location (*loc);
3208         }
3209 }
3210
3211 void
3212 Editor::separate_regions_using_location (Location& loc)
3213 {
3214         if (loc.is_mark()) {
3215                 return;
3216         }
3217
3218         AudioRange ar (loc.start(), loc.end(), 1);
3219         TimeSelection ts;
3220
3221         ts.push_back (ar);
3222
3223         separate_regions_between (ts);
3224 }
3225
3226 /** Separate regions under the selected region */
3227 void
3228 Editor::separate_under_selected_regions ()
3229 {
3230         vector<PlaylistState> playlists;
3231
3232         RegionSelection rs;
3233
3234         rs = get_regions_from_selection_and_entered();
3235
3236         if (!_session || rs.empty()) {
3237                 return;
3238         }
3239
3240         begin_reversible_command (_("separate region under"));
3241
3242         list<boost::shared_ptr<Region> > regions_to_remove;
3243
3244         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3245                 // we can't just remove the region(s) in this loop because
3246                 // this removes them from the RegionSelection, and they thus
3247                 // disappear from underneath the iterator, and the ++i above
3248                 // SEGVs in a puzzling fashion.
3249
3250                 // so, first iterate over the regions to be removed from rs and
3251                 // add them to the regions_to_remove list, and then
3252                 // iterate over the list to actually remove them.
3253
3254                 regions_to_remove.push_back ((*i)->region());
3255         }
3256
3257         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3258
3259                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3260
3261                 if (!playlist) {
3262                         // is this check necessary?
3263                         continue;
3264                 }
3265
3266                 vector<PlaylistState>::iterator i;
3267
3268                 //only take state if this is a new playlist.
3269                 for (i = playlists.begin(); i != playlists.end(); ++i) {
3270                         if ((*i).playlist == playlist) {
3271                                 break;
3272                         }
3273                 }
3274
3275                 if (i == playlists.end()) {
3276
3277                         PlaylistState before;
3278                         before.playlist = playlist;
3279                         before.before = &playlist->get_state();
3280
3281                         playlist->freeze ();
3282                         playlists.push_back(before);
3283                 }
3284
3285                 //Partition on the region bounds
3286                 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3287
3288                 //Re-add region that was just removed due to the partition operation
3289                 playlist->add_region( (*rl), (*rl)->first_frame() );
3290         }
3291
3292         vector<PlaylistState>::iterator pl;
3293
3294         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3295                 (*pl).playlist->thaw ();
3296                 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3297         }
3298
3299         commit_reversible_command ();
3300 }
3301
3302 void
3303 Editor::crop_region_to_selection ()
3304 {
3305         if (!selection->time.empty()) {
3306
3307                 crop_region_to (selection->time.start(), selection->time.end_frame());
3308
3309         } else {
3310
3311                 framepos_t start;
3312                 framepos_t end;
3313
3314                 if (get_edit_op_range (start, end)) {
3315                         crop_region_to (start, end);
3316                 }
3317         }
3318
3319 }
3320
3321 void
3322 Editor::crop_region_to (framepos_t start, framepos_t end)
3323 {
3324         vector<boost::shared_ptr<Playlist> > playlists;
3325         boost::shared_ptr<Playlist> playlist;
3326         TrackViewList ts;
3327
3328         if (selection->tracks.empty()) {
3329                 ts = track_views.filter_to_unique_playlists();
3330         } else {
3331                 ts = selection->tracks.filter_to_unique_playlists ();
3332         }
3333
3334         sort_track_selection (ts);
3335
3336         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3337
3338                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3339
3340                 if (!rtv) {
3341                         continue;
3342                 }
3343
3344                 boost::shared_ptr<Track> t = rtv->track();
3345
3346                 if (t != 0 && ! t->destructive()) {
3347
3348                         if ((playlist = rtv->playlist()) != 0) {
3349                                 playlists.push_back (playlist);
3350                         }
3351                 }
3352         }
3353
3354         if (playlists.empty()) {
3355                 return;
3356         }
3357
3358         framepos_t pos;
3359         framepos_t new_start;
3360         framepos_t new_end;
3361         framecnt_t new_length;
3362         bool in_command = false;
3363
3364         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3365
3366                 /* Only the top regions at start and end have to be cropped */
3367                 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3368                 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3369
3370                 vector<boost::shared_ptr<Region> > regions;
3371
3372                 if (region_at_start != 0) {
3373                         regions.push_back (region_at_start);
3374                 }
3375                 if (region_at_end != 0) {
3376                         regions.push_back (region_at_end);
3377                 }
3378
3379                 /* now adjust lengths */
3380                 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3381
3382                         pos = (*i)->position();
3383                         new_start = max (start, pos);
3384                         if (max_framepos - pos > (*i)->length()) {
3385                                 new_end = pos + (*i)->length() - 1;
3386                         } else {
3387                                 new_end = max_framepos;
3388                         }
3389                         new_end = min (end, new_end);
3390                         new_length = new_end - new_start + 1;
3391
3392                         if(!in_command) {
3393                                 begin_reversible_command (_("trim to selection"));
3394                                 in_command = true;
3395                         }
3396                         (*i)->clear_changes ();
3397                         (*i)->trim_to (new_start, new_length);
3398                         _session->add_command (new StatefulDiffCommand (*i));
3399                 }
3400         }
3401
3402         if (in_command) {
3403                 commit_reversible_command ();
3404         }
3405 }
3406
3407 void
3408 Editor::region_fill_track ()
3409 {
3410         boost::shared_ptr<Playlist> playlist;
3411         RegionSelection regions = get_regions_from_selection_and_entered ();
3412         RegionSelection foo;
3413
3414         framepos_t const end = _session->current_end_frame ();
3415
3416         if (regions.empty () || regions.end_frame () + 1 >= end) {
3417                 return;
3418         }
3419
3420         framepos_t const start_frame = regions.start ();
3421         framepos_t const end_frame = regions.end_frame ();
3422         framecnt_t const gap = end_frame - start_frame + 1;
3423
3424         begin_reversible_command (Operations::region_fill);
3425
3426         selection->clear_regions ();
3427
3428         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3429
3430                 boost::shared_ptr<Region> r ((*i)->region());
3431
3432                 TimeAxisView& tv = (*i)->get_time_axis_view();
3433                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3434                 latest_regionviews.clear ();
3435                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3436
3437                 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3438                 playlist = (*i)->region()->playlist();
3439                 playlist->clear_changes ();
3440                 playlist->duplicate_until (r, position, gap, end);
3441                 _session->add_command(new StatefulDiffCommand (playlist));
3442
3443                 c.disconnect ();
3444
3445                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3446         }
3447
3448         if (!foo.empty()) {
3449                 selection->set (foo);
3450         }
3451
3452         commit_reversible_command ();
3453 }
3454
3455 void
3456 Editor::set_region_sync_position ()
3457 {
3458         set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3459 }
3460
3461 void
3462 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3463 {
3464         bool in_command = false;
3465
3466         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3467
3468                 if (!(*r)->region()->covers (where)) {
3469                         continue;
3470                 }
3471
3472                 boost::shared_ptr<Region> region ((*r)->region());
3473
3474                 if (!in_command) {
3475                         begin_reversible_command (_("set sync point"));
3476                         in_command = true;
3477                 }
3478
3479                 region->clear_changes ();
3480                 region->set_sync_position (where);
3481                 _session->add_command(new StatefulDiffCommand (region));
3482         }
3483
3484         if (in_command) {
3485                 commit_reversible_command ();
3486         }
3487 }
3488
3489 /** Remove the sync positions of the selection */
3490 void
3491 Editor::remove_region_sync ()
3492 {
3493         RegionSelection rs = get_regions_from_selection_and_entered ();
3494
3495         if (rs.empty()) {
3496                 return;
3497         }
3498
3499         begin_reversible_command (_("remove region sync"));
3500
3501         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3502
3503                 (*i)->region()->clear_changes ();
3504                 (*i)->region()->clear_sync_position ();
3505                 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3506         }
3507
3508         commit_reversible_command ();
3509 }
3510
3511 void
3512 Editor::naturalize_region ()
3513 {
3514         RegionSelection rs = get_regions_from_selection_and_entered ();
3515
3516         if (rs.empty()) {
3517                 return;
3518         }
3519
3520         if (rs.size() > 1) {
3521                 begin_reversible_command (_("move regions to original position"));
3522         } else {
3523                 begin_reversible_command (_("move region to original position"));
3524         }
3525
3526         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3527                 (*i)->region()->clear_changes ();
3528                 (*i)->region()->move_to_natural_position ();
3529                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3530         }
3531
3532         commit_reversible_command ();
3533 }
3534
3535 void
3536 Editor::align_regions (RegionPoint what)
3537 {
3538         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3539
3540         if (rs.empty()) {
3541                 return;
3542         }
3543
3544         begin_reversible_command (_("align selection"));
3545
3546         framepos_t const position = get_preferred_edit_position ();
3547
3548         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3549                 align_region_internal ((*i)->region(), what, position);
3550         }
3551
3552         commit_reversible_command ();
3553 }
3554
3555 struct RegionSortByTime {
3556     bool operator() (const RegionView* a, const RegionView* b) {
3557             return a->region()->position() < b->region()->position();
3558     }
3559 };
3560
3561 void
3562 Editor::align_regions_relative (RegionPoint point)
3563 {
3564         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3565
3566         if (rs.empty()) {
3567                 return;
3568         }
3569
3570         framepos_t const position = get_preferred_edit_position ();
3571
3572         framepos_t distance = 0;
3573         framepos_t pos = 0;
3574         int dir = 1;
3575
3576         list<RegionView*> sorted;
3577         rs.by_position (sorted);
3578
3579         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3580
3581         switch (point) {
3582         case Start:
3583                 pos = position;
3584                 if (position > r->position()) {
3585                         distance = position - r->position();
3586                 } else {
3587                         distance = r->position() - position;
3588                         dir = -1;
3589                 }
3590                 break;
3591
3592         case End:
3593                 if (position > r->last_frame()) {
3594                         distance = position - r->last_frame();
3595                         pos = r->position() + distance;
3596                 } else {
3597                         distance = r->last_frame() - position;
3598                         pos = r->position() - distance;
3599                         dir = -1;
3600                 }
3601                 break;
3602
3603         case SyncPoint:
3604                 pos = r->adjust_to_sync (position);
3605                 if (pos > r->position()) {
3606                         distance = pos - r->position();
3607                 } else {
3608                         distance = r->position() - pos;
3609                         dir = -1;
3610                 }
3611                 break;
3612         }
3613
3614         if (pos == r->position()) {
3615                 return;
3616         }
3617
3618         begin_reversible_command (_("align selection (relative)"));
3619
3620         /* move first one specially */
3621
3622         r->clear_changes ();
3623         r->set_position (pos);
3624         _session->add_command(new StatefulDiffCommand (r));
3625
3626         /* move rest by the same amount */
3627
3628         sorted.pop_front();
3629
3630         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3631
3632                 boost::shared_ptr<Region> region ((*i)->region());
3633
3634                 region->clear_changes ();
3635
3636                 if (dir > 0) {
3637                         region->set_position (region->position() + distance);
3638                 } else {
3639                         region->set_position (region->position() - distance);
3640                 }
3641
3642                 _session->add_command(new StatefulDiffCommand (region));
3643
3644         }
3645
3646         commit_reversible_command ();
3647 }
3648
3649 void
3650 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3651 {
3652         begin_reversible_command (_("align region"));
3653         align_region_internal (region, point, position);
3654         commit_reversible_command ();
3655 }
3656
3657 void
3658 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3659 {
3660         region->clear_changes ();
3661
3662         switch (point) {
3663         case SyncPoint:
3664                 region->set_position (region->adjust_to_sync (position));
3665                 break;
3666
3667         case End:
3668                 if (position > region->length()) {
3669                         region->set_position (position - region->length());
3670                 }
3671                 break;
3672
3673         case Start:
3674                 region->set_position (position);
3675                 break;
3676         }
3677
3678         _session->add_command(new StatefulDiffCommand (region));
3679 }
3680
3681 void
3682 Editor::trim_region_front ()
3683 {
3684         trim_region (true);
3685 }
3686
3687 void
3688 Editor::trim_region_back ()
3689 {
3690         trim_region (false);
3691 }
3692
3693 void
3694 Editor::trim_region (bool front)
3695 {
3696         framepos_t where = get_preferred_edit_position();
3697         RegionSelection rs = get_regions_from_selection_and_edit_point ();
3698
3699         if (rs.empty()) {
3700                 return;
3701         }
3702
3703         begin_reversible_command (front ? _("trim front") : _("trim back"));
3704
3705         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3706                 if (!(*i)->region()->locked()) {
3707
3708                         (*i)->region()->clear_changes ();
3709
3710                         if (front) {
3711                                 (*i)->region()->trim_front (where);
3712                         } else {
3713                                 (*i)->region()->trim_end (where);
3714                         }
3715
3716                         _session->add_command (new StatefulDiffCommand ((*i)->region()));
3717                 }
3718         }
3719
3720         commit_reversible_command ();
3721 }
3722
3723 /** Trim the end of the selected regions to the position of the edit cursor */
3724 void
3725 Editor::trim_region_to_loop ()
3726 {
3727         Location* loc = _session->locations()->auto_loop_location();
3728         if (!loc) {
3729                 return;
3730         }
3731         trim_region_to_location (*loc, _("trim to loop"));
3732 }
3733
3734 void
3735 Editor::trim_region_to_punch ()
3736 {
3737         Location* loc = _session->locations()->auto_punch_location();
3738         if (!loc) {
3739                 return;
3740         }
3741         trim_region_to_location (*loc, _("trim to punch"));
3742 }
3743
3744 void
3745 Editor::trim_region_to_location (const Location& loc, const char* str)
3746 {
3747         RegionSelection rs = get_regions_from_selection_and_entered ();
3748         bool in_command = false;
3749
3750         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3751                 RegionView* rv = (*x);
3752
3753                 /* require region to span proposed trim */
3754                 switch (rv->region()->coverage (loc.start(), loc.end())) {
3755                 case Evoral::OverlapInternal:
3756                         break;
3757                 default:
3758                         continue;
3759                 }
3760
3761                 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3762                 if (!tav) {
3763                         return;
3764                 }
3765
3766                 float speed = 1.0;
3767                 framepos_t start;
3768                 framepos_t end;
3769
3770                 if (tav->track() != 0) {
3771                         speed = tav->track()->speed();
3772                 }
3773
3774                 start = session_frame_to_track_frame (loc.start(), speed);
3775                 end = session_frame_to_track_frame (loc.end(), speed);
3776
3777                 rv->region()->clear_changes ();
3778                 rv->region()->trim_to (start, (end - start));
3779
3780                 if (!in_command) {
3781                         begin_reversible_command (str);
3782                         in_command = true;
3783                 }
3784                 _session->add_command(new StatefulDiffCommand (rv->region()));
3785         }
3786
3787         if (in_command) {
3788                 commit_reversible_command ();
3789         }
3790 }
3791
3792 void
3793 Editor::trim_region_to_previous_region_end ()
3794 {
3795         return trim_to_region(false);
3796 }
3797
3798 void
3799 Editor::trim_region_to_next_region_start ()
3800 {
3801         return trim_to_region(true);
3802 }
3803
3804 void
3805 Editor::trim_to_region(bool forward)
3806 {
3807         RegionSelection rs = get_regions_from_selection_and_entered ();
3808         bool in_command = false;
3809
3810         boost::shared_ptr<Region> next_region;
3811
3812         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3813
3814                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3815
3816                 if (!arv) {
3817                         continue;
3818                 }
3819
3820                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3821
3822                 if (!atav) {
3823                         continue;
3824                 }
3825
3826                 float speed = 1.0;
3827
3828                 if (atav->track() != 0) {
3829                         speed = atav->track()->speed();
3830                 }
3831
3832
3833                 boost::shared_ptr<Region> region = arv->region();
3834                 boost::shared_ptr<Playlist> playlist (region->playlist());
3835
3836                 region->clear_changes ();
3837
3838                 if (forward) {
3839
3840                     next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3841
3842                     if (!next_region) {
3843                         continue;
3844                     }
3845
3846                     region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3847                     arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3848                 }
3849                 else {
3850
3851                     next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3852
3853                     if(!next_region){
3854                         continue;
3855                     }
3856
3857                     region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3858
3859                     arv->region_changed (ARDOUR::bounds_change);
3860                 }
3861
3862                 if (!in_command) {
3863                         begin_reversible_command (_("trim to region"));
3864                         in_command = true;
3865                 }
3866                 _session->add_command(new StatefulDiffCommand (region));
3867         }
3868
3869         if (in_command) {
3870                 commit_reversible_command ();
3871         }
3872 }
3873
3874 void
3875 Editor::unfreeze_route ()
3876 {
3877         if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3878                 return;
3879         }
3880
3881         clicked_routeview->track()->unfreeze ();
3882 }
3883
3884 void*
3885 Editor::_freeze_thread (void* arg)
3886 {
3887         return static_cast<Editor*>(arg)->freeze_thread ();
3888 }
3889
3890 void*
3891 Editor::freeze_thread ()
3892 {
3893         /* create event pool because we may need to talk to the session */
3894         SessionEvent::create_per_thread_pool ("freeze events", 64);
3895         /* create per-thread buffers for process() tree to use */
3896         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3897         current_interthread_info->done = true;
3898         return 0;
3899 }
3900
3901 void
3902 Editor::freeze_route ()
3903 {
3904         if (!_session) {
3905                 return;
3906         }
3907
3908         /* stop transport before we start. this is important */
3909
3910         _session->request_transport_speed (0.0);
3911
3912         /* wait for just a little while, because the above call is asynchronous */
3913
3914         Glib::usleep (250000);
3915
3916         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3917                 return;
3918         }
3919
3920         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3921                 MessageDialog d (
3922                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3923                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3924                         );
3925                 d.set_title (_("Cannot freeze"));
3926                 d.run ();
3927                 return;
3928         }
3929
3930         if (clicked_routeview->track()->has_external_redirects()) {
3931                 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"
3932                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3933                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3934
3935                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3936                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3937                 d.set_title (_("Freeze Limits"));
3938
3939                 int response = d.run ();
3940
3941                 switch (response) {
3942                 case Gtk::RESPONSE_CANCEL:
3943                         return;
3944                 default:
3945                         break;
3946                 }
3947         }
3948
3949         InterThreadInfo itt;
3950         current_interthread_info = &itt;
3951
3952         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3953
3954         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3955
3956         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3957
3958         while (!itt.done && !itt.cancel) {
3959                 gtk_main_iteration ();
3960         }
3961
3962         pthread_join (itt.thread, 0);
3963         current_interthread_info = 0;
3964 }
3965
3966 void
3967 Editor::bounce_range_selection (bool replace, bool enable_processing)
3968 {
3969         if (selection->time.empty()) {
3970                 return;
3971         }
3972
3973         TrackSelection views = selection->tracks;
3974
3975         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3976
3977                 if (enable_processing) {
3978
3979                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3980
3981                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3982                                 MessageDialog d (
3983                                         _("You can't perform this operation because the processing of the signal "
3984                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3985                                           "You can do this without processing, which is a different operation.")
3986                                         );
3987                                 d.set_title (_("Cannot bounce"));
3988                                 d.run ();
3989                                 return;
3990                         }
3991                 }
3992         }
3993
3994         framepos_t start = selection->time[clicked_selection].start;
3995         framepos_t end = selection->time[clicked_selection].end;
3996         framepos_t cnt = end - start + 1;
3997         bool in_command = false;
3998
3999         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4000
4001                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4002
4003                 if (!rtv) {
4004                         continue;
4005                 }
4006
4007                 boost::shared_ptr<Playlist> playlist;
4008
4009                 if ((playlist = rtv->playlist()) == 0) {
4010                         continue;
4011                 }
4012
4013                 InterThreadInfo itt;
4014
4015                 playlist->clear_changes ();
4016                 playlist->clear_owned_changes ();
4017
4018                 boost::shared_ptr<Region> r;
4019
4020                 if (enable_processing) {
4021                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4022                 } else {
4023                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4024                 }
4025
4026                 if (!r) {
4027                         continue;
4028                 }
4029
4030                 if (replace) {
4031                         list<AudioRange> ranges;
4032                         ranges.push_back (AudioRange (start, start+cnt, 0));
4033                         playlist->cut (ranges); // discard result
4034                         playlist->add_region (r, start);
4035                 }
4036
4037                 if (!in_command) {
4038                         begin_reversible_command (_("bounce range"));
4039                         in_command = true;
4040                 }
4041                 vector<Command*> cmds;
4042                 playlist->rdiff (cmds);
4043                 _session->add_commands (cmds);
4044
4045                 _session->add_command (new StatefulDiffCommand (playlist));
4046         }
4047
4048         if (in_command) {
4049                 commit_reversible_command ();
4050         }
4051 }
4052
4053 /** Delete selected regions, automation points or a time range */
4054 void
4055 Editor::delete_ ()
4056 {
4057         //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4058         //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4059         bool deleted = false;
4060         if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4061                 deleted = current_mixer_strip->delete_processors ();
4062
4063         if (!deleted)
4064                 cut_copy (Delete);
4065 }
4066
4067 /** Cut selected regions, automation points or a time range */
4068 void
4069 Editor::cut ()
4070 {
4071         cut_copy (Cut);
4072 }
4073
4074 /** Copy selected regions, automation points or a time range */
4075 void
4076 Editor::copy ()
4077 {
4078         cut_copy (Copy);
4079 }
4080
4081
4082 /** @return true if a Cut, Copy or Clear is possible */
4083 bool
4084 Editor::can_cut_copy () const
4085 {
4086         if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4087                 return true;
4088
4089         return false;
4090 }
4091
4092
4093 /** Cut, copy or clear selected regions, automation points or a time range.
4094  * @param op Operation (Delete, Cut, Copy or Clear)
4095  */
4096 void
4097 Editor::cut_copy (CutCopyOp op)
4098 {
4099         /* only cancel selection if cut/copy is successful.*/
4100
4101         string opname;
4102
4103         switch (op) {
4104         case Delete:
4105                 opname = _("delete");
4106                 break;
4107         case Cut:
4108                 opname = _("cut");
4109                 break;
4110         case Copy:
4111                 opname = _("copy");
4112                 break;
4113         case Clear:
4114                 opname = _("clear");
4115                 break;
4116         }
4117
4118         /* if we're deleting something, and the mouse is still pressed,
4119            the thing we started a drag for will be gone when we release
4120            the mouse button(s). avoid this. see part 2 at the end of
4121            this function.
4122         */
4123
4124         if (op == Delete || op == Cut || op == Clear) {
4125                 if (_drags->active ()) {
4126                         _drags->abort ();
4127                 }
4128         }
4129
4130         if ( op != Delete )  //"Delete" doesn't change copy/paste buf
4131                 cut_buffer->clear ();
4132
4133         if (entered_marker) {
4134
4135                 /* cut/delete op while pointing at a marker */
4136
4137                 bool ignored;
4138                 Location* loc = find_location_from_marker (entered_marker, ignored);
4139
4140                 if (_session && loc) {
4141                         entered_marker = NULL;
4142                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4143                 }
4144
4145                 _drags->abort ();
4146                 return;
4147         }
4148
4149         switch (mouse_mode) {
4150         case MouseDraw:
4151         case MouseContent:
4152                 begin_reversible_command (opname + ' ' + X_("MIDI"));
4153                 cut_copy_midi (op);
4154                 commit_reversible_command ();
4155                 return;
4156         default:
4157                 break;
4158         }
4159
4160         bool did_edit = false;
4161
4162         if (!selection->regions.empty() || !selection->points.empty()) {
4163                 begin_reversible_command (opname + ' ' + _("objects"));
4164                 did_edit = true;
4165
4166                 if (!selection->regions.empty()) {
4167                         cut_copy_regions (op, selection->regions);
4168
4169                         if (op == Cut || op == Delete) {
4170                                 selection->clear_regions ();
4171                         }
4172                 }
4173
4174                 if (!selection->points.empty()) {
4175                         cut_copy_points (op);
4176
4177                         if (op == Cut || op == Delete) {
4178                                 selection->clear_points ();
4179                         }
4180                 }
4181         } else if (selection->time.empty()) {
4182                 framepos_t start, end;
4183                 /* no time selection, see if we can get an edit range
4184                    and use that.
4185                 */
4186                 if (get_edit_op_range (start, end)) {
4187                         selection->set (start, end);
4188                 }
4189         } else if (!selection->time.empty()) {
4190                 begin_reversible_command (opname + ' ' + _("range"));
4191
4192                 did_edit = true;
4193                 cut_copy_ranges (op);
4194
4195                 if (op == Cut || op == Delete) {
4196                         selection->clear_time ();
4197                 }
4198         }
4199
4200         if (did_edit) {
4201                 /* reset repeated paste state */
4202                 paste_count    = 0;
4203                 last_paste_pos = 0;
4204                 commit_reversible_command ();
4205         }
4206
4207         if (op == Delete || op == Cut || op == Clear) {
4208                 _drags->abort ();
4209         }
4210 }
4211
4212
4213 struct AutomationRecord {
4214         AutomationRecord () : state (0) , line(NULL) {}
4215         AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4216
4217         XMLNode* state; ///< state before any operation
4218         const AutomationLine* line; ///< line this came from
4219         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4220 };
4221 struct PointsSelectionPositionSorter {
4222         bool operator() (ControlPoint* a, ControlPoint* b) {
4223                 return (*(a->model()))->when < (*(b->model()))->when;
4224         }
4225 };
4226 /** Cut, copy or clear selected automation points.
4227  *  @param op Operation (Cut, Copy or Clear)
4228  */
4229 void
4230 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4231 {
4232         if (selection->points.empty ()) {
4233                 return;
4234         }
4235
4236         /* XXX: not ideal, as there may be more than one track involved in the point selection */
4237         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4238
4239         /* Keep a record of the AutomationLists that we end up using in this operation */
4240         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4241         Lists lists;
4242
4243         /* user could select points in any order */
4244         selection->points.sort(PointsSelectionPositionSorter ());
4245
4246         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4247         for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4248                 const AutomationLine&                   line = (*sel_point)->line();
4249                 const boost::shared_ptr<AutomationList> al   = line.the_list();
4250                 if (lists.find (al) == lists.end ()) {
4251                         /* We haven't seen this list yet, so make a record for it.  This includes
4252                            taking a copy of its current state, in case this is needed for undo later.
4253                         */
4254                         lists[al] = AutomationRecord (&al->get_state (), &line);
4255                 }
4256         }
4257
4258         if (op == Cut || op == Copy) {
4259                 /* This operation will involve putting things in the cut buffer, so create an empty
4260                    ControlList for each of our source lists to put the cut buffer data in.
4261                 */
4262                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4263                         i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4264                 }
4265
4266                 /* Add all selected points to the relevant copy ControlLists */
4267                 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4268                 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4269                         boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
4270                         AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4271
4272                         lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4273                         if (midi) {
4274                                 /* Update earliest MIDI start time in beats */
4275                                 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4276                         } else {
4277                                 /* Update earliest session start time in frames */
4278                                 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4279                         }
4280                 }
4281
4282                 /* Snap start time backwards, so copy/paste is snap aligned. */
4283                 if (midi) {
4284                         if (earliest == Evoral::Beats::max()) {
4285                                 earliest = Evoral::Beats();  // Weird... don't offset
4286                         }
4287                         earliest.round_down_to_beat();
4288                 } else {
4289                         if (start.frame == std::numeric_limits<double>::max()) {
4290                                 start.frame = 0;  // Weird... don't offset
4291                         }
4292                         snap_to(start, RoundDownMaybe);
4293                 }
4294
4295                 const double line_offset = midi ? earliest.to_double() : start.frame;
4296                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4297                         /* Correct this copy list so that it is relative to the earliest
4298                            start time, so relative ordering between points is preserved
4299                            when copying from several lists and the paste starts at the
4300                            earliest copied piece of data. */
4301                         boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4302                         for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4303                                 (*ctrl_evt)->when -= line_offset;
4304                         }
4305
4306                         /* And add it to the cut buffer */
4307                         cut_buffer->add (al_cpy);
4308                 }
4309         }
4310
4311         if (op == Delete || op == Cut) {
4312                 /* This operation needs to remove things from the main AutomationList, so do that now */
4313
4314                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4315                         i->first->freeze ();
4316                 }
4317
4318                 /* Remove each selected point from its AutomationList */
4319                 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4320                         AutomationLine& line = (*sel_point)->line ();
4321                         boost::shared_ptr<AutomationList> al = line.the_list();
4322
4323                         bool erase = true;
4324
4325                         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4326                                 /* removing of first and last gain point in region gain lines is prohibited*/
4327                                 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4328                                         erase = false;
4329                                 }
4330                         }
4331
4332                         if(erase) {
4333                                 al->erase ((*sel_point)->model ());
4334                         }
4335                 }
4336
4337                 /* Thaw the lists and add undo records for them */
4338                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4339                         boost::shared_ptr<AutomationList> al = i->first;
4340                         al->thaw ();
4341                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4342                 }
4343         }
4344 }
4345
4346 /** Cut, copy or clear selected automation points.
4347  * @param op Operation (Cut, Copy or Clear)
4348  */
4349 void
4350 Editor::cut_copy_midi (CutCopyOp op)
4351 {
4352         Evoral::Beats earliest = Evoral::Beats::max();
4353         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4354                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4355                 if (mrv) {
4356                         if (!mrv->selection().empty()) {
4357                                 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4358                         }
4359                         mrv->cut_copy_clear (op);
4360
4361                         /* XXX: not ideal, as there may be more than one track involved in the selection */
4362                         _last_cut_copy_source_track = &mrv->get_time_axis_view();
4363                 }
4364         }
4365
4366         if (!selection->points.empty()) {
4367                 cut_copy_points (op, earliest, true);
4368                 if (op == Cut || op == Delete) {
4369                         selection->clear_points ();
4370                 }
4371         }
4372 }
4373
4374 struct lt_playlist {
4375     bool operator () (const PlaylistState& a, const PlaylistState& b) {
4376             return a.playlist < b.playlist;
4377     }
4378 };
4379
4380 struct PlaylistMapping {
4381     TimeAxisView* tv;
4382     boost::shared_ptr<Playlist> pl;
4383
4384     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4385 };
4386
4387 /** Remove `clicked_regionview' */
4388 void
4389 Editor::remove_clicked_region ()
4390 {
4391         if (clicked_routeview == 0 || clicked_regionview == 0) {
4392                 return;
4393         }
4394
4395         begin_reversible_command (_("remove region"));
4396
4397         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4398
4399         playlist->clear_changes ();
4400         playlist->clear_owned_changes ();
4401         playlist->remove_region (clicked_regionview->region());
4402         if (Config->get_edit_mode() == Ripple)
4403                 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4404
4405         /* We might have removed regions, which alters other regions' layering_index,
4406            so we need to do a recursive diff here.
4407         */
4408         vector<Command*> cmds;
4409         playlist->rdiff (cmds);
4410         _session->add_commands (cmds);
4411
4412         _session->add_command(new StatefulDiffCommand (playlist));
4413         commit_reversible_command ();
4414 }
4415
4416
4417 /** Remove the selected regions */
4418 void
4419 Editor::remove_selected_regions ()
4420 {
4421         RegionSelection rs = get_regions_from_selection_and_entered ();
4422
4423         if (!_session || rs.empty()) {
4424                 return;
4425         }
4426
4427         list<boost::shared_ptr<Region> > regions_to_remove;
4428
4429         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4430                 // we can't just remove the region(s) in this loop because
4431                 // this removes them from the RegionSelection, and they thus
4432                 // disappear from underneath the iterator, and the ++i above
4433                 // SEGVs in a puzzling fashion.
4434
4435                 // so, first iterate over the regions to be removed from rs and
4436                 // add them to the regions_to_remove list, and then
4437                 // iterate over the list to actually remove them.
4438
4439                 regions_to_remove.push_back ((*i)->region());
4440         }
4441
4442         vector<boost::shared_ptr<Playlist> > playlists;
4443
4444         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4445
4446                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4447
4448                 if (!playlist) {
4449                         // is this check necessary?
4450                         continue;
4451                 }
4452
4453                 /* get_regions_from_selection_and_entered() guarantees that
4454                    the playlists involved are unique, so there is no need
4455                    to check here.
4456                 */
4457
4458                 playlists.push_back (playlist);
4459
4460                 playlist->clear_changes ();
4461                 playlist->clear_owned_changes ();
4462                 playlist->freeze ();
4463                 playlist->remove_region (*rl);
4464                 if (Config->get_edit_mode() == Ripple)
4465                         playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4466
4467         }
4468
4469         vector<boost::shared_ptr<Playlist> >::iterator pl;
4470         bool in_command = false;
4471
4472         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4473                 (*pl)->thaw ();
4474
4475                 /* We might have removed regions, which alters other regions' layering_index,
4476                    so we need to do a recursive diff here.
4477                 */
4478
4479                 if (!in_command) {
4480                         begin_reversible_command (_("remove region"));
4481                         in_command = true;
4482                 }
4483                 vector<Command*> cmds;
4484                 (*pl)->rdiff (cmds);
4485                 _session->add_commands (cmds);
4486
4487                 _session->add_command(new StatefulDiffCommand (*pl));
4488         }
4489
4490         if (in_command) {
4491                 commit_reversible_command ();
4492         }
4493 }
4494
4495 /** Cut, copy or clear selected regions.
4496  * @param op Operation (Cut, Copy or Clear)
4497  */
4498 void
4499 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4500 {
4501         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4502            a map when we want ordered access to both elements. i think.
4503         */
4504
4505         vector<PlaylistMapping> pmap;
4506
4507         framepos_t first_position = max_framepos;
4508
4509         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4510         FreezeList freezelist;
4511
4512         /* get ordering correct before we cut/copy */
4513
4514         rs.sort_by_position_and_track ();
4515
4516         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4517
4518                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4519
4520                 if (op == Cut || op == Clear || op == Delete) {
4521                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4522
4523                         if (pl) {
4524                                 FreezeList::iterator fl;
4525
4526                                 // only take state if this is a new playlist.
4527                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4528                                         if ((*fl) == pl) {
4529                                                 break;
4530                                         }
4531                                 }
4532
4533                                 if (fl == freezelist.end()) {
4534                                         pl->clear_changes();
4535                                         pl->clear_owned_changes ();
4536                                         pl->freeze ();
4537                                         freezelist.insert (pl);
4538                                 }
4539                         }
4540                 }
4541
4542                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4543                 vector<PlaylistMapping>::iterator z;
4544
4545                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4546                         if ((*z).tv == tv) {
4547                                 break;
4548                         }
4549                 }
4550
4551                 if (z == pmap.end()) {
4552                         pmap.push_back (PlaylistMapping (tv));
4553                 }
4554         }
4555
4556         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4557
4558                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4559
4560                 if (!pl) {
4561                         /* region not yet associated with a playlist (e.g. unfinished
4562                            capture pass.
4563                         */
4564                         ++x;
4565                         continue;
4566                 }
4567
4568                 TimeAxisView& tv = (*x)->get_time_axis_view();
4569                 boost::shared_ptr<Playlist> npl;
4570                 RegionSelection::iterator tmp;
4571
4572                 tmp = x;
4573                 ++tmp;
4574
4575                 if (op != Delete) {
4576
4577                         vector<PlaylistMapping>::iterator z;
4578
4579                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4580                                 if ((*z).tv == &tv) {
4581                                         break;
4582                                 }
4583                         }
4584
4585                         assert (z != pmap.end());
4586
4587                         if (!(*z).pl) {
4588                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4589                                 npl->freeze();
4590                                 (*z).pl = npl;
4591                         } else {
4592                                 npl = (*z).pl;
4593                         }
4594                 }
4595
4596                 boost::shared_ptr<Region> r = (*x)->region();
4597                 boost::shared_ptr<Region> _xx;
4598
4599                 assert (r != 0);
4600
4601                 switch (op) {
4602                 case Delete:
4603                         pl->remove_region (r);
4604                         if (Config->get_edit_mode() == Ripple)
4605                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4606                         break;
4607
4608                 case Cut:
4609                         _xx = RegionFactory::create (r);
4610                         npl->add_region (_xx, r->position() - first_position);
4611                         pl->remove_region (r);
4612                         if (Config->get_edit_mode() == Ripple)
4613                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4614                         break;
4615
4616                 case Copy:
4617                         /* copy region before adding, so we're not putting same object into two different playlists */
4618                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4619                         break;
4620
4621                 case Clear:
4622                         pl->remove_region (r);
4623                         if (Config->get_edit_mode() == Ripple)
4624                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4625                         break;
4626                 }
4627
4628                 x = tmp;
4629         }
4630
4631         if (op != Delete) {
4632
4633                 list<boost::shared_ptr<Playlist> > foo;
4634
4635                 /* the pmap is in the same order as the tracks in which selected regions occurred */
4636
4637                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4638                         if ((*i).pl) {
4639                                 (*i).pl->thaw();
4640                                 foo.push_back ((*i).pl);
4641                         }
4642                 }
4643
4644                 if (!foo.empty()) {
4645                         cut_buffer->set (foo);
4646                 }
4647
4648                 if (pmap.empty()) {
4649                         _last_cut_copy_source_track = 0;
4650                 } else {
4651                         _last_cut_copy_source_track = pmap.front().tv;
4652                 }
4653         }
4654
4655         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4656                 (*pl)->thaw ();
4657
4658                 /* We might have removed regions, which alters other regions' layering_index,
4659                    so we need to do a recursive diff here.
4660                 */
4661                 vector<Command*> cmds;
4662                 (*pl)->rdiff (cmds);
4663                 _session->add_commands (cmds);
4664
4665                 _session->add_command (new StatefulDiffCommand (*pl));
4666         }
4667 }
4668
4669 void
4670 Editor::cut_copy_ranges (CutCopyOp op)
4671 {
4672         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4673
4674         /* Sort the track selection now, so that it if is used, the playlists
4675            selected by the calls below to cut_copy_clear are in the order that
4676            their tracks appear in the editor.  This makes things like paste
4677            of ranges work properly.
4678         */
4679
4680         sort_track_selection (ts);
4681
4682         if (ts.empty()) {
4683                 if (!entered_track) {
4684                         return;
4685                 }
4686                 ts.push_back (entered_track);
4687         }
4688
4689         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4690                 (*i)->cut_copy_clear (*selection, op);
4691         }
4692 }
4693
4694 void
4695 Editor::paste (float times, bool from_context)
4696 {
4697         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4698         MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4699         paste_internal (where.frame, times, 0);
4700 }
4701
4702 void
4703 Editor::mouse_paste ()
4704 {
4705         MusicFrame where (0, 0);
4706         bool ignored;
4707         if (!mouse_frame (where.frame, ignored)) {
4708                 return;
4709         }
4710
4711         snap_to (where);
4712         paste_internal (where.frame, 1, where.division);
4713 }
4714
4715 void
4716 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4717 {
4718         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4719
4720         if (cut_buffer->empty(internal_editing())) {
4721                 return;
4722         }
4723
4724         if (position == max_framepos) {
4725                 position = get_preferred_edit_position();
4726                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4727         }
4728
4729         if (position == last_paste_pos) {
4730                 /* repeated paste in the same position */
4731                 ++paste_count;
4732         } else {
4733                 /* paste in new location, reset repeated paste state */
4734                 paste_count = 0;
4735                 last_paste_pos = position;
4736         }
4737
4738         /* get everything in the correct order */
4739
4740         TrackViewList ts;
4741         if (!selection->tracks.empty()) {
4742                 /* If there is a track selection, paste into exactly those tracks and
4743                    only those tracks.  This allows the user to be explicit and override
4744                    the below "do the reasonable thing" logic. */
4745                 ts = selection->tracks.filter_to_unique_playlists ();
4746                 sort_track_selection (ts);
4747         } else {
4748                 /* Figure out which track to base the paste at. */
4749                 TimeAxisView* base_track = NULL;
4750                 if (_edit_point == Editing::EditAtMouse && entered_track) {
4751                         /* With the mouse edit point, paste onto the track under the mouse. */
4752                         base_track = entered_track;
4753                 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4754                         /* With the mouse edit point, paste onto the track of the region under the mouse. */
4755                         base_track = &entered_regionview->get_time_axis_view();
4756                 } else if (_last_cut_copy_source_track) {
4757                         /* Paste to the track that the cut/copy came from (see mantis #333). */
4758                         base_track = _last_cut_copy_source_track;
4759                 } else {
4760                         /* This is "impossible" since we've copied... well, do nothing. */
4761                         return;
4762                 }
4763
4764                 /* Walk up to parent if necessary, so base track is a route. */
4765                 while (base_track->get_parent()) {
4766                         base_track = base_track->get_parent();
4767                 }
4768
4769                 /* Add base track and all tracks below it.  The paste logic will select
4770                    the appropriate object types from the cut buffer in relative order. */
4771                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4772                         if ((*i)->order() >= base_track->order()) {
4773                                 ts.push_back(*i);
4774                         }
4775                 }
4776
4777                 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4778                 sort_track_selection (ts);
4779
4780                 /* Add automation children of each track in order, for pasting several lines. */
4781                 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4782                         /* Add any automation children for pasting several lines */
4783                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4784                         if (!rtv) {
4785                                 continue;
4786                         }
4787
4788                         typedef RouteTimeAxisView::AutomationTracks ATracks;
4789                         const ATracks& atracks = rtv->automation_tracks();
4790                         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4791                                 i = ts.insert(i, a->second.get());
4792                                 ++i;
4793                         }
4794                 }
4795
4796                 /* We now have a list of trackviews starting at base_track, including
4797                    automation children, in the order shown in the editor, e.g. R1,
4798                    R1.A1, R1.A2, R2, R2.A1, ... */
4799         }
4800
4801         begin_reversible_command (Operations::paste);
4802
4803         if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4804             dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4805             /* Only one line copied, and one automation track selected.  Do a
4806                "greedy" paste from one automation type to another. */
4807
4808             PasteContext ctx(paste_count, times, ItemCounts(), true);
4809             ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4810
4811         } else {
4812
4813                 /* Paste into tracks */
4814
4815                 PasteContext ctx(paste_count, times, ItemCounts(), false);
4816                 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4817                         (*i)->paste (position, *cut_buffer, ctx, sub_num);
4818                 }
4819         }
4820
4821         commit_reversible_command ();
4822 }
4823
4824 void
4825 Editor::duplicate_regions (float times)
4826 {
4827         RegionSelection rs (get_regions_from_selection_and_entered());
4828         duplicate_some_regions (rs, times);
4829 }
4830
4831 void
4832 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4833 {
4834         if (regions.empty ()) {
4835                 return;
4836         }
4837
4838         boost::shared_ptr<Playlist> playlist;
4839         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4840         RegionSelection foo;
4841
4842         framepos_t const start_frame = regions.start ();
4843         framepos_t const end_frame = regions.end_frame ();
4844         framecnt_t const gap = end_frame - start_frame + 1;
4845
4846         begin_reversible_command (Operations::duplicate_region);
4847
4848         selection->clear_regions ();
4849
4850         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4851
4852                 boost::shared_ptr<Region> r ((*i)->region());
4853
4854                 TimeAxisView& tv = (*i)->get_time_axis_view();
4855                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4856                 latest_regionviews.clear ();
4857                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4858
4859                 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4860                 playlist = (*i)->region()->playlist();
4861                 playlist->clear_changes ();
4862                 playlist->duplicate (r, position, gap, times);
4863                 _session->add_command(new StatefulDiffCommand (playlist));
4864
4865                 c.disconnect ();
4866
4867                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4868         }
4869
4870         if (!foo.empty()) {
4871                 selection->set (foo);
4872         }
4873
4874         commit_reversible_command ();
4875 }
4876
4877 void
4878 Editor::duplicate_selection (float times)
4879 {
4880         if (selection->time.empty() || selection->tracks.empty()) {
4881                 return;
4882         }
4883
4884         boost::shared_ptr<Playlist> playlist;
4885
4886         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4887
4888         bool in_command = false;
4889
4890         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4891                 if ((playlist = (*i)->playlist()) == 0) {
4892                         continue;
4893                 }
4894                 playlist->clear_changes ();
4895
4896                 if (clicked_selection) {
4897                         playlist->duplicate_range (selection->time[clicked_selection], times);
4898                 } else {
4899                         playlist->duplicate_ranges (selection->time, times);
4900                 }
4901
4902                 if (!in_command) {
4903                         begin_reversible_command (_("duplicate range selection"));
4904                         in_command = true;
4905                 }
4906                 _session->add_command (new StatefulDiffCommand (playlist));
4907
4908         }
4909
4910         if (in_command) {
4911                 if (times == 1.0f) {
4912                         // now "move" range selection to after the current range selection
4913                         framecnt_t distance = 0;
4914
4915                         if (clicked_selection) {
4916                                 distance =
4917                                     selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4918                         } else {
4919                                 distance = selection->time.end_frame () - selection->time.start ();
4920                         }
4921
4922                         selection->move_time (distance);
4923                 }
4924                 commit_reversible_command ();
4925         }
4926 }
4927
4928 /** Reset all selected points to the relevant default value */
4929 void
4930 Editor::reset_point_selection ()
4931 {
4932         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4933                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4934                 (*j)->value = (*i)->line().the_list()->default_value ();
4935         }
4936 }
4937
4938 void
4939 Editor::center_playhead ()
4940 {
4941         float const page = _visible_canvas_width * samples_per_pixel;
4942         center_screen_internal (playhead_cursor->current_frame (), page);
4943 }
4944
4945 void
4946 Editor::center_edit_point ()
4947 {
4948         float const page = _visible_canvas_width * samples_per_pixel;
4949         center_screen_internal (get_preferred_edit_position(), page);
4950 }
4951
4952 /** Caller must begin and commit a reversible command */
4953 void
4954 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4955 {
4956         playlist->clear_changes ();
4957         playlist->clear ();
4958         _session->add_command (new StatefulDiffCommand (playlist));
4959 }
4960
4961 void
4962 Editor::nudge_track (bool use_edit, bool forwards)
4963 {
4964         boost::shared_ptr<Playlist> playlist;
4965         framepos_t distance;
4966         framepos_t next_distance;
4967         framepos_t start;
4968
4969         if (use_edit) {
4970                 start = get_preferred_edit_position();
4971         } else {
4972                 start = 0;
4973         }
4974
4975         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4976                 return;
4977         }
4978
4979         if (selection->tracks.empty()) {
4980                 return;
4981         }
4982
4983         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4984         bool in_command = false;
4985
4986         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4987
4988                 if ((playlist = (*i)->playlist()) == 0) {
4989                         continue;
4990                 }
4991
4992                 playlist->clear_changes ();
4993                 playlist->clear_owned_changes ();
4994
4995                 playlist->nudge_after (start, distance, forwards);
4996
4997                 if (!in_command) {
4998                         begin_reversible_command (_("nudge track"));
4999                         in_command = true;
5000                 }
5001                 vector<Command*> cmds;
5002
5003                 playlist->rdiff (cmds);
5004                 _session->add_commands (cmds);
5005
5006                 _session->add_command (new StatefulDiffCommand (playlist));
5007         }
5008
5009         if (in_command) {
5010                 commit_reversible_command ();
5011         }
5012 }
5013
5014 void
5015 Editor::remove_last_capture ()
5016 {
5017         vector<string> choices;
5018         string prompt;
5019
5020         if (!_session) {
5021                 return;
5022         }
5023
5024         if (Config->get_verify_remove_last_capture()) {
5025                 prompt  = _("Do you really want to destroy the last capture?"
5026                             "\n(This is destructive and cannot be undone)");
5027
5028                 choices.push_back (_("No, do nothing."));
5029                 choices.push_back (_("Yes, destroy it."));
5030
5031                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5032
5033                 if (prompter.run () == 1) {
5034                         _session->remove_last_capture ();
5035                         _regions->redisplay ();
5036                 }
5037
5038         } else {
5039                 _session->remove_last_capture();
5040                 _regions->redisplay ();
5041         }
5042 }
5043
5044 void
5045 Editor::normalize_region ()
5046 {
5047         if (!_session) {
5048                 return;
5049         }
5050
5051         RegionSelection rs = get_regions_from_selection_and_entered ();
5052
5053         if (rs.empty()) {
5054                 return;
5055         }
5056
5057         NormalizeDialog dialog (rs.size() > 1);
5058
5059         if (dialog.run () != RESPONSE_ACCEPT) {
5060                 return;
5061         }
5062
5063         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5064         gdk_flush ();
5065
5066         /* XXX: should really only count audio regions here */
5067         int const regions = rs.size ();
5068
5069         /* Make a list of the selected audio regions' maximum amplitudes, and also
5070            obtain the maximum amplitude of them all.
5071         */
5072         list<double> max_amps;
5073         list<double> rms_vals;
5074         double max_amp = 0;
5075         double max_rms = 0;
5076         bool use_rms = dialog.constrain_rms ();
5077
5078         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5079                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5080                 if (!arv) {
5081                         continue;
5082                 }
5083                 dialog.descend (1.0 / regions);
5084                 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5085                 if (use_rms) {
5086                         double r = arv->audio_region()->rms (&dialog);
5087                         max_rms = max (max_rms, r);
5088                         rms_vals.push_back (r);
5089                 }
5090
5091                 if (a == -1) {
5092                         /* the user cancelled the operation */
5093                         return;
5094                 }
5095
5096                 max_amps.push_back (a);
5097                 max_amp = max (max_amp, a);
5098                 dialog.ascend ();
5099         }
5100
5101         list<double>::const_iterator a = max_amps.begin ();
5102         list<double>::const_iterator l = rms_vals.begin ();
5103         bool in_command = false;
5104
5105         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5106                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5107                 if (!arv) {
5108                         continue;
5109                 }
5110
5111                 arv->region()->clear_changes ();
5112
5113                 double amp = dialog.normalize_individually() ? *a : max_amp;
5114                 double target = dialog.target_peak (); // dB
5115
5116                 if (use_rms) {
5117                         double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5118                         const double t_rms = dialog.target_rms ();
5119                         const gain_t c_peak = dB_to_coefficient (target);
5120                         const gain_t c_rms  = dB_to_coefficient (t_rms);
5121                         if ((amp_rms / c_rms) > (amp / c_peak)) {
5122                                 amp = amp_rms;
5123                                 target = t_rms;
5124                         }
5125                 }
5126
5127                 arv->audio_region()->normalize (amp, target);
5128
5129                 if (!in_command) {
5130                         begin_reversible_command (_("normalize"));
5131                         in_command = true;
5132                 }
5133                 _session->add_command (new StatefulDiffCommand (arv->region()));
5134
5135                 ++a;
5136                 ++l;
5137         }
5138
5139         if (in_command) {
5140                 commit_reversible_command ();
5141         }
5142 }
5143
5144
5145 void
5146 Editor::reset_region_scale_amplitude ()
5147 {
5148         if (!_session) {
5149                 return;
5150         }
5151
5152         RegionSelection rs = get_regions_from_selection_and_entered ();
5153
5154         if (rs.empty()) {
5155                 return;
5156         }
5157
5158         bool in_command = false;
5159
5160         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5161                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5162                 if (!arv)
5163                         continue;
5164                 arv->region()->clear_changes ();
5165                 arv->audio_region()->set_scale_amplitude (1.0f);
5166
5167                 if(!in_command) {
5168                                 begin_reversible_command ("reset gain");
5169                                 in_command = true;
5170                 }
5171                 _session->add_command (new StatefulDiffCommand (arv->region()));
5172         }
5173
5174         if (in_command) {
5175                 commit_reversible_command ();
5176         }
5177 }
5178
5179 void
5180 Editor::adjust_region_gain (bool up)
5181 {
5182         RegionSelection rs = get_regions_from_selection_and_entered ();
5183
5184         if (!_session || rs.empty()) {
5185                 return;
5186         }
5187
5188         bool in_command = false;
5189
5190         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5191                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5192                 if (!arv) {
5193                         continue;
5194                 }
5195
5196                 arv->region()->clear_changes ();
5197
5198                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5199
5200                 if (up) {
5201                         dB += 1;
5202                 } else {
5203                         dB -= 1;
5204                 }
5205
5206                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5207
5208                 if (!in_command) {
5209                                 begin_reversible_command ("adjust region gain");
5210                                 in_command = true;
5211                 }
5212                 _session->add_command (new StatefulDiffCommand (arv->region()));
5213         }
5214
5215         if (in_command) {
5216                 commit_reversible_command ();
5217         }
5218 }
5219
5220 void
5221 Editor::reset_region_gain ()
5222 {
5223         RegionSelection rs = get_regions_from_selection_and_entered ();
5224
5225         if (!_session || rs.empty()) {
5226                 return;
5227         }
5228
5229         bool in_command = false;
5230
5231         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5232                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5233                 if (!arv) {
5234                         continue;
5235                 }
5236
5237                 arv->region()->clear_changes ();
5238
5239                 arv->audio_region()->set_scale_amplitude (1.0f);
5240
5241                 if (!in_command) {
5242                                 begin_reversible_command ("reset region gain");
5243                                 in_command = true;
5244                 }
5245                 _session->add_command (new StatefulDiffCommand (arv->region()));
5246         }
5247
5248         if (in_command) {
5249                 commit_reversible_command ();
5250         }
5251 }
5252
5253 void
5254 Editor::reverse_region ()
5255 {
5256         if (!_session) {
5257                 return;
5258         }
5259
5260         Reverse rev (*_session);
5261         apply_filter (rev, _("reverse regions"));
5262 }
5263
5264 void
5265 Editor::strip_region_silence ()
5266 {
5267         if (!_session) {
5268                 return;
5269         }
5270
5271         RegionSelection rs = get_regions_from_selection_and_entered ();
5272
5273         if (rs.empty()) {
5274                 return;
5275         }
5276
5277         std::list<RegionView*> audio_only;
5278
5279         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5280                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5281                 if (arv) {
5282                         audio_only.push_back (arv);
5283                 }
5284         }
5285
5286         assert (!audio_only.empty());
5287
5288         StripSilenceDialog d (_session, audio_only);
5289         int const r = d.run ();
5290
5291         d.drop_rects ();
5292
5293         if (r == Gtk::RESPONSE_OK) {
5294                 ARDOUR::AudioIntervalMap silences;
5295                 d.silences (silences);
5296                 StripSilence s (*_session, silences, d.fade_length());
5297
5298                 apply_filter (s, _("strip silence"), &d);
5299         }
5300 }
5301
5302 Command*
5303 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5304 {
5305         Evoral::Sequence<Evoral::Beats>::Notes selected;
5306         mrv.selection_as_notelist (selected, true);
5307
5308         vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5309         v.push_back (selected);
5310
5311         Evoral::Beats pos_beats  = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5312
5313         return op (mrv.midi_region()->model(), pos_beats, v);
5314 }
5315
5316 void
5317 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5318 {
5319         if (rs.empty()) {
5320                 return;
5321         }
5322
5323         bool in_command = false;
5324
5325         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5326                 RegionSelection::const_iterator tmp = r;
5327                 ++tmp;
5328
5329                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5330
5331                 if (mrv) {
5332                         Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5333                         if (cmd) {
5334                                 if (!in_command) {
5335                                         begin_reversible_command (op.name ());
5336                                         in_command = true;
5337                                 }
5338                                 (*cmd)();
5339                                 _session->add_command (cmd);
5340                         }
5341                 }
5342
5343                 r = tmp;
5344         }
5345
5346         if (in_command) {
5347                 commit_reversible_command ();
5348         }
5349 }
5350
5351 void
5352 Editor::fork_region ()
5353 {
5354         RegionSelection rs = get_regions_from_selection_and_entered ();
5355
5356         if (rs.empty()) {
5357                 return;
5358         }
5359
5360         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5361         bool in_command = false;
5362
5363         gdk_flush ();
5364
5365         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5366                 RegionSelection::iterator tmp = r;
5367                 ++tmp;
5368
5369                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5370
5371                 if (mrv) {
5372                         try {
5373                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5374                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5375                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5376
5377                                 if (!in_command) {
5378                                         begin_reversible_command (_("Fork Region(s)"));
5379                                         in_command = true;
5380                                 }
5381                                 playlist->clear_changes ();
5382                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5383                                 _session->add_command(new StatefulDiffCommand (playlist));
5384                         } catch (...) {
5385                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5386                         }
5387                 }
5388
5389                 r = tmp;
5390         }
5391
5392         if (in_command) {
5393                 commit_reversible_command ();
5394         }
5395 }
5396
5397 void
5398 Editor::quantize_region ()
5399 {
5400         if (_session) {
5401                 quantize_regions(get_regions_from_selection_and_entered ());
5402         }
5403 }
5404
5405 void
5406 Editor::quantize_regions (const RegionSelection& rs)
5407 {
5408         if (rs.n_midi_regions() == 0) {
5409                 return;
5410         }
5411
5412         if (!quantize_dialog) {
5413                 quantize_dialog = new QuantizeDialog (*this);
5414         }
5415
5416         if (quantize_dialog->is_mapped()) {
5417                 /* in progress already */
5418                 return;
5419         }
5420
5421         quantize_dialog->present ();
5422         const int r = quantize_dialog->run ();
5423         quantize_dialog->hide ();
5424
5425         if (r == Gtk::RESPONSE_OK) {
5426                 Quantize quant (quantize_dialog->snap_start(),
5427                                 quantize_dialog->snap_end(),
5428                                 quantize_dialog->start_grid_size(),
5429                                 quantize_dialog->end_grid_size(),
5430                                 quantize_dialog->strength(),
5431                                 quantize_dialog->swing(),
5432                                 quantize_dialog->threshold());
5433
5434                 apply_midi_note_edit_op (quant, rs);
5435         }
5436 }
5437
5438 void
5439 Editor::legatize_region (bool shrink_only)
5440 {
5441         if (_session) {
5442                 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5443         }
5444 }
5445
5446 void
5447 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5448 {
5449         if (rs.n_midi_regions() == 0) {
5450                 return;
5451         }
5452
5453         Legatize legatize(shrink_only);
5454         apply_midi_note_edit_op (legatize, rs);
5455 }
5456
5457 void
5458 Editor::transform_region ()
5459 {
5460         if (_session) {
5461                 transform_regions(get_regions_from_selection_and_entered ());
5462         }
5463 }
5464
5465 void
5466 Editor::transform_regions (const RegionSelection& rs)
5467 {
5468         if (rs.n_midi_regions() == 0) {
5469                 return;
5470         }
5471
5472         TransformDialog td;
5473
5474         td.present();
5475         const int r = td.run();
5476         td.hide();
5477
5478         if (r == Gtk::RESPONSE_OK) {
5479                 Transform transform(td.get());
5480                 apply_midi_note_edit_op(transform, rs);
5481         }
5482 }
5483
5484 void
5485 Editor::transpose_region ()
5486 {
5487         if (_session) {
5488                 transpose_regions(get_regions_from_selection_and_entered ());
5489         }
5490 }
5491
5492 void
5493 Editor::transpose_regions (const RegionSelection& rs)
5494 {
5495         if (rs.n_midi_regions() == 0) {
5496                 return;
5497         }
5498
5499         TransposeDialog d;
5500         int const r = d.run ();
5501
5502         if (r == RESPONSE_ACCEPT) {
5503                 Transpose transpose(d.semitones ());
5504                 apply_midi_note_edit_op (transpose, rs);
5505         }
5506 }
5507
5508 void
5509 Editor::insert_patch_change (bool from_context)
5510 {
5511         RegionSelection rs = get_regions_from_selection_and_entered ();
5512
5513         if (rs.empty ()) {
5514                 return;
5515         }
5516
5517         const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5518
5519         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5520            there may be more than one, but the PatchChangeDialog can only offer
5521            one set of patch menus.
5522         */
5523         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5524
5525         Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5526         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5527
5528         if (d.run() == RESPONSE_CANCEL) {
5529                 return;
5530         }
5531
5532         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5533                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5534                 if (mrv) {
5535                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5536                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5537                         }
5538                 }
5539         }
5540 }
5541
5542 void
5543 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5544 {
5545         RegionSelection rs = get_regions_from_selection_and_entered ();
5546
5547         if (rs.empty()) {
5548                 return;
5549         }
5550
5551         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5552         bool in_command = false;
5553
5554         gdk_flush ();
5555
5556         int n = 0;
5557         int const N = rs.size ();
5558
5559         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5560                 RegionSelection::iterator tmp = r;
5561                 ++tmp;
5562
5563                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5564                 if (arv) {
5565                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5566
5567                         if (progress) {
5568                                 progress->descend (1.0 / N);
5569                         }
5570
5571                         if (arv->audio_region()->apply (filter, progress) == 0) {
5572
5573                                 playlist->clear_changes ();
5574                                 playlist->clear_owned_changes ();
5575
5576                                 if (!in_command) {
5577                                         begin_reversible_command (command);
5578                                         in_command = true;
5579                                 }
5580
5581                                 if (filter.results.empty ()) {
5582
5583                                         /* no regions returned; remove the old one */
5584                                         playlist->remove_region (arv->region ());
5585
5586                                 } else {
5587
5588                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5589
5590                                         /* first region replaces the old one */
5591                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5592                                         ++res;
5593
5594                                         /* add the rest */
5595                                         while (res != filter.results.end()) {
5596                                                 playlist->add_region (*res, (*res)->position());
5597                                                 ++res;
5598                                         }
5599
5600                                 }
5601
5602                                 /* We might have removed regions, which alters other regions' layering_index,
5603                                    so we need to do a recursive diff here.
5604                                 */
5605                                 vector<Command*> cmds;
5606                                 playlist->rdiff (cmds);
5607                                 _session->add_commands (cmds);
5608
5609                                 _session->add_command(new StatefulDiffCommand (playlist));
5610                         }
5611
5612                         if (progress) {
5613                                 progress->ascend ();
5614                         }
5615                 }
5616
5617                 r = tmp;
5618                 ++n;
5619         }
5620
5621         if (in_command) {
5622                 commit_reversible_command ();
5623         }
5624 }
5625
5626 void
5627 Editor::external_edit_region ()
5628 {
5629         /* more to come */
5630 }
5631
5632 void
5633 Editor::reset_region_gain_envelopes ()
5634 {
5635         RegionSelection rs = get_regions_from_selection_and_entered ();
5636
5637         if (!_session || rs.empty()) {
5638                 return;
5639         }
5640
5641         bool in_command = false;
5642
5643         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5644                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5645                 if (arv) {
5646                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5647                         XMLNode& before (alist->get_state());
5648
5649                         arv->audio_region()->set_default_envelope ();
5650
5651                         if (!in_command) {
5652                                 begin_reversible_command (_("reset region gain"));
5653                                 in_command = true;
5654                         }
5655                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5656                 }
5657         }
5658
5659         if (in_command) {
5660                 commit_reversible_command ();
5661         }
5662 }
5663
5664 void
5665 Editor::set_region_gain_visibility (RegionView* rv)
5666 {
5667         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5668         if (arv) {
5669                 arv->update_envelope_visibility();
5670         }
5671 }
5672
5673 void
5674 Editor::set_gain_envelope_visibility ()
5675 {
5676         if (!_session) {
5677                 return;
5678         }
5679
5680         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5681                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5682                 if (v) {
5683                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5684                 }
5685         }
5686 }
5687
5688 void
5689 Editor::toggle_gain_envelope_active ()
5690 {
5691         if (_ignore_region_action) {
5692                 return;
5693         }
5694
5695         RegionSelection rs = get_regions_from_selection_and_entered ();
5696
5697         if (!_session || rs.empty()) {
5698                 return;
5699         }
5700
5701         bool in_command = false;
5702
5703         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5704                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5705                 if (arv) {
5706                         arv->region()->clear_changes ();
5707                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5708
5709                         if (!in_command) {
5710                                 begin_reversible_command (_("region gain envelope active"));
5711                                 in_command = true;
5712                         }
5713                         _session->add_command (new StatefulDiffCommand (arv->region()));
5714                 }
5715         }
5716
5717         if (in_command) {
5718                 commit_reversible_command ();
5719         }
5720 }
5721
5722 void
5723 Editor::toggle_region_lock ()
5724 {
5725         if (_ignore_region_action) {
5726                 return;
5727         }
5728
5729         RegionSelection rs = get_regions_from_selection_and_entered ();
5730
5731         if (!_session || rs.empty()) {
5732                 return;
5733         }
5734
5735         begin_reversible_command (_("toggle region lock"));
5736
5737         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5738                 (*i)->region()->clear_changes ();
5739                 (*i)->region()->set_locked (!(*i)->region()->locked());
5740                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5741         }
5742
5743         commit_reversible_command ();
5744 }
5745
5746 void
5747 Editor::toggle_region_video_lock ()
5748 {
5749         if (_ignore_region_action) {
5750                 return;
5751         }
5752
5753         RegionSelection rs = get_regions_from_selection_and_entered ();
5754
5755         if (!_session || rs.empty()) {
5756                 return;
5757         }
5758
5759         begin_reversible_command (_("Toggle Video Lock"));
5760
5761         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5762                 (*i)->region()->clear_changes ();
5763                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5764                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5765         }
5766
5767         commit_reversible_command ();
5768 }
5769
5770 void
5771 Editor::toggle_region_lock_style ()
5772 {
5773         if (_ignore_region_action) {
5774                 return;
5775         }
5776
5777         RegionSelection rs = get_regions_from_selection_and_entered ();
5778
5779         if (!_session || rs.empty()) {
5780                 return;
5781         }
5782
5783         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5784         vector<Widget*> proxies = a->get_proxies();
5785         Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5786
5787         assert (cmi);
5788
5789         begin_reversible_command (_("toggle region lock style"));
5790
5791         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5792                 (*i)->region()->clear_changes ();
5793                 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5794                 (*i)->region()->set_position_lock_style (ns);
5795                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5796         }
5797
5798         commit_reversible_command ();
5799 }
5800
5801 void
5802 Editor::toggle_opaque_region ()
5803 {
5804         if (_ignore_region_action) {
5805                 return;
5806         }
5807
5808         RegionSelection rs = get_regions_from_selection_and_entered ();
5809
5810         if (!_session || rs.empty()) {
5811                 return;
5812         }
5813
5814         begin_reversible_command (_("change region opacity"));
5815
5816         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5817                 (*i)->region()->clear_changes ();
5818                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5819                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5820         }
5821
5822         commit_reversible_command ();
5823 }
5824
5825 void
5826 Editor::toggle_record_enable ()
5827 {
5828         bool new_state = false;
5829         bool first = true;
5830         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5831                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5832                 if (!rtav)
5833                         continue;
5834                 if (!rtav->is_track())
5835                         continue;
5836
5837                 if (first) {
5838                         new_state = !rtav->track()->rec_enable_control()->get_value();
5839                         first = false;
5840                 }
5841
5842                 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5843         }
5844 }
5845
5846 void
5847 Editor::toggle_solo ()
5848 {
5849         bool new_state = false;
5850         bool first = true;
5851         boost::shared_ptr<ControlList> cl (new ControlList);
5852
5853         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5854                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5855
5856                 if (!rtav) {
5857                         continue;
5858                 }
5859
5860                 if (first) {
5861                         new_state = !rtav->route()->soloed ();
5862                         first = false;
5863                 }
5864
5865                 cl->push_back (rtav->route()->solo_control());
5866         }
5867
5868         _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5869 }
5870
5871 void
5872 Editor::toggle_mute ()
5873 {
5874         bool new_state = false;
5875         bool first = true;
5876         boost::shared_ptr<RouteList> rl (new RouteList);
5877
5878         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5879                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5880
5881                 if (!rtav) {
5882                         continue;
5883                 }
5884
5885                 if (first) {
5886                         new_state = !rtav->route()->muted();
5887                         first = false;
5888                 }
5889
5890                 rl->push_back (rtav->route());
5891         }
5892
5893         _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5894 }
5895
5896 void
5897 Editor::toggle_solo_isolate ()
5898 {
5899 }
5900
5901
5902 void
5903 Editor::fade_range ()
5904 {
5905         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5906
5907         begin_reversible_command (_("fade range"));
5908
5909         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5910                 (*i)->fade_range (selection->time);
5911         }
5912
5913         commit_reversible_command ();
5914 }
5915
5916
5917 void
5918 Editor::set_fade_length (bool in)
5919 {
5920         RegionSelection rs = get_regions_from_selection_and_entered ();
5921
5922         if (rs.empty()) {
5923                 return;
5924         }
5925
5926         /* we need a region to measure the offset from the start */
5927
5928         RegionView* rv = rs.front ();
5929
5930         framepos_t pos = get_preferred_edit_position();
5931         framepos_t len;
5932         char const * cmd;
5933
5934         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5935                 /* edit point is outside the relevant region */
5936                 return;
5937         }
5938
5939         if (in) {
5940                 if (pos <= rv->region()->position()) {
5941                         /* can't do it */
5942                         return;
5943                 }
5944                 len = pos - rv->region()->position();
5945                 cmd = _("set fade in length");
5946         } else {
5947                 if (pos >= rv->region()->last_frame()) {
5948                         /* can't do it */
5949                         return;
5950                 }
5951                 len = rv->region()->last_frame() - pos;
5952                 cmd = _("set fade out length");
5953         }
5954
5955         bool in_command = false;
5956
5957         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5958                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5959
5960                 if (!tmp) {
5961                         continue;
5962                 }
5963
5964                 boost::shared_ptr<AutomationList> alist;
5965                 if (in) {
5966                         alist = tmp->audio_region()->fade_in();
5967                 } else {
5968                         alist = tmp->audio_region()->fade_out();
5969                 }
5970
5971                 XMLNode &before = alist->get_state();
5972
5973                 if (in) {
5974                         tmp->audio_region()->set_fade_in_length (len);
5975                         tmp->audio_region()->set_fade_in_active (true);
5976                 } else {
5977                         tmp->audio_region()->set_fade_out_length (len);
5978                         tmp->audio_region()->set_fade_out_active (true);
5979                 }
5980
5981                 if (!in_command) {
5982                         begin_reversible_command (cmd);
5983                         in_command = true;
5984                 }
5985                 XMLNode &after = alist->get_state();
5986                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5987         }
5988
5989         if (in_command) {
5990                 commit_reversible_command ();
5991         }
5992 }
5993
5994 void
5995 Editor::set_fade_in_shape (FadeShape shape)
5996 {
5997         RegionSelection rs = get_regions_from_selection_and_entered ();
5998
5999         if (rs.empty()) {
6000                 return;
6001         }
6002         bool in_command = false;
6003
6004         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6005                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6006
6007                 if (!tmp) {
6008                         continue;
6009                 }
6010
6011                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6012                 XMLNode &before = alist->get_state();
6013
6014                 tmp->audio_region()->set_fade_in_shape (shape);
6015
6016                 if (!in_command) {
6017                         begin_reversible_command (_("set fade in shape"));
6018                         in_command = true;
6019                 }
6020                 XMLNode &after = alist->get_state();
6021                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6022         }
6023
6024         if (in_command) {
6025                 commit_reversible_command ();
6026         }
6027 }
6028
6029 void
6030 Editor::set_fade_out_shape (FadeShape shape)
6031 {
6032         RegionSelection rs = get_regions_from_selection_and_entered ();
6033
6034         if (rs.empty()) {
6035                 return;
6036         }
6037         bool in_command = false;
6038
6039         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6040                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6041
6042                 if (!tmp) {
6043                         continue;
6044                 }
6045
6046                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6047                 XMLNode &before = alist->get_state();
6048
6049                 tmp->audio_region()->set_fade_out_shape (shape);
6050
6051                 if(!in_command) {
6052                         begin_reversible_command (_("set fade out shape"));
6053                         in_command = true;
6054                 }
6055                 XMLNode &after = alist->get_state();
6056                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6057         }
6058
6059         if (in_command) {
6060                 commit_reversible_command ();
6061         }
6062 }
6063
6064 void
6065 Editor::set_fade_in_active (bool yn)
6066 {
6067         RegionSelection rs = get_regions_from_selection_and_entered ();
6068
6069         if (rs.empty()) {
6070                 return;
6071         }
6072         bool in_command = false;
6073
6074         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6075                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6076
6077                 if (!tmp) {
6078                         continue;
6079                 }
6080
6081
6082                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6083
6084                 ar->clear_changes ();
6085                 ar->set_fade_in_active (yn);
6086
6087                 if (!in_command) {
6088                         begin_reversible_command (_("set fade in active"));
6089                         in_command = true;
6090                 }
6091                 _session->add_command (new StatefulDiffCommand (ar));
6092         }
6093
6094         if (in_command) {
6095                 commit_reversible_command ();
6096         }
6097 }
6098
6099 void
6100 Editor::set_fade_out_active (bool yn)
6101 {
6102         RegionSelection rs = get_regions_from_selection_and_entered ();
6103
6104         if (rs.empty()) {
6105                 return;
6106         }
6107         bool in_command = false;
6108
6109         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6110                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6111
6112                 if (!tmp) {
6113                         continue;
6114                 }
6115
6116                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6117
6118                 ar->clear_changes ();
6119                 ar->set_fade_out_active (yn);
6120
6121                 if (!in_command) {
6122                         begin_reversible_command (_("set fade out active"));
6123                         in_command = true;
6124                 }
6125                 _session->add_command(new StatefulDiffCommand (ar));
6126         }
6127
6128         if (in_command) {
6129                 commit_reversible_command ();
6130         }
6131 }
6132
6133 void
6134 Editor::toggle_region_fades (int dir)
6135 {
6136         if (_ignore_region_action) {
6137                 return;
6138         }
6139
6140         boost::shared_ptr<AudioRegion> ar;
6141         bool yn = false;
6142
6143         RegionSelection rs = get_regions_from_selection_and_entered ();
6144
6145         if (rs.empty()) {
6146                 return;
6147         }
6148
6149         RegionSelection::iterator i;
6150         for (i = rs.begin(); i != rs.end(); ++i) {
6151                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6152                         if (dir == -1) {
6153                                 yn = ar->fade_out_active ();
6154                         } else {
6155                                 yn = ar->fade_in_active ();
6156                         }
6157                         break;
6158                 }
6159         }
6160
6161         if (i == rs.end()) {
6162                 return;
6163         }
6164
6165         /* XXX should this undo-able? */
6166         bool in_command = false;
6167
6168         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6169                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6170                         continue;
6171                 }
6172                 ar->clear_changes ();
6173
6174                 if (dir == 1 || dir == 0) {
6175                         ar->set_fade_in_active (!yn);
6176                 }
6177
6178                 if (dir == -1 || dir == 0) {
6179                         ar->set_fade_out_active (!yn);
6180                 }
6181                 if (!in_command) {
6182                         begin_reversible_command (_("toggle fade active"));
6183                         in_command = true;
6184                 }
6185                 _session->add_command(new StatefulDiffCommand (ar));
6186         }
6187
6188         if (in_command) {
6189                 commit_reversible_command ();
6190         }
6191 }
6192
6193
6194 /** Update region fade visibility after its configuration has been changed */
6195 void
6196 Editor::update_region_fade_visibility ()
6197 {
6198         bool _fade_visibility = _session->config.get_show_region_fades ();
6199
6200         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6201                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6202                 if (v) {
6203                         if (_fade_visibility) {
6204                                 v->audio_view()->show_all_fades ();
6205                         } else {
6206                                 v->audio_view()->hide_all_fades ();
6207                         }
6208                 }
6209         }
6210 }
6211
6212 void
6213 Editor::set_edit_point ()
6214 {
6215         bool ignored;
6216         MusicFrame where (0, 0);
6217
6218         if (!mouse_frame (where.frame, ignored)) {
6219                 return;
6220         }
6221
6222         snap_to (where);
6223
6224         if (selection->markers.empty()) {
6225
6226                 mouse_add_new_marker (where.frame);
6227
6228         } else {
6229                 bool ignored;
6230
6231                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6232
6233                 if (loc) {
6234                         loc->move_to (where.frame, where.division);
6235                 }
6236         }
6237 }
6238
6239 void
6240 Editor::set_playhead_cursor ()
6241 {
6242         if (entered_marker) {
6243                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6244         } else {
6245                 MusicFrame where (0, 0);
6246                 bool ignored;
6247
6248                 if (!mouse_frame (where.frame, ignored)) {
6249                         return;
6250                 }
6251
6252                 snap_to (where);
6253
6254                 if (_session) {
6255                         _session->request_locate (where.frame, _session->transport_rolling());
6256                 }
6257         }
6258
6259 //not sure what this was for;  remove it for now.
6260 //      if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6261 //              cancel_time_selection();
6262 //      }
6263
6264 }
6265
6266 void
6267 Editor::split_region ()
6268 {
6269         if (_drags->active ()) {
6270                 return;
6271         }
6272
6273         //if a range is selected, separate it
6274         if ( !selection->time.empty()) {
6275                 separate_regions_between (selection->time);
6276                 return;
6277         }
6278
6279         //if no range was selected, try to find some regions to split
6280         if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6281
6282                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6283                 const framepos_t pos = get_preferred_edit_position();
6284                 const int32_t division = get_grid_music_divisions (0);
6285                 MusicFrame where (pos, division);
6286
6287                 if (rs.empty()) {
6288                         return;
6289                 }
6290
6291                 split_regions_at (where, rs);
6292
6293         }
6294 }
6295
6296 void
6297 Editor::select_next_route()
6298 {
6299         if (selection->tracks.empty()) {
6300                 selection->set (track_views.front());
6301                 return;
6302         }
6303
6304         TimeAxisView* current = selection->tracks.front();
6305
6306         RouteUI *rui;
6307         do {
6308                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6309
6310                         if (*i == current) {
6311                                 ++i;
6312                                 if (i != track_views.end()) {
6313                                         current = (*i);
6314                                 } else {
6315                                         current = (*(track_views.begin()));
6316                                         //selection->set (*(track_views.begin()));
6317                                 }
6318                                 break;
6319                         }
6320                 }
6321
6322                 rui = dynamic_cast<RouteUI *>(current);
6323
6324         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6325
6326         selection->set (current);
6327
6328         ensure_time_axis_view_is_visible (*current, false);
6329 }
6330
6331 void
6332 Editor::select_prev_route()
6333 {
6334         if (selection->tracks.empty()) {
6335                 selection->set (track_views.front());
6336                 return;
6337         }
6338
6339         TimeAxisView* current = selection->tracks.front();
6340
6341         RouteUI *rui;
6342         do {
6343                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6344
6345                         if (*i == current) {
6346                                 ++i;
6347                                 if (i != track_views.rend()) {
6348                                         current = (*i);
6349                                 } else {
6350                                         current = *(track_views.rbegin());
6351                                 }
6352                                 break;
6353                         }
6354                 }
6355                 rui = dynamic_cast<RouteUI *>(current);
6356
6357         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6358
6359         selection->set (current);
6360
6361         ensure_time_axis_view_is_visible (*current, false);
6362 }
6363
6364 void
6365 Editor::set_loop_from_selection (bool play)
6366 {
6367         if (_session == 0) {
6368                 return;
6369         }
6370
6371         framepos_t start, end;
6372         if (!get_selection_extents ( start, end))
6373                 return;
6374
6375         set_loop_range (start, end,  _("set loop range from selection"));
6376
6377         if (play) {
6378                 _session->request_play_loop (true, true);
6379         }
6380 }
6381
6382 void
6383 Editor::set_loop_from_region (bool play)
6384 {
6385         framepos_t start, end;
6386         if (!get_selection_extents ( start, end))
6387                 return;
6388
6389         set_loop_range (start, end, _("set loop range from region"));
6390
6391         if (play) {
6392                 _session->request_locate (start, true);
6393                 _session->request_play_loop (true);
6394         }
6395 }
6396
6397 void
6398 Editor::set_punch_from_selection ()
6399 {
6400         if (_session == 0) {
6401                 return;
6402         }
6403
6404         framepos_t start, end;
6405         if (!get_selection_extents ( start, end))
6406                 return;
6407
6408         set_punch_range (start, end,  _("set punch range from selection"));
6409 }
6410
6411 void
6412 Editor::set_auto_punch_range ()
6413 {
6414         // auto punch in/out button from a single button
6415         // If Punch In is unset, set punch range from playhead to end, enable punch in
6416         // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6417         //   rewound beyond the Punch In marker, in which case that marker will be moved back
6418         //   to the current playhead position.
6419         // If punch out is set, it clears the punch range and Punch In/Out buttons
6420
6421         if (_session == 0) {
6422                 return;
6423         }
6424
6425         Location* tpl = transport_punch_location();
6426         framepos_t now = playhead_cursor->current_frame();
6427         framepos_t begin = now;
6428         framepos_t end = _session->current_end_frame();
6429
6430         if (!_session->config.get_punch_in()) {
6431                 // First Press - set punch in and create range from here to eternity
6432                 set_punch_range (begin, end, _("Auto Punch In"));
6433                 _session->config.set_punch_in(true);
6434         } else if (tpl && !_session->config.get_punch_out()) {
6435                 // Second press - update end range marker and set punch_out
6436                 if (now < tpl->start()) {
6437                         // playhead has been rewound - move start back  and pretend nothing happened
6438                         begin = now;
6439                         set_punch_range (begin, end, _("Auto Punch In/Out"));
6440                 } else {
6441                         // normal case for 2nd press - set the punch out
6442                         end = playhead_cursor->current_frame ();
6443                         set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6444                         _session->config.set_punch_out(true);
6445                 }
6446         } else  {
6447                 if (_session->config.get_punch_out()) {
6448                         _session->config.set_punch_out(false);
6449                 }
6450
6451                 if (_session->config.get_punch_in()) {
6452                         _session->config.set_punch_in(false);
6453                 }
6454
6455                 if (tpl)
6456                 {
6457                         // third press - unset punch in/out and remove range
6458                         _session->locations()->remove(tpl);
6459                 }
6460         }
6461
6462 }
6463
6464 void
6465 Editor::set_session_extents_from_selection ()
6466 {
6467         if (_session == 0) {
6468                 return;
6469         }
6470
6471         framepos_t start, end;
6472         if (!get_selection_extents ( start, end))
6473                 return;
6474
6475         Location* loc;
6476         if ((loc = _session->locations()->session_range_location()) == 0) {
6477                 _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6478         } else {
6479                 XMLNode &before = loc->get_state();
6480
6481                 _session->set_session_extents (start, end);
6482
6483                 XMLNode &after = loc->get_state();
6484
6485                 begin_reversible_command (_("set session start/end from selection"));
6486
6487                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6488
6489                 commit_reversible_command ();
6490         }
6491
6492         _session->set_end_is_free (false);
6493 }
6494
6495 void
6496 Editor::set_punch_start_from_edit_point ()
6497 {
6498         if (_session) {
6499
6500                 MusicFrame start (0, 0);
6501                 framepos_t end = max_framepos;
6502
6503                 //use the existing punch end, if any
6504                 Location* tpl = transport_punch_location();
6505                 if (tpl) {
6506                         end = tpl->end();
6507                 }
6508
6509                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6510                         start.frame = _session->audible_frame();
6511                 } else {
6512                         start.frame = get_preferred_edit_position();
6513                 }
6514
6515                 //snap the selection start/end
6516                 snap_to(start);
6517
6518                 //if there's not already a sensible selection endpoint, go "forever"
6519                 if (start.frame > end ) {
6520                         end = max_framepos;
6521                 }
6522
6523                 set_punch_range (start.frame, end, _("set punch start from EP"));
6524         }
6525
6526 }
6527
6528 void
6529 Editor::set_punch_end_from_edit_point ()
6530 {
6531         if (_session) {
6532
6533                 framepos_t start = 0;
6534                 MusicFrame end (max_framepos, 0);
6535
6536                 //use the existing punch start, if any
6537                 Location* tpl = transport_punch_location();
6538                 if (tpl) {
6539                         start = tpl->start();
6540                 }
6541
6542                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6543                         end.frame = _session->audible_frame();
6544                 } else {
6545                         end.frame = get_preferred_edit_position();
6546                 }
6547
6548                 //snap the selection start/end
6549                 snap_to (end);
6550
6551                 set_punch_range (start, end.frame, _("set punch end from EP"));
6552
6553         }
6554 }
6555
6556 void
6557 Editor::set_loop_start_from_edit_point ()
6558 {
6559         if (_session) {
6560
6561                 MusicFrame start (0, 0);
6562                 framepos_t end = max_framepos;
6563
6564                 //use the existing loop end, if any
6565                 Location* tpl = transport_loop_location();
6566                 if (tpl) {
6567                         end = tpl->end();
6568                 }
6569
6570                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6571                         start.frame = _session->audible_frame();
6572                 } else {
6573                         start.frame = get_preferred_edit_position();
6574                 }
6575
6576                 //snap the selection start/end
6577                 snap_to (start);
6578
6579                 //if there's not already a sensible selection endpoint, go "forever"
6580                 if (start.frame > end ) {
6581                         end = max_framepos;
6582                 }
6583
6584                 set_loop_range (start.frame, end, _("set loop start from EP"));
6585         }
6586
6587 }
6588
6589 void
6590 Editor::set_loop_end_from_edit_point ()
6591 {
6592         if (_session) {
6593
6594                 framepos_t start = 0;
6595                 MusicFrame end (max_framepos, 0);
6596
6597                 //use the existing loop start, if any
6598                 Location* tpl = transport_loop_location();
6599                 if (tpl) {
6600                         start = tpl->start();
6601                 }
6602
6603                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6604                         end.frame = _session->audible_frame();
6605                 } else {
6606                         end.frame = get_preferred_edit_position();
6607                 }
6608
6609                 //snap the selection start/end
6610                 snap_to(end);
6611
6612                 set_loop_range (start, end.frame, _("set loop end from EP"));
6613         }
6614 }
6615
6616 void
6617 Editor::set_punch_from_region ()
6618 {
6619         framepos_t start, end;
6620         if (!get_selection_extents ( start, end))
6621                 return;
6622
6623         set_punch_range (start, end, _("set punch range from region"));
6624 }
6625
6626 void
6627 Editor::pitch_shift_region ()
6628 {
6629         RegionSelection rs = get_regions_from_selection_and_entered ();
6630
6631         RegionSelection audio_rs;
6632         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6633                 if (dynamic_cast<AudioRegionView*> (*i)) {
6634                         audio_rs.push_back (*i);
6635                 }
6636         }
6637
6638         if (audio_rs.empty()) {
6639                 return;
6640         }
6641
6642         pitch_shift (audio_rs, 1.2);
6643 }
6644
6645 void
6646 Editor::set_tempo_from_region ()
6647 {
6648         RegionSelection rs = get_regions_from_selection_and_entered ();
6649
6650         if (!_session || rs.empty()) {
6651                 return;
6652         }
6653
6654         RegionView* rv = rs.front();
6655
6656         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6657 }
6658
6659 void
6660 Editor::use_range_as_bar ()
6661 {
6662         framepos_t start, end;
6663         if (get_edit_op_range (start, end)) {
6664                 define_one_bar (start, end);
6665         }
6666 }
6667
6668 void
6669 Editor::define_one_bar (framepos_t start, framepos_t end)
6670 {
6671         framepos_t length = end - start;
6672
6673         const Meter& m (_session->tempo_map().meter_at_frame (start));
6674
6675         /* length = 1 bar */
6676
6677         /* We're going to deliver a constant tempo here,
6678            so we can use frames per beat to determine length.
6679            now we want frames per beat.
6680            we have frames per bar, and beats per bar, so ...
6681         */
6682
6683         /* XXXX METER MATH */
6684
6685         double frames_per_beat = length / m.divisions_per_bar();
6686
6687         /* beats per minute = */
6688
6689         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6690
6691         /* now decide whether to:
6692
6693             (a) set global tempo
6694             (b) add a new tempo marker
6695
6696         */
6697
6698         const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6699
6700         bool do_global = false;
6701
6702         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6703
6704                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6705                    at the start, or create a new marker
6706                 */
6707
6708                 vector<string> options;
6709                 options.push_back (_("Cancel"));
6710                 options.push_back (_("Add new marker"));
6711                 options.push_back (_("Set global tempo"));
6712
6713                 Choice c (
6714                         _("Define one bar"),
6715                         _("Do you want to set the global tempo or add a new tempo marker?"),
6716                         options
6717                         );
6718
6719                 c.set_default_response (2);
6720
6721                 switch (c.run()) {
6722                 case 0:
6723                         return;
6724
6725                 case 2:
6726                         do_global = true;
6727                         break;
6728
6729                 default:
6730                         do_global = false;
6731                 }
6732
6733         } else {
6734
6735                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6736                    if the marker is at the region starter, change it, otherwise add
6737                    a new tempo marker
6738                 */
6739         }
6740
6741         begin_reversible_command (_("set tempo from region"));
6742         XMLNode& before (_session->tempo_map().get_state());
6743
6744         if (do_global) {
6745                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6746         } else if (t.frame() == start) {
6747                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6748         } else {
6749                 /* constant tempo */
6750                 const Tempo tempo (beats_per_minute, t.note_type());
6751                 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6752         }
6753
6754         XMLNode& after (_session->tempo_map().get_state());
6755
6756         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6757         commit_reversible_command ();
6758 }
6759
6760 void
6761 Editor::split_region_at_transients ()
6762 {
6763         AnalysisFeatureList positions;
6764
6765         RegionSelection rs = get_regions_from_selection_and_entered ();
6766
6767         if (!_session || rs.empty()) {
6768                 return;
6769         }
6770
6771         begin_reversible_command (_("split regions"));
6772
6773         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6774
6775                 RegionSelection::iterator tmp;
6776
6777                 tmp = i;
6778                 ++tmp;
6779
6780                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6781
6782                 if (ar) {
6783                         ar->transients (positions);
6784                         split_region_at_points ((*i)->region(), positions, true);
6785                         positions.clear ();
6786                 }
6787
6788                 i = tmp;
6789         }
6790
6791         commit_reversible_command ();
6792
6793 }
6794
6795 void
6796 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6797 {
6798         bool use_rhythmic_rodent = false;
6799
6800         boost::shared_ptr<Playlist> pl = r->playlist();
6801
6802         list<boost::shared_ptr<Region> > new_regions;
6803
6804         if (!pl) {
6805                 return;
6806         }
6807
6808         if (positions.empty()) {
6809                 return;
6810         }
6811
6812         if (positions.size() > 20 && can_ferret) {
6813                 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);
6814                 MessageDialog msg (msgstr,
6815                                    false,
6816                                    Gtk::MESSAGE_INFO,
6817                                    Gtk::BUTTONS_OK_CANCEL);
6818
6819                 if (can_ferret) {
6820                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6821                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6822                 } else {
6823                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6824                 }
6825
6826                 msg.set_title (_("Excessive split?"));
6827                 msg.present ();
6828
6829                 int response = msg.run();
6830                 msg.hide ();
6831
6832                 switch (response) {
6833                 case RESPONSE_OK:
6834                         break;
6835                 case RESPONSE_APPLY:
6836                         use_rhythmic_rodent = true;
6837                         break;
6838                 default:
6839                         return;
6840                 }
6841         }
6842
6843         if (use_rhythmic_rodent) {
6844                 show_rhythm_ferret ();
6845                 return;
6846         }
6847
6848         AnalysisFeatureList::const_iterator x;
6849
6850         pl->clear_changes ();
6851         pl->clear_owned_changes ();
6852
6853         x = positions.begin();
6854
6855         if (x == positions.end()) {
6856                 return;
6857         }
6858
6859         pl->freeze ();
6860         pl->remove_region (r);
6861
6862         framepos_t pos = 0;
6863
6864         framepos_t rstart = r->first_frame ();
6865         framepos_t rend = r->last_frame ();
6866
6867         while (x != positions.end()) {
6868
6869                 /* deal with positons that are out of scope of present region bounds */
6870                 if (*x <= rstart || *x > rend) {
6871                         ++x;
6872                         continue;
6873                 }
6874
6875                 /* file start = original start + how far we from the initial position ?  */
6876
6877                 framepos_t file_start = r->start() + pos;
6878
6879                 /* length = next position - current position */
6880
6881                 framepos_t len = (*x) - pos - rstart;
6882
6883                 /* XXX we do we really want to allow even single-sample regions?
6884                  * shouldn't we have some kind of lower limit on region size?
6885                  */
6886
6887                 if (len <= 0) {
6888                         break;
6889                 }
6890
6891                 string new_name;
6892
6893                 if (RegionFactory::region_name (new_name, r->name())) {
6894                         break;
6895                 }
6896
6897                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6898
6899                 PropertyList plist;
6900
6901                 plist.add (ARDOUR::Properties::start, file_start);
6902                 plist.add (ARDOUR::Properties::length, len);
6903                 plist.add (ARDOUR::Properties::name, new_name);
6904                 plist.add (ARDOUR::Properties::layer, 0);
6905                 // TODO set transients_offset
6906
6907                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6908                 /* because we set annouce to false, manually add the new region to the
6909                  * RegionFactory map
6910                  */
6911                 RegionFactory::map_add (nr);
6912
6913                 pl->add_region (nr, rstart + pos);
6914
6915                 if (select_new) {
6916                         new_regions.push_front(nr);
6917                 }
6918
6919                 pos += len;
6920                 ++x;
6921         }
6922
6923         string new_name;
6924
6925         RegionFactory::region_name (new_name, r->name());
6926
6927         /* Add the final region */
6928         PropertyList plist;
6929
6930         plist.add (ARDOUR::Properties::start, r->start() + pos);
6931         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6932         plist.add (ARDOUR::Properties::name, new_name);
6933         plist.add (ARDOUR::Properties::layer, 0);
6934
6935         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6936         /* because we set annouce to false, manually add the new region to the
6937            RegionFactory map
6938         */
6939         RegionFactory::map_add (nr);
6940         pl->add_region (nr, r->position() + pos);
6941
6942         if (select_new) {
6943                 new_regions.push_front(nr);
6944         }
6945
6946         pl->thaw ();
6947
6948         /* We might have removed regions, which alters other regions' layering_index,
6949            so we need to do a recursive diff here.
6950         */
6951         vector<Command*> cmds;
6952         pl->rdiff (cmds);
6953         _session->add_commands (cmds);
6954
6955         _session->add_command (new StatefulDiffCommand (pl));
6956
6957         if (select_new) {
6958
6959                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6960                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6961                 }
6962         }
6963 }
6964
6965 void
6966 Editor::place_transient()
6967 {
6968         if (!_session) {
6969                 return;
6970         }
6971
6972         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6973
6974         if (rs.empty()) {
6975                 return;
6976         }
6977
6978         framepos_t where = get_preferred_edit_position();
6979
6980         begin_reversible_command (_("place transient"));
6981
6982         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6983                 (*r)->region()->add_transient(where);
6984         }
6985
6986         commit_reversible_command ();
6987 }
6988
6989 void
6990 Editor::remove_transient(ArdourCanvas::Item* item)
6991 {
6992         if (!_session) {
6993                 return;
6994         }
6995
6996         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6997         assert (_line);
6998
6999         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7000         _arv->remove_transient (*(float*) _line->get_data ("position"));
7001 }
7002
7003 void
7004 Editor::snap_regions_to_grid ()
7005 {
7006         list <boost::shared_ptr<Playlist > > used_playlists;
7007
7008         RegionSelection rs = get_regions_from_selection_and_entered ();
7009
7010         if (!_session || rs.empty()) {
7011                 return;
7012         }
7013
7014         begin_reversible_command (_("snap regions to grid"));
7015
7016         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7017
7018                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7019
7020                 if (!pl->frozen()) {
7021                         /* we haven't seen this playlist before */
7022
7023                         /* remember used playlists so we can thaw them later */
7024                         used_playlists.push_back(pl);
7025                         pl->freeze();
7026                 }
7027                 (*r)->region()->clear_changes ();
7028
7029                 MusicFrame start ((*r)->region()->first_frame (), 0);
7030                 snap_to (start);
7031                 (*r)->region()->set_position (start.frame, start.division);
7032                 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7033         }
7034
7035         while (used_playlists.size() > 0) {
7036                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7037                 (*i)->thaw();
7038                 used_playlists.pop_front();
7039         }
7040
7041         commit_reversible_command ();
7042 }
7043
7044 void
7045 Editor::close_region_gaps ()
7046 {
7047         list <boost::shared_ptr<Playlist > > used_playlists;
7048
7049         RegionSelection rs = get_regions_from_selection_and_entered ();
7050
7051         if (!_session || rs.empty()) {
7052                 return;
7053         }
7054
7055         Dialog dialog (_("Close Region Gaps"));
7056
7057         Table table (2, 3);
7058         table.set_spacings (12);
7059         table.set_border_width (12);
7060         Label* l = manage (left_aligned_label (_("Crossfade length")));
7061         table.attach (*l, 0, 1, 0, 1);
7062
7063         SpinButton spin_crossfade (1, 0);
7064         spin_crossfade.set_range (0, 15);
7065         spin_crossfade.set_increments (1, 1);
7066         spin_crossfade.set_value (5);
7067         table.attach (spin_crossfade, 1, 2, 0, 1);
7068
7069         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7070
7071         l = manage (left_aligned_label (_("Pull-back length")));
7072         table.attach (*l, 0, 1, 1, 2);
7073
7074         SpinButton spin_pullback (1, 0);
7075         spin_pullback.set_range (0, 100);
7076         spin_pullback.set_increments (1, 1);
7077         spin_pullback.set_value(30);
7078         table.attach (spin_pullback, 1, 2, 1, 2);
7079
7080         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7081
7082         dialog.get_vbox()->pack_start (table);
7083         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7084         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7085         dialog.show_all ();
7086
7087         if (dialog.run () == RESPONSE_CANCEL) {
7088                 return;
7089         }
7090
7091         framepos_t crossfade_len = spin_crossfade.get_value();
7092         framepos_t pull_back_frames = spin_pullback.get_value();
7093
7094         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7095         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7096
7097         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7098
7099         begin_reversible_command (_("close region gaps"));
7100
7101         int idx = 0;
7102         boost::shared_ptr<Region> last_region;
7103
7104         rs.sort_by_position_and_track();
7105
7106         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7107
7108                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7109
7110                 if (!pl->frozen()) {
7111                         /* we haven't seen this playlist before */
7112
7113                         /* remember used playlists so we can thaw them later */
7114                         used_playlists.push_back(pl);
7115                         pl->freeze();
7116                 }
7117
7118                 framepos_t position = (*r)->region()->position();
7119
7120                 if (idx == 0 || position < last_region->position()){
7121                         last_region = (*r)->region();
7122                         idx++;
7123                         continue;
7124                 }
7125
7126                 (*r)->region()->clear_changes ();
7127                 (*r)->region()->trim_front( (position - pull_back_frames));
7128
7129                 last_region->clear_changes ();
7130                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7131
7132                 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7133                 _session->add_command (new StatefulDiffCommand (last_region));
7134
7135                 last_region = (*r)->region();
7136                 idx++;
7137         }
7138
7139         while (used_playlists.size() > 0) {
7140                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7141                 (*i)->thaw();
7142                 used_playlists.pop_front();
7143         }
7144
7145         commit_reversible_command ();
7146 }
7147
7148 void
7149 Editor::tab_to_transient (bool forward)
7150 {
7151         AnalysisFeatureList positions;
7152
7153         RegionSelection rs = get_regions_from_selection_and_entered ();
7154
7155         if (!_session) {
7156                 return;
7157         }
7158
7159         framepos_t pos = _session->audible_frame ();
7160
7161         if (!selection->tracks.empty()) {
7162
7163                 /* don't waste time searching for transients in duplicate playlists.
7164                  */
7165
7166                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7167
7168                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7169
7170                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7171
7172                         if (rtv) {
7173                                 boost::shared_ptr<Track> tr = rtv->track();
7174                                 if (tr) {
7175                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
7176                                         if (pl) {
7177                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7178
7179                                                 if (result >= 0) {
7180                                                         positions.push_back (result);
7181                                                 }
7182                                         }
7183                                 }
7184                         }
7185                 }
7186
7187         } else {
7188
7189                 if (rs.empty()) {
7190                         return;
7191                 }
7192
7193                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7194                         (*r)->region()->get_transients (positions);
7195                 }
7196         }
7197
7198         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7199
7200         if (forward) {
7201                 AnalysisFeatureList::iterator x;
7202
7203                 for (x = positions.begin(); x != positions.end(); ++x) {
7204                         if ((*x) > pos) {
7205                                 break;
7206                         }
7207                 }
7208
7209                 if (x != positions.end ()) {
7210                         _session->request_locate (*x);
7211                 }
7212
7213         } else {
7214                 AnalysisFeatureList::reverse_iterator x;
7215
7216                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7217                         if ((*x) < pos) {
7218                                 break;
7219                         }
7220                 }
7221
7222                 if (x != positions.rend ()) {
7223                         _session->request_locate (*x);
7224                 }
7225         }
7226 }
7227
7228 void
7229 Editor::playhead_forward_to_grid ()
7230 {
7231         if (!_session) {
7232                 return;
7233         }
7234
7235         MusicFrame pos (playhead_cursor->current_frame (), 0);
7236
7237         if (pos.frame < max_framepos - 1) {
7238                 pos.frame += 2;
7239                 snap_to_internal (pos, RoundUpAlways, false);
7240                 _session->request_locate (pos.frame);
7241         }
7242 }
7243
7244
7245 void
7246 Editor::playhead_backward_to_grid ()
7247 {
7248         if (!_session) {
7249                 return;
7250         }
7251
7252         MusicFrame pos  (playhead_cursor->current_frame (), 0);
7253
7254         if (pos.frame > 2) {
7255                 pos.frame -= 2;
7256                 snap_to_internal (pos, RoundDownAlways, false);
7257                 _session->request_locate (pos.frame);
7258         }
7259 }
7260
7261 void
7262 Editor::set_track_height (Height h)
7263 {
7264         TrackSelection& ts (selection->tracks);
7265
7266         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7267                 (*x)->set_height_enum (h);
7268         }
7269 }
7270
7271 void
7272 Editor::toggle_tracks_active ()
7273 {
7274         TrackSelection& ts (selection->tracks);
7275         bool first = true;
7276         bool target = false;
7277
7278         if (ts.empty()) {
7279                 return;
7280         }
7281
7282         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7283                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7284
7285                 if (rtv) {
7286                         if (first) {
7287                                 target = !rtv->_route->active();
7288                                 first = false;
7289                         }
7290                         rtv->_route->set_active (target, this);
7291                 }
7292         }
7293 }
7294
7295 void
7296 Editor::remove_tracks ()
7297 {
7298         /* this will delete GUI objects that may be the subject of an event
7299            handler in which this method is called. Defer actual deletion to the
7300            next idle callback, when all event handling is finished.
7301         */
7302         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7303 }
7304
7305 bool
7306 Editor::idle_remove_tracks ()
7307 {
7308         Session::StateProtector sp (_session);
7309         _remove_tracks ();
7310         return false; /* do not call again */
7311 }
7312
7313 void
7314 Editor::_remove_tracks ()
7315 {
7316         TrackSelection& ts (selection->tracks);
7317
7318         if (ts.empty()) {
7319                 return;
7320         }
7321
7322         vector<string> choices;
7323         string prompt;
7324         int ntracks = 0;
7325         int nbusses = 0;
7326         const char* trackstr;
7327         const char* busstr;
7328         vector<boost::shared_ptr<Route> > routes;
7329         bool special_bus = false;
7330
7331         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7332                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7333                 if (!rtv) {
7334                         continue;
7335                 }
7336                 if (rtv->is_track()) {
7337                         ntracks++;
7338                 } else {
7339                         nbusses++;
7340                 }
7341                 routes.push_back (rtv->_route);
7342
7343                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7344                         special_bus = true;
7345                 }
7346         }
7347
7348         if (special_bus && !Config->get_allow_special_bus_removal()) {
7349                 MessageDialog msg (_("That would be bad news ...."),
7350                                    false,
7351                                    Gtk::MESSAGE_INFO,
7352                                    Gtk::BUTTONS_OK);
7353                 msg.set_secondary_text (string_compose (_(
7354                                                                 "Removing the master or monitor bus is such a bad idea\n\
7355 that %1 is not going to allow it.\n\
7356 \n\
7357 If you really want to do this sort of thing\n\
7358 edit your ardour.rc file to set the\n\
7359 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7360
7361                 msg.present ();
7362                 msg.run ();
7363                 return;
7364         }
7365
7366         if (ntracks + nbusses == 0) {
7367                 return;
7368         }
7369
7370         trackstr = P_("track", "tracks", ntracks);
7371         busstr = P_("bus", "busses", nbusses);
7372
7373         if (ntracks) {
7374                 if (nbusses) {
7375                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7376                                                     "(You may also lose the playlists associated with the %2)\n\n"
7377                                                     "This action cannot be undone, and the session file will be overwritten!"),
7378                                                   ntracks, trackstr, nbusses, busstr);
7379                 } else {
7380                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7381                                                     "(You may also lose the playlists associated with the %2)\n\n"
7382                                                     "This action cannot be undone, and the session file will be overwritten!"),
7383                                                   ntracks, trackstr);
7384                 }
7385         } else if (nbusses) {
7386                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7387                                             "This action cannot be undone, and the session file will be overwritten"),
7388                                           nbusses, busstr);
7389         }
7390
7391         choices.push_back (_("No, do nothing."));
7392         if (ntracks + nbusses > 1) {
7393                 choices.push_back (_("Yes, remove them."));
7394         } else {
7395                 choices.push_back (_("Yes, remove it."));
7396         }
7397
7398         string title;
7399         if (ntracks) {
7400                 title = string_compose (_("Remove %1"), trackstr);
7401         } else {
7402                 title = string_compose (_("Remove %1"), busstr);
7403         }
7404
7405         Choice prompter (title, prompt, choices);
7406
7407         if (prompter.run () != 1) {
7408                 return;
7409         }
7410
7411         if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7412                 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7413                  * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7414                  * likely because deletion requires selection) this will call
7415                  * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7416                  * It's likewise likely that the route that has just been displayed in the
7417                  * Editor-Mixer will be next in line for deletion.
7418                  *
7419                  * So simply switch to the master-bus (if present)
7420                  */
7421                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7422                         if ((*i)->stripable ()->is_master ()) {
7423                                 set_selected_mixer_strip (*(*i));
7424                                 break;
7425                         }
7426                 }
7427         }
7428
7429         {
7430                 PresentationInfo::ChangeSuspender cs;
7431                 DisplaySuspender ds;
7432
7433                 boost::shared_ptr<RouteList> rl (new RouteList);
7434                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7435                         rl->push_back (*x);
7436                 }
7437                 _session->remove_routes (rl);
7438         }
7439         /* TrackSelection and RouteList leave scope,
7440          * destructors are called,
7441          * diskstream drops references, save_state is called (again for every track)
7442          */
7443 }
7444
7445 void
7446 Editor::do_insert_time ()
7447 {
7448         if (selection->tracks.empty()) {
7449                 return;
7450         }
7451
7452         InsertRemoveTimeDialog d (*this);
7453         int response = d.run ();
7454
7455         if (response != RESPONSE_OK) {
7456                 return;
7457         }
7458
7459         if (d.distance() == 0) {
7460                 return;
7461         }
7462
7463         insert_time (
7464                 d.position(),
7465                 d.distance(),
7466                 d.intersected_region_action (),
7467                 d.all_playlists(),
7468                 d.move_glued(),
7469                 d.move_markers(),
7470                 d.move_glued_markers(),
7471                 d.move_locked_markers(),
7472                 d.move_tempos()
7473                 );
7474 }
7475
7476 void
7477 Editor::insert_time (
7478         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7479         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7480         )
7481 {
7482
7483         if (Config->get_edit_mode() == Lock) {
7484                 return;
7485         }
7486         bool in_command = false;
7487
7488         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7489
7490         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7491
7492                 /* regions */
7493
7494                 /* don't operate on any playlist more than once, which could
7495                  * happen if "all playlists" is enabled, but there is more
7496                  * than 1 track using playlists "from" a given track.
7497                  */
7498
7499                 set<boost::shared_ptr<Playlist> > pl;
7500
7501                 if (all_playlists) {
7502                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7503                         if (rtav && rtav->track ()) {
7504                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7505                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7506                                         pl.insert (*p);
7507                                 }
7508                         }
7509                 } else {
7510                         if ((*x)->playlist ()) {
7511                                 pl.insert ((*x)->playlist ());
7512                         }
7513                 }
7514
7515                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7516
7517                         (*i)->clear_changes ();
7518                         (*i)->clear_owned_changes ();
7519
7520                         if (!in_command) {
7521                                 begin_reversible_command (_("insert time"));
7522                                 in_command = true;
7523                         }
7524
7525                         if (opt == SplitIntersected) {
7526                                 /* non musical split */
7527                                 (*i)->split (MusicFrame (pos, 0));
7528                         }
7529
7530                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7531
7532                         vector<Command*> cmds;
7533                         (*i)->rdiff (cmds);
7534                         _session->add_commands (cmds);
7535
7536                         _session->add_command (new StatefulDiffCommand (*i));
7537                 }
7538
7539                 /* automation */
7540                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7541                 if (rtav) {
7542                         if (!in_command) {
7543                                 begin_reversible_command (_("insert time"));
7544                                 in_command = true;
7545                         }
7546                         rtav->route ()->shift (pos, frames);
7547                 }
7548         }
7549
7550         /* markers */
7551         if (markers_too) {
7552                 bool moved = false;
7553                 const int32_t divisions = get_grid_music_divisions (0);
7554                 XMLNode& before (_session->locations()->get_state());
7555                 Locations::LocationList copy (_session->locations()->list());
7556
7557                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7558
7559                         Locations::LocationList::const_iterator tmp;
7560
7561                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7562                                 bool const was_locked = (*i)->locked ();
7563                                 if (locked_markers_too) {
7564                                         (*i)->unlock ();
7565                                 }
7566
7567                                 if ((*i)->start() >= pos) {
7568                                         // move end first, in case we're moving by more than the length of the range
7569                                         if (!(*i)->is_mark()) {
7570                                                 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7571                                         }
7572                                         (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7573                                         moved = true;
7574                                 }
7575
7576                                 if (was_locked) {
7577                                         (*i)->lock ();
7578                                 }
7579                         }
7580                 }
7581
7582                 if (moved) {
7583                         if (!in_command) {
7584                                 begin_reversible_command (_("insert time"));
7585                                 in_command = true;
7586                         }
7587                         XMLNode& after (_session->locations()->get_state());
7588                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7589                 }
7590         }
7591
7592         if (tempo_too) {
7593                 if (!in_command) {
7594                         begin_reversible_command (_("insert time"));
7595                         in_command = true;
7596                 }
7597                 XMLNode& before (_session->tempo_map().get_state());
7598                 _session->tempo_map().insert_time (pos, frames);
7599                 XMLNode& after (_session->tempo_map().get_state());
7600                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7601         }
7602
7603         if (in_command) {
7604                 commit_reversible_command ();
7605         }
7606 }
7607
7608 void
7609 Editor::do_remove_time ()
7610 {
7611         if (selection->tracks.empty()) {
7612                 return;
7613         }
7614
7615         InsertRemoveTimeDialog d (*this, true);
7616
7617         int response = d.run ();
7618
7619         if (response != RESPONSE_OK) {
7620                 return;
7621         }
7622
7623         framecnt_t distance = d.distance();
7624
7625         if (distance == 0) {
7626                 return;
7627         }
7628
7629         remove_time (
7630                 d.position(),
7631                 distance,
7632                 SplitIntersected,
7633                 d.move_glued(),
7634                 d.move_markers(),
7635                 d.move_glued_markers(),
7636                 d.move_locked_markers(),
7637                 d.move_tempos()
7638         );
7639 }
7640
7641 void
7642 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7643                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7644 {
7645         if (Config->get_edit_mode() == Lock) {
7646                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7647                 return;
7648         }
7649         bool in_command = false;
7650
7651         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7652                 /* regions */
7653                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7654
7655                 if (pl) {
7656
7657                         XMLNode &before = pl->get_state();
7658
7659                         if (!in_command) {
7660                                 begin_reversible_command (_("remove time"));
7661                                 in_command = true;
7662                         }
7663
7664                         std::list<AudioRange> rl;
7665                         AudioRange ar(pos, pos+frames, 0);
7666                         rl.push_back(ar);
7667                         pl->cut (rl);
7668                         pl->shift (pos, -frames, true, ignore_music_glue);
7669
7670                         XMLNode &after = pl->get_state();
7671
7672                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7673                 }
7674
7675                 /* automation */
7676                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7677                 if (rtav) {
7678                         if (!in_command) {
7679                                 begin_reversible_command (_("remove time"));
7680                                 in_command = true;
7681                         }
7682                         rtav->route ()->shift (pos, -frames);
7683                 }
7684         }
7685
7686         const int32_t divisions = get_grid_music_divisions (0);
7687         std::list<Location*> loc_kill_list;
7688
7689         /* markers */
7690         if (markers_too) {
7691                 bool moved = false;
7692                 XMLNode& before (_session->locations()->get_state());
7693                 Locations::LocationList copy (_session->locations()->list());
7694
7695                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7696                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7697
7698                                 bool const was_locked = (*i)->locked ();
7699                                 if (locked_markers_too) {
7700                                         (*i)->unlock ();
7701                                 }
7702
7703                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7704                                         if ((*i)->end() >= pos
7705                                         && (*i)->end() < pos+frames
7706                                         && (*i)->start() >= pos
7707                                         && (*i)->end() < pos+frames) {  // range is completely enclosed;  kill it
7708                                                 moved = true;
7709                                                 loc_kill_list.push_back(*i);
7710                                         } else {  // only start or end is included, try to do the right thing
7711                                                 // move start before moving end, to avoid trying to move the end to before the start
7712                                                 // if we're removing more time than the length of the range
7713                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7714                                                         // start is within cut
7715                                                         (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
7716                                                         moved = true;
7717                                                 } else if ((*i)->start() >= pos+frames) {
7718                                                         // start (and thus entire range) lies beyond end of cut
7719                                                         (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7720                                                         moved = true;
7721                                                 }
7722                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7723                                                         // end is inside cut
7724                                                         (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
7725                                                         moved = true;
7726                                                 } else if ((*i)->end() >= pos+frames) {
7727                                                         // end is beyond end of cut
7728                                                         (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7729                                                         moved = true;
7730                                                 }
7731
7732                                         }
7733                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7734                                         loc_kill_list.push_back(*i);
7735                                         moved = true;
7736                                 } else if ((*i)->start() >= pos) {
7737                                         (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7738                                         moved = true;
7739                                 }
7740
7741                                 if (was_locked) {
7742                                         (*i)->lock ();
7743                                 }
7744                         }
7745                 }
7746
7747                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7748                         _session->locations()->remove( *i );
7749                 }
7750
7751                 if (moved) {
7752                         if (!in_command) {
7753                                 begin_reversible_command (_("remove time"));
7754                                 in_command = true;
7755                         }
7756                         XMLNode& after (_session->locations()->get_state());
7757                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7758                 }
7759         }
7760
7761         if (tempo_too) {
7762                 XMLNode& before (_session->tempo_map().get_state());
7763
7764                 if (_session->tempo_map().remove_time (pos, frames) ) {
7765                         if (!in_command) {
7766                                 begin_reversible_command (_("remove time"));
7767                                 in_command = true;
7768                         }
7769                         XMLNode& after (_session->tempo_map().get_state());
7770                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7771                 }
7772         }
7773
7774         if (in_command) {
7775                 commit_reversible_command ();
7776         }
7777 }
7778
7779 void
7780 Editor::fit_selection ()
7781 {
7782         if (!selection->tracks.empty()) {
7783                 fit_tracks (selection->tracks);
7784         } else {
7785                 TrackViewList tvl;
7786
7787                 /* no selected tracks - use tracks with selected regions */
7788
7789                 if (!selection->regions.empty()) {
7790                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7791                                 tvl.push_back (&(*r)->get_time_axis_view ());
7792                         }
7793
7794                         if (!tvl.empty()) {
7795                                 fit_tracks (tvl);
7796                         }
7797                 } else if (internal_editing()) {
7798                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7799                            the entered track
7800                         */
7801                         if (entered_track) {
7802                                 tvl.push_back (entered_track);
7803                                 fit_tracks (tvl);
7804                         }
7805                 }
7806         }
7807
7808 }
7809
7810 void
7811 Editor::fit_tracks (TrackViewList & tracks)
7812 {
7813         if (tracks.empty()) {
7814                 return;
7815         }
7816
7817         uint32_t child_heights = 0;
7818         int visible_tracks = 0;
7819
7820         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7821
7822                 if (!(*t)->marked_for_display()) {
7823                         continue;
7824                 }
7825
7826                 child_heights += (*t)->effective_height() - (*t)->current_height();
7827                 ++visible_tracks;
7828         }
7829
7830         /* compute the per-track height from:
7831
7832            total canvas visible height -
7833                  height that will be taken by visible children of selected
7834                  tracks - height of the ruler/hscroll area
7835         */
7836         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7837         double first_y_pos = DBL_MAX;
7838
7839         if (h < TimeAxisView::preset_height (HeightSmall)) {
7840                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7841                 /* too small to be displayed */
7842                 return;
7843         }
7844
7845         undo_visual_stack.push_back (current_visual_state (true));
7846         PBD::Unwinder<bool> nsv (no_save_visual, true);
7847
7848         /* build a list of all tracks, including children */
7849
7850         TrackViewList all;
7851         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7852                 all.push_back (*i);
7853                 TimeAxisView::Children c = (*i)->get_child_list ();
7854                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7855                         all.push_back (j->get());
7856                 }
7857         }
7858
7859
7860         // find selection range.
7861         // if someone knows how to user TrackViewList::iterator for this
7862         // I'm all ears.
7863         int selected_top = -1;
7864         int selected_bottom = -1;
7865         int i = 0;
7866         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7867                 if ((*t)->marked_for_display ()) {
7868                         if (tracks.contains(*t)) {
7869                                 if (selected_top == -1) {
7870                                         selected_top = i;
7871                                 }
7872                                 selected_bottom = i;
7873                         }
7874                 }
7875         }
7876
7877         i = 0;
7878         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7879                 if ((*t)->marked_for_display ()) {
7880                         if (tracks.contains(*t)) {
7881                                 (*t)->set_height (h);
7882                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7883                         } else {
7884                                 if (i > selected_top && i < selected_bottom) {
7885                                         hide_track_in_display (*t);
7886                                 }
7887                         }
7888                 }
7889         }
7890
7891         /*
7892            set the controls_layout height now, because waiting for its size
7893            request signal handler will cause the vertical adjustment setting to fail
7894         */
7895
7896         controls_layout.property_height () = _full_canvas_height;
7897         vertical_adjustment.set_value (first_y_pos);
7898
7899         redo_visual_stack.push_back (current_visual_state (true));
7900
7901         visible_tracks_selector.set_text (_("Sel"));
7902 }
7903
7904 void
7905 Editor::save_visual_state (uint32_t n)
7906 {
7907         while (visual_states.size() <= n) {
7908                 visual_states.push_back (0);
7909         }
7910
7911         if (visual_states[n] != 0) {
7912                 delete visual_states[n];
7913         }
7914
7915         visual_states[n] = current_visual_state (true);
7916         gdk_beep ();
7917 }
7918
7919 void
7920 Editor::goto_visual_state (uint32_t n)
7921 {
7922         if (visual_states.size() <= n) {
7923                 return;
7924         }
7925
7926         if (visual_states[n] == 0) {
7927                 return;
7928         }
7929
7930         use_visual_state (*visual_states[n]);
7931 }
7932
7933 void
7934 Editor::start_visual_state_op (uint32_t n)
7935 {
7936         save_visual_state (n);
7937
7938         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7939         char buf[32];
7940         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7941         pup->set_text (buf);
7942         pup->touch();
7943 }
7944
7945 void
7946 Editor::cancel_visual_state_op (uint32_t n)
7947 {
7948         goto_visual_state (n);
7949 }
7950
7951 void
7952 Editor::toggle_region_mute ()
7953 {
7954         if (_ignore_region_action) {
7955                 return;
7956         }
7957
7958         RegionSelection rs = get_regions_from_selection_and_entered ();
7959
7960         if (rs.empty ()) {
7961                 return;
7962         }
7963
7964         if (rs.size() > 1) {
7965                 begin_reversible_command (_("mute regions"));
7966         } else {
7967                 begin_reversible_command (_("mute region"));
7968         }
7969
7970         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7971
7972                 (*i)->region()->playlist()->clear_changes ();
7973                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7974                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7975
7976         }
7977
7978         commit_reversible_command ();
7979 }
7980
7981 void
7982 Editor::combine_regions ()
7983 {
7984         /* foreach track with selected regions, take all selected regions
7985            and join them into a new region containing the subregions (as a
7986            playlist)
7987         */
7988
7989         typedef set<RouteTimeAxisView*> RTVS;
7990         RTVS tracks;
7991
7992         if (selection->regions.empty()) {
7993                 return;
7994         }
7995
7996         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7997                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7998
7999                 if (rtv) {
8000                         tracks.insert (rtv);
8001                 }
8002         }
8003
8004         begin_reversible_command (_("combine regions"));
8005
8006         vector<RegionView*> new_selection;
8007
8008         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8009                 RegionView* rv;
8010
8011                 if ((rv = (*i)->combine_regions ()) != 0) {
8012                         new_selection.push_back (rv);
8013                 }
8014         }
8015
8016         selection->clear_regions ();
8017         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8018                 selection->add (*i);
8019         }
8020
8021         commit_reversible_command ();
8022 }
8023
8024 void
8025 Editor::uncombine_regions ()
8026 {
8027         typedef set<RouteTimeAxisView*> RTVS;
8028         RTVS tracks;
8029
8030         if (selection->regions.empty()) {
8031                 return;
8032         }
8033
8034         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8035                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8036
8037                 if (rtv) {
8038                         tracks.insert (rtv);
8039                 }
8040         }
8041
8042         begin_reversible_command (_("uncombine regions"));
8043
8044         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8045                 (*i)->uncombine_regions ();
8046         }
8047
8048         commit_reversible_command ();
8049 }
8050
8051 void
8052 Editor::toggle_midi_input_active (bool flip_others)
8053 {
8054         bool onoff = false;
8055         boost::shared_ptr<RouteList> rl (new RouteList);
8056
8057         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8058                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8059
8060                 if (!rtav) {
8061                         continue;
8062                 }
8063
8064                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8065
8066                 if (mt) {
8067                         rl->push_back (rtav->route());
8068                         onoff = !mt->input_active();
8069                 }
8070         }
8071
8072         _session->set_exclusive_input_active (rl, onoff, flip_others);
8073 }
8074
8075 static bool ok_fine (GdkEventAny*) { return true; }
8076
8077 void
8078 Editor::lock ()
8079 {
8080         if (!lock_dialog) {
8081                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8082
8083                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8084                 lock_dialog->get_vbox()->pack_start (*padlock);
8085                 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8086
8087                 ArdourButton* b = manage (new ArdourButton);
8088                 b->set_name ("lock button");
8089                 b->set_text (_("Click to unlock"));
8090                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8091                 lock_dialog->get_vbox()->pack_start (*b);
8092
8093                 lock_dialog->get_vbox()->show_all ();
8094                 lock_dialog->set_size_request (200, 200);
8095         }
8096
8097         delete _main_menu_disabler;
8098         _main_menu_disabler = new MainMenuDisabler;
8099
8100         lock_dialog->present ();
8101
8102         lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8103 }
8104
8105 void
8106 Editor::unlock ()
8107 {
8108         lock_dialog->hide ();
8109
8110         delete _main_menu_disabler;
8111         _main_menu_disabler = 0;
8112
8113         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8114                 start_lock_event_timing ();
8115         }
8116 }
8117
8118 void
8119 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8120 {
8121         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8122 }
8123
8124 void
8125 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8126 {
8127         Timers::TimerSuspender t;
8128         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8129         Gtkmm2ext::UI::instance()->flush_pending (1);
8130 }
8131
8132 void
8133 Editor::bring_all_sources_into_session ()
8134 {
8135         if (!_session) {
8136                 return;
8137         }
8138
8139         Gtk::Label msg;
8140         ArdourDialog w (_("Moving embedded files into session folder"));
8141         w.get_vbox()->pack_start (msg);
8142         w.present ();
8143
8144         /* flush all pending GUI events because we're about to start copying
8145          * files
8146          */
8147
8148         Timers::TimerSuspender t;
8149         Gtkmm2ext::UI::instance()->flush_pending (3);
8150
8151         cerr << " Do it\n";
8152
8153         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8154 }