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