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