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