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