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