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