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