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