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