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