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