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