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