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