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