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