fix background of ArdourButtons with no Body
[ardour.git] / gtk2_ardour / editor_ops.cc
1 /*
2     Copyright (C) 2000-2004 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /* Note: public Editor methods are documented in public_editor.h */
21
22 #include <unistd.h>
23
24 #include <cstdlib>
25 #include <cmath>
26 #include <string>
27 #include <limits>
28 #include <map>
29 #include <set>
30
31 #include "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
38
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
42
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
62
63 #include "canvas/canvas.h"
64
65 #include "actions.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
72 #include "debug.h"
73 #include "editing.h"
74 #include "editor.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
83 #include "keyboard.h"
84 #include "midi_region_view.h"
85 #include "mixer_strip.h"
86 #include "mouse_cursors.h"
87 #include "normalize_dialog.h"
88 #include "note.h"
89 #include "paste_context.h"
90 #include "patch_change_dialog.h"
91 #include "quantize_dialog.h"
92 #include "region_gain_line.h"
93 #include "rgb_macros.h"
94 #include "route_time_axis.h"
95 #include "selection.h"
96 #include "selection_templates.h"
97 #include "streamview.h"
98 #include "strip_silence_dialog.h"
99 #include "time_axis_view.h"
100 #include "timers.h"
101 #include "transpose_dialog.h"
102 #include "transform_dialog.h"
103 #include "ui_config.h"
104
105 #include "i18n.h"
106
107 using namespace std;
108 using namespace ARDOUR;
109 using namespace PBD;
110 using namespace Gtk;
111 using namespace Gtkmm2ext;
112 using namespace Editing;
113 using Gtkmm2ext::Keyboard;
114
115 /***********************************************************************
116   Editor operations
117  ***********************************************************************/
118
119 void
120 Editor::undo (uint32_t n)
121 {
122         if (_drags->active ()) {
123                 _drags->abort ();
124         }
125
126         if (_session) {
127                 _session->undo (n);
128                 if (_session->undo_depth() == 0) {
129                         undo_action->set_sensitive(false);
130                 }
131                 redo_action->set_sensitive(true);
132                 begin_selection_op_history ();
133         }
134 }
135
136 void
137 Editor::redo (uint32_t n)
138 {
139         if (_drags->active ()) {
140                 _drags->abort ();
141         }
142
143         if (_session) {
144                 _session->redo (n);
145                 if (_session->redo_depth() == 0) {
146                         redo_action->set_sensitive(false);
147                 }
148                 undo_action->set_sensitive(true);
149                 begin_selection_op_history ();
150         }
151 }
152
153 void
154 Editor::split_regions_at (framepos_t where, RegionSelection& regions)
155 {
156         bool frozen = false;
157
158         RegionSelection pre_selected_regions = selection->regions;
159         bool working_on_selection = !pre_selected_regions.empty();
160
161         list<boost::shared_ptr<Playlist> > used_playlists;
162         list<RouteTimeAxisView*> used_trackviews;
163
164         if (regions.empty()) {
165                 return;
166         }
167
168         begin_reversible_command (_("split"));
169
170         // if splitting a single region, and snap-to is using
171         // region boundaries, don't pay attention to them
172
173         if (regions.size() == 1) {
174                 switch (_snap_type) {
175                 case SnapToRegionStart:
176                 case SnapToRegionSync:
177                 case SnapToRegionEnd:
178                         break;
179                 default:
180                         snap_to (where);
181                 }
182         } else {
183                 snap_to (where);
184
185                 frozen = true;
186                 EditorFreeze(); /* Emit Signal */
187         }
188
189         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
190
191                 RegionSelection::iterator tmp;
192
193                 /* XXX this test needs to be more complicated, to make sure we really
194                    have something to split.
195                 */
196
197                 if (!(*a)->region()->covers (where)) {
198                         ++a;
199                         continue;
200                 }
201
202                 tmp = a;
203                 ++tmp;
204
205                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
206
207                 if (!pl) {
208                         a = tmp;
209                         continue;
210                 }
211
212                 if (!pl->frozen()) {
213                         /* we haven't seen this playlist before */
214
215                         /* remember used playlists so we can thaw them later */
216                         used_playlists.push_back(pl);
217
218                         TimeAxisView& tv = (*a)->get_time_axis_view();
219                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
220                         if (rtv) {
221                                 used_trackviews.push_back (rtv);
222                         }
223                         pl->freeze();
224                 }
225
226
227                 if (pl) {
228                         pl->clear_changes ();
229                         pl->split_region ((*a)->region(), where);
230                         _session->add_command (new StatefulDiffCommand (pl));
231                 }
232
233                 a = tmp;
234         }
235
236         latest_regionviews.clear ();
237
238         vector<sigc::connection> region_added_connections;
239
240         for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
241                 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
242         }
243
244         while (used_playlists.size() > 0) {
245                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
246                 (*i)->thaw();
247                 used_playlists.pop_front();
248         }
249
250         for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
251                 (*c).disconnect ();
252         }
253
254         if (frozen){
255                 EditorThaw(); /* Emit Signal */
256         }
257
258         if (working_on_selection) {
259                 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
260
261                 _ignore_follow_edits = true;  // a split will change the region selection in mysterious ways;  it's not practical or wanted to follow this edit
262                 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
263                 /* There are three classes of regions that we might want selected after
264                    splitting selected regions:
265                     - regions selected before the split operation, and unaffected by it
266                     - newly-created regions before the split
267                     - newly-created regions after the split
268                  */
269
270                 if (rsas & Existing) {
271                         // region selections that existed before the split.
272                         selection->add ( pre_selected_regions );
273                 }
274
275                 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
276                         if ((*ri)->region()->position() < where) {
277                                 // new regions created before the split
278                                 if (rsas & NewlyCreatedLeft) {
279                                         selection->add (*ri);
280                                 }
281                         } else {
282                                 // new regions created after the split
283                                 if (rsas & NewlyCreatedRight) {
284                                         selection->add (*ri);
285                                 }
286                         }
287                 }
288                 _ignore_follow_edits = false;
289         } else {
290                 _ignore_follow_edits = true;
291                 if( working_on_selection ) {
292                         selection->add (latest_regionviews);  //these are the new regions created after the split
293                 }
294                 _ignore_follow_edits = false;
295         }
296
297         commit_reversible_command ();
298 }
299
300 /** Move one extreme of the current range selection.  If more than one range is selected,
301  *  the start of the earliest range or the end of the latest range is moved.
302  *
303  *  @param move_end true to move the end of the current range selection, false to move
304  *  the start.
305  *  @param next true to move the extreme to the next region boundary, false to move to
306  *  the previous.
307  */
308 void
309 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
310 {
311         if (selection->time.start() == selection->time.end_frame()) {
312                 return;
313         }
314
315         framepos_t start = selection->time.start ();
316         framepos_t end = selection->time.end_frame ();
317
318         /* the position of the thing we may move */
319         framepos_t pos = move_end ? end : start;
320         int dir = next ? 1 : -1;
321
322         /* so we don't find the current region again */
323         if (dir > 0 || pos > 0) {
324                 pos += dir;
325         }
326
327         framepos_t const target = get_region_boundary (pos, dir, true, false);
328         if (target < 0) {
329                 return;
330         }
331
332         if (move_end) {
333                 end = target;
334         } else {
335                 start = target;
336         }
337
338         if (end < start) {
339                 return;
340         }
341
342         begin_reversible_selection_op (_("alter selection"));
343         selection->set_preserving_all_ranges (start, end);
344         commit_reversible_selection_op ();
345 }
346
347 bool
348 Editor::nudge_forward_release (GdkEventButton* ev)
349 {
350         if (ev->state & Keyboard::PrimaryModifier) {
351                 nudge_forward (false, true);
352         } else {
353                 nudge_forward (false, false);
354         }
355         return false;
356 }
357
358 bool
359 Editor::nudge_backward_release (GdkEventButton* ev)
360 {
361         if (ev->state & Keyboard::PrimaryModifier) {
362                 nudge_backward (false, true);
363         } else {
364                 nudge_backward (false, false);
365         }
366         return false;
367 }
368
369
370 void
371 Editor::nudge_forward (bool next, bool force_playhead)
372 {
373         framepos_t distance;
374         framepos_t next_distance;
375
376         if (!_session) {
377                 return;
378         }
379
380         RegionSelection rs = get_regions_from_selection_and_entered ();
381
382         if (!force_playhead && !rs.empty()) {
383
384                 begin_reversible_command (_("nudge regions forward"));
385
386                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
387                         boost::shared_ptr<Region> r ((*i)->region());
388
389                         distance = get_nudge_distance (r->position(), next_distance);
390
391                         if (next) {
392                                 distance = next_distance;
393                         }
394
395                         r->clear_changes ();
396                         r->set_position (r->position() + distance);
397                         _session->add_command (new StatefulDiffCommand (r));
398                 }
399
400                 commit_reversible_command ();
401
402
403         } else if (!force_playhead && !selection->markers.empty()) {
404
405                 bool is_start;
406                 bool in_command = false;
407
408                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
409
410                         Location* loc = find_location_from_marker ((*i), is_start);
411
412                         if (loc) {
413
414                                 XMLNode& before (loc->get_state());
415
416                                 if (is_start) {
417                                         distance = get_nudge_distance (loc->start(), next_distance);
418                                         if (next) {
419                                                 distance = next_distance;
420                                         }
421                                         if (max_framepos - distance > loc->start() + loc->length()) {
422                                                 loc->set_start (loc->start() + distance);
423                                         } else {
424                                                 loc->set_start (max_framepos - loc->length());
425                                         }
426                                 } else {
427                                         distance = get_nudge_distance (loc->end(), next_distance);
428                                         if (next) {
429                                                 distance = next_distance;
430                                         }
431                                         if (max_framepos - distance > loc->end()) {
432                                                 loc->set_end (loc->end() + distance);
433                                         } else {
434                                                 loc->set_end (max_framepos);
435                                         }
436                                 }
437                                 if (!in_command) {
438                                         begin_reversible_command (_("nudge location forward"));
439                                         in_command = true;
440                                 }
441                                 XMLNode& after (loc->get_state());
442                                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
443                         }
444                 }
445
446                 if (in_command) {
447                         commit_reversible_command ();
448                 }
449         } else {
450                 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
451                 _session->request_locate (playhead_cursor->current_frame () + distance);
452         }
453 }
454
455 void
456 Editor::nudge_backward (bool next, bool force_playhead)
457 {
458         framepos_t distance;
459         framepos_t next_distance;
460
461         if (!_session) {
462                 return;
463         }
464
465         RegionSelection rs = get_regions_from_selection_and_entered ();
466
467         if (!force_playhead && !rs.empty()) {
468
469                 begin_reversible_command (_("nudge regions backward"));
470
471                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
472                         boost::shared_ptr<Region> r ((*i)->region());
473
474                         distance = get_nudge_distance (r->position(), next_distance);
475
476                         if (next) {
477                                 distance = next_distance;
478                         }
479
480                         r->clear_changes ();
481
482                         if (r->position() > distance) {
483                                 r->set_position (r->position() - distance);
484                         } else {
485                                 r->set_position (0);
486                         }
487                         _session->add_command (new StatefulDiffCommand (r));
488                 }
489
490                 commit_reversible_command ();
491
492         } else if (!force_playhead && !selection->markers.empty()) {
493
494                 bool is_start;
495                 bool in_command = false;
496
497                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
498
499                         Location* loc = find_location_from_marker ((*i), is_start);
500
501                         if (loc) {
502
503                                 XMLNode& before (loc->get_state());
504
505                                 if (is_start) {
506                                         distance = get_nudge_distance (loc->start(), next_distance);
507                                         if (next) {
508                                                 distance = next_distance;
509                                         }
510                                         if (distance < loc->start()) {
511                                                 loc->set_start (loc->start() - distance);
512                                         } else {
513                                                 loc->set_start (0);
514                                         }
515                                 } else {
516                                         distance = get_nudge_distance (loc->end(), next_distance);
517
518                                         if (next) {
519                                                 distance = next_distance;
520                                         }
521
522                                         if (distance < loc->end() - loc->length()) {
523                                                 loc->set_end (loc->end() - distance);
524                                         } else {
525                                                 loc->set_end (loc->length());
526                                         }
527                                 }
528                                 if (!in_command) {
529                                         begin_reversible_command (_("nudge location forward"));
530                                         in_command = true;
531                                 }
532                                 XMLNode& after (loc->get_state());
533                                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
534                         }
535                 }
536                 if (in_command) {
537                         commit_reversible_command ();
538                 }
539
540         } else {
541
542                 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
543
544                 if (playhead_cursor->current_frame () > distance) {
545                         _session->request_locate (playhead_cursor->current_frame () - distance);
546                 } else {
547                         _session->goto_start();
548                 }
549         }
550 }
551
552 void
553 Editor::nudge_forward_capture_offset ()
554 {
555         RegionSelection rs = get_regions_from_selection_and_entered ();
556
557         if (!_session || rs.empty()) {
558                 return;
559         }
560
561         begin_reversible_command (_("nudge forward"));
562
563         framepos_t const distance = _session->worst_output_latency();
564
565         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
566                 boost::shared_ptr<Region> r ((*i)->region());
567
568                 r->clear_changes ();
569                 r->set_position (r->position() + distance);
570                 _session->add_command(new StatefulDiffCommand (r));
571         }
572
573         commit_reversible_command ();
574 }
575
576 void
577 Editor::nudge_backward_capture_offset ()
578 {
579         RegionSelection rs = get_regions_from_selection_and_entered ();
580
581         if (!_session || rs.empty()) {
582                 return;
583         }
584
585         begin_reversible_command (_("nudge backward"));
586
587         framepos_t const distance = _session->worst_output_latency();
588
589         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
590                 boost::shared_ptr<Region> r ((*i)->region());
591
592                 r->clear_changes ();
593
594                 if (r->position() > distance) {
595                         r->set_position (r->position() - distance);
596                 } else {
597                         r->set_position (0);
598                 }
599                 _session->add_command(new StatefulDiffCommand (r));
600         }
601
602         commit_reversible_command ();
603 }
604
605 struct RegionSelectionPositionSorter {
606         bool operator() (RegionView* a, RegionView* b) {
607                 return a->region()->position() < b->region()->position();
608         }
609 };
610
611 void
612 Editor::sequence_regions ()
613 {
614         framepos_t r_end;
615         framepos_t r_end_prev;
616
617         int iCount=0;
618
619         if (!_session) {
620                 return;
621         }
622
623         RegionSelection rs = get_regions_from_selection_and_entered ();
624         rs.sort(RegionSelectionPositionSorter());
625
626         if (!rs.empty()) {
627
628                 bool in_command = false;
629
630                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
631                         boost::shared_ptr<Region> r ((*i)->region());
632
633                         r->clear_changes();
634
635                         if(r->locked())
636                         {
637                                 continue;
638                         }
639                         if(r->position_locked())
640                         {
641                                 continue;
642                         }
643                         if(iCount>0)
644                         {
645                                 r_end_prev=r_end;
646                                 r->set_position(r_end_prev);
647                         }
648
649                         if (!in_command) {
650                                 begin_reversible_command (_("sequence regions"));
651                                 in_command = true;
652                         }
653                         _session->add_command (new StatefulDiffCommand (r));
654
655                         r_end=r->position() + r->length();
656
657                         iCount++;
658                 }
659
660                 if (in_command) {
661                         commit_reversible_command ();
662                 }
663         }
664 }
665
666
667 /* DISPLAY MOTION */
668
669 void
670 Editor::move_to_start ()
671 {
672         _session->goto_start ();
673 }
674
675 void
676 Editor::move_to_end ()
677 {
678
679         _session->request_locate (_session->current_end_frame());
680 }
681
682 void
683 Editor::build_region_boundary_cache ()
684 {
685         framepos_t pos = 0;
686         vector<RegionPoint> interesting_points;
687         boost::shared_ptr<Region> r;
688         TrackViewList tracks;
689         bool at_end = false;
690
691         region_boundary_cache.clear ();
692
693         if (_session == 0) {
694                 return;
695         }
696
697         switch (_snap_type) {
698         case SnapToRegionStart:
699                 interesting_points.push_back (Start);
700                 break;
701         case SnapToRegionEnd:
702                 interesting_points.push_back (End);
703                 break;
704         case SnapToRegionSync:
705                 interesting_points.push_back (SyncPoint);
706                 break;
707         case SnapToRegionBoundary:
708                 interesting_points.push_back (Start);
709                 interesting_points.push_back (End);
710                 break;
711         default:
712                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
713                 abort(); /*NOTREACHED*/
714                 return;
715         }
716
717         TimeAxisView *ontrack = 0;
718         TrackViewList tlist;
719
720         if (!selection->tracks.empty()) {
721                 tlist = selection->tracks.filter_to_unique_playlists ();
722         } else {
723                 tlist = track_views.filter_to_unique_playlists ();
724         }
725
726         while (pos < _session->current_end_frame() && !at_end) {
727
728                 framepos_t rpos;
729                 framepos_t lpos = max_framepos;
730
731                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
732
733                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
734                                 if (*p == interesting_points.back()) {
735                                         at_end = true;
736                                 }
737                                 /* move to next point type */
738                                 continue;
739                         }
740
741                         switch (*p) {
742                         case Start:
743                                 rpos = r->first_frame();
744                                 break;
745
746                         case End:
747                                 rpos = r->last_frame();
748                                 break;
749
750                         case SyncPoint:
751                                 rpos = r->sync_position ();
752                                 break;
753
754                         default:
755                                 break;
756                         }
757
758                         float speed = 1.0f;
759                         RouteTimeAxisView *rtav;
760
761                         if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
762                                 if (rtav->track() != 0) {
763                                         speed = rtav->track()->speed();
764                                 }
765                         }
766
767                         rpos = track_frame_to_session_frame (rpos, speed);
768
769                         if (rpos < lpos) {
770                                 lpos = rpos;
771                         }
772
773                         /* prevent duplicates, but we don't use set<> because we want to be able
774                            to sort later.
775                         */
776
777                         vector<framepos_t>::iterator ri;
778
779                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
780                                 if (*ri == rpos) {
781                                         break;
782                                 }
783                         }
784
785                         if (ri == region_boundary_cache.end()) {
786                                 region_boundary_cache.push_back (rpos);
787                         }
788                 }
789
790                 pos = lpos + 1;
791         }
792
793         /* finally sort to be sure that the order is correct */
794
795         sort (region_boundary_cache.begin(), region_boundary_cache.end());
796 }
797
798 boost::shared_ptr<Region>
799 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
800 {
801         TrackViewList::iterator i;
802         framepos_t closest = max_framepos;
803         boost::shared_ptr<Region> ret;
804         framepos_t rpos = 0;
805
806         float track_speed;
807         framepos_t track_frame;
808         RouteTimeAxisView *rtav;
809
810         for (i = tracks.begin(); i != tracks.end(); ++i) {
811
812                 framecnt_t distance;
813                 boost::shared_ptr<Region> r;
814
815                 track_speed = 1.0f;
816                 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
817                         if (rtav->track()!=0)
818                                 track_speed = rtav->track()->speed();
819                 }
820
821                 track_frame = session_frame_to_track_frame(frame, track_speed);
822
823                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
824                         continue;
825                 }
826
827                 switch (point) {
828                 case Start:
829                         rpos = r->first_frame ();
830                         break;
831
832                 case End:
833                         rpos = r->last_frame ();
834                         break;
835
836                 case SyncPoint:
837                         rpos = r->sync_position ();
838                         break;
839                 }
840
841                 // rpos is a "track frame", converting it to "_session frame"
842                 rpos = track_frame_to_session_frame(rpos, track_speed);
843
844                 if (rpos > frame) {
845                         distance = rpos - frame;
846                 } else {
847                         distance = frame - rpos;
848                 }
849
850                 if (distance < closest) {
851                         closest = distance;
852                         if (ontrack != 0)
853                                 *ontrack = (*i);
854                         ret = r;
855                 }
856         }
857
858         return ret;
859 }
860
861 framepos_t
862 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
863 {
864         framecnt_t distance = max_framepos;
865         framepos_t current_nearest = -1;
866
867         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
868                 framepos_t contender;
869                 framecnt_t d;
870
871                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
872
873                 if (!rtv) {
874                         continue;
875                 }
876
877                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
878                         continue;
879                 }
880
881                 d = ::llabs (pos - contender);
882
883                 if (d < distance) {
884                         current_nearest = contender;
885                         distance = d;
886                 }
887         }
888
889         return current_nearest;
890 }
891
892 framepos_t
893 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
894 {
895         framepos_t target;
896         TrackViewList tvl;
897
898         if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
899
900                 if (!selection->tracks.empty()) {
901
902                         target = find_next_region_boundary (pos, dir, selection->tracks);
903
904                 } else {
905
906                         if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
907                                 get_onscreen_tracks (tvl);
908                                 target = find_next_region_boundary (pos, dir, tvl);
909                         } else {
910                                 target = find_next_region_boundary (pos, dir, track_views);
911                         }
912                 }
913
914         } else {
915
916                 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
917                         get_onscreen_tracks (tvl);
918                         target = find_next_region_boundary (pos, dir, tvl);
919                 } else {
920                         target = find_next_region_boundary (pos, dir, track_views);
921                 }
922         }
923
924         return target;
925 }
926
927 void
928 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
929 {
930         framepos_t pos = playhead_cursor->current_frame ();
931         framepos_t target;
932
933         if (!_session) {
934                 return;
935         }
936
937         // so we don't find the current region again..
938         if (dir > 0 || pos > 0) {
939                 pos += dir;
940         }
941
942         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
943                 return;
944         }
945
946         _session->request_locate (target);
947 }
948
949 void
950 Editor::cursor_to_next_region_boundary (bool with_selection)
951 {
952         cursor_to_region_boundary (with_selection, 1);
953 }
954
955 void
956 Editor::cursor_to_previous_region_boundary (bool with_selection)
957 {
958         cursor_to_region_boundary (with_selection, -1);
959 }
960
961 void
962 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
963 {
964         boost::shared_ptr<Region> r;
965         framepos_t pos = cursor->current_frame ();
966
967         if (!_session) {
968                 return;
969         }
970
971         TimeAxisView *ontrack = 0;
972
973         // so we don't find the current region again..
974         if (dir>0 || pos>0)
975                 pos+=dir;
976
977         if (!selection->tracks.empty()) {
978
979                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
980
981         } else if (clicked_axisview) {
982
983                 TrackViewList t;
984                 t.push_back (clicked_axisview);
985
986                 r = find_next_region (pos, point, dir, t, &ontrack);
987
988         } else {
989
990                 r = find_next_region (pos, point, dir, track_views, &ontrack);
991         }
992
993         if (r == 0) {
994                 return;
995         }
996
997         switch (point) {
998         case Start:
999                 pos = r->first_frame ();
1000                 break;
1001
1002         case End:
1003                 pos = r->last_frame ();
1004                 break;
1005
1006         case SyncPoint:
1007                 pos = r->sync_position ();
1008                 break;
1009         }
1010
1011         float speed = 1.0f;
1012         RouteTimeAxisView *rtav;
1013
1014         if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1015                 if (rtav->track() != 0) {
1016                         speed = rtav->track()->speed();
1017                 }
1018         }
1019
1020         pos = track_frame_to_session_frame(pos, speed);
1021
1022         if (cursor == playhead_cursor) {
1023                 _session->request_locate (pos);
1024         } else {
1025                 cursor->set_position (pos);
1026         }
1027 }
1028
1029 void
1030 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1031 {
1032         cursor_to_region_point (cursor, point, 1);
1033 }
1034
1035 void
1036 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1037 {
1038         cursor_to_region_point (cursor, point, -1);
1039 }
1040
1041 void
1042 Editor::cursor_to_selection_start (EditorCursor *cursor)
1043 {
1044         framepos_t pos = 0;
1045
1046         switch (mouse_mode) {
1047         case MouseObject:
1048                 if (!selection->regions.empty()) {
1049                         pos = selection->regions.start();
1050                 }
1051                 break;
1052
1053         case MouseRange:
1054                 if (!selection->time.empty()) {
1055                         pos = selection->time.start ();
1056                 }
1057                 break;
1058
1059         default:
1060                 return;
1061         }
1062
1063         if (cursor == playhead_cursor) {
1064                 _session->request_locate (pos);
1065         } else {
1066                 cursor->set_position (pos);
1067         }
1068 }
1069
1070 void
1071 Editor::cursor_to_selection_end (EditorCursor *cursor)
1072 {
1073         framepos_t pos = 0;
1074
1075         switch (mouse_mode) {
1076         case MouseObject:
1077                 if (!selection->regions.empty()) {
1078                         pos = selection->regions.end_frame();
1079                 }
1080                 break;
1081
1082         case MouseRange:
1083                 if (!selection->time.empty()) {
1084                         pos = selection->time.end_frame ();
1085                 }
1086                 break;
1087
1088         default:
1089                 return;
1090         }
1091
1092         if (cursor == playhead_cursor) {
1093                 _session->request_locate (pos);
1094         } else {
1095                 cursor->set_position (pos);
1096         }
1097 }
1098
1099 void
1100 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1101 {
1102         framepos_t target;
1103         Location* loc;
1104         bool ignored;
1105
1106         if (!_session) {
1107                 return;
1108         }
1109
1110         if (selection->markers.empty()) {
1111                 framepos_t mouse;
1112                 bool ignored;
1113
1114                 if (!mouse_frame (mouse, ignored)) {
1115                         return;
1116                 }
1117
1118                 add_location_mark (mouse);
1119         }
1120
1121         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1122                 return;
1123         }
1124
1125         framepos_t pos = loc->start();
1126
1127         // so we don't find the current region again..
1128         if (dir > 0 || pos > 0) {
1129                 pos += dir;
1130         }
1131
1132         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1133                 return;
1134         }
1135
1136         loc->move_to (target);
1137 }
1138
1139 void
1140 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1141 {
1142         selected_marker_to_region_boundary (with_selection, 1);
1143 }
1144
1145 void
1146 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1147 {
1148         selected_marker_to_region_boundary (with_selection, -1);
1149 }
1150
1151 void
1152 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1153 {
1154         boost::shared_ptr<Region> r;
1155         framepos_t pos;
1156         Location* loc;
1157         bool ignored;
1158
1159         if (!_session || selection->markers.empty()) {
1160                 return;
1161         }
1162
1163         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1164                 return;
1165         }
1166
1167         TimeAxisView *ontrack = 0;
1168
1169         pos = loc->start();
1170
1171         // so we don't find the current region again..
1172         if (dir>0 || pos>0)
1173                 pos+=dir;
1174
1175         if (!selection->tracks.empty()) {
1176
1177                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1178
1179         } else {
1180
1181                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1182         }
1183
1184         if (r == 0) {
1185                 return;
1186         }
1187
1188         switch (point) {
1189         case Start:
1190                 pos = r->first_frame ();
1191                 break;
1192
1193         case End:
1194                 pos = r->last_frame ();
1195                 break;
1196
1197         case SyncPoint:
1198                 pos = r->adjust_to_sync (r->first_frame());
1199                 break;
1200         }
1201
1202         float speed = 1.0f;
1203         RouteTimeAxisView *rtav;
1204
1205         if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1206                 if (rtav->track() != 0) {
1207                         speed = rtav->track()->speed();
1208                 }
1209         }
1210
1211         pos = track_frame_to_session_frame(pos, speed);
1212
1213         loc->move_to (pos);
1214 }
1215
1216 void
1217 Editor::selected_marker_to_next_region_point (RegionPoint point)
1218 {
1219         selected_marker_to_region_point (point, 1);
1220 }
1221
1222 void
1223 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1224 {
1225         selected_marker_to_region_point (point, -1);
1226 }
1227
1228 void
1229 Editor::selected_marker_to_selection_start ()
1230 {
1231         framepos_t pos = 0;
1232         Location* loc;
1233         bool ignored;
1234
1235         if (!_session || selection->markers.empty()) {
1236                 return;
1237         }
1238
1239         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1240                 return;
1241         }
1242
1243         switch (mouse_mode) {
1244         case MouseObject:
1245                 if (!selection->regions.empty()) {
1246                         pos = selection->regions.start();
1247                 }
1248                 break;
1249
1250         case MouseRange:
1251                 if (!selection->time.empty()) {
1252                         pos = selection->time.start ();
1253                 }
1254                 break;
1255
1256         default:
1257                 return;
1258         }
1259
1260         loc->move_to (pos);
1261 }
1262
1263 void
1264 Editor::selected_marker_to_selection_end ()
1265 {
1266         framepos_t pos = 0;
1267         Location* loc;
1268         bool ignored;
1269
1270         if (!_session || selection->markers.empty()) {
1271                 return;
1272         }
1273
1274         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1275                 return;
1276         }
1277
1278         switch (mouse_mode) {
1279         case MouseObject:
1280                 if (!selection->regions.empty()) {
1281                         pos = selection->regions.end_frame();
1282                 }
1283                 break;
1284
1285         case MouseRange:
1286                 if (!selection->time.empty()) {
1287                         pos = selection->time.end_frame ();
1288                 }
1289                 break;
1290
1291         default:
1292                 return;
1293         }
1294
1295         loc->move_to (pos);
1296 }
1297
1298 void
1299 Editor::scroll_playhead (bool forward)
1300 {
1301         framepos_t pos = playhead_cursor->current_frame ();
1302         framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1303
1304         if (forward) {
1305                 if (pos == max_framepos) {
1306                         return;
1307                 }
1308
1309                 if (pos < max_framepos - delta) {
1310                         pos += delta ;
1311                 } else {
1312                         pos = max_framepos;
1313                 }
1314
1315         } else {
1316
1317                 if (pos == 0) {
1318                         return;
1319                 }
1320
1321                 if (pos > delta) {
1322                         pos -= delta;
1323                 } else {
1324                         pos = 0;
1325                 }
1326         }
1327
1328         _session->request_locate (pos);
1329 }
1330
1331 void
1332 Editor::cursor_align (bool playhead_to_edit)
1333 {
1334         if (!_session) {
1335                 return;
1336         }
1337
1338         if (playhead_to_edit) {
1339
1340                 if (selection->markers.empty()) {
1341                         return;
1342                 }
1343
1344                 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1345
1346         } else {
1347                 /* move selected markers to playhead */
1348
1349                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1350                         bool ignored;
1351
1352                         Location* loc = find_location_from_marker (*i, ignored);
1353
1354                         if (loc->is_mark()) {
1355                                 loc->set_start (playhead_cursor->current_frame ());
1356                         } else {
1357                                 loc->set (playhead_cursor->current_frame (),
1358                                           playhead_cursor->current_frame () + loc->length());
1359                         }
1360                 }
1361         }
1362 }
1363
1364 void
1365 Editor::scroll_backward (float pages)
1366 {
1367         framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1368         framepos_t const cnt = (framepos_t) floor (pages * one_page);
1369
1370         framepos_t frame;
1371         if (leftmost_frame < cnt) {
1372                 frame = 0;
1373         } else {
1374                 frame = leftmost_frame - cnt;
1375         }
1376
1377         reset_x_origin (frame);
1378 }
1379
1380 void
1381 Editor::scroll_forward (float pages)
1382 {
1383         framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1384         framepos_t const cnt = (framepos_t) floor (pages * one_page);
1385
1386         framepos_t frame;
1387         if (max_framepos - cnt < leftmost_frame) {
1388                 frame = max_framepos - cnt;
1389         } else {
1390                 frame = leftmost_frame + cnt;
1391         }
1392
1393         reset_x_origin (frame);
1394 }
1395
1396 void
1397 Editor::scroll_tracks_down ()
1398 {
1399         double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1400         if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1401                 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1402         }
1403
1404         vertical_adjustment.set_value (vert_value);
1405 }
1406
1407 void
1408 Editor::scroll_tracks_up ()
1409 {
1410         vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1411 }
1412
1413 void
1414 Editor::scroll_tracks_down_line ()
1415 {
1416         double vert_value = vertical_adjustment.get_value() + 60;
1417
1418         if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1419                 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1420         }
1421
1422         vertical_adjustment.set_value (vert_value);
1423 }
1424
1425 void
1426 Editor::scroll_tracks_up_line ()
1427 {
1428         reset_y_origin (vertical_adjustment.get_value() - 60);
1429 }
1430
1431 bool
1432 Editor::scroll_down_one_track (bool skip_child_views)
1433 {
1434         TrackViewList::reverse_iterator next = track_views.rend();
1435         const double top_of_trackviews = vertical_adjustment.get_value();
1436
1437         for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1438                 if ((*t)->hidden()) {
1439                         continue;
1440                 }
1441
1442                 /* If this is the upper-most visible trackview, we want to display
1443                  * the one above it (next)
1444                  *
1445                  * Note that covers_y_position() is recursive and includes child views
1446                  */
1447                 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1448
1449                 if (res.first) {
1450                         if (skip_child_views) {
1451                                 break;
1452                         }
1453                         /* automation lane (one level, non-recursive)
1454                          *
1455                          * - if no automation lane exists -> move to next tack
1456                          * - if the first (here: bottom-most) matches -> move to next tack
1457                          * - if no y-axis match is found -> the current track is at the top
1458                          *     -> move to last (here: top-most) automation lane
1459                          */
1460                         TimeAxisView::Children kids = (*t)->get_child_list();
1461                         TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1462
1463                         for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1464                                 if ((*ci)->hidden()) {
1465                                         continue;
1466                                 }
1467
1468                                 std::pair<TimeAxisView*,double> dev;
1469                                 dev = (*ci)->covers_y_position (top_of_trackviews);
1470                                 if (dev.first) {
1471                                         /* some automation lane is currently at the top */
1472                                         if (ci == kids.rbegin()) {
1473                                                 /* first (bottom-most) autmation lane is at the top.
1474                                                  * -> move to next track
1475                                                  */
1476                                                 nkid = kids.rend();
1477                                         }
1478                                         break;
1479                                 }
1480                                 nkid = ci;
1481                         }
1482
1483                         if (nkid != kids.rend()) {
1484                                 ensure_time_axis_view_is_visible (**nkid, true);
1485                                 return true;
1486                         }
1487                         break;
1488                 }
1489                 next = t;
1490         }
1491
1492         /* move to the track below the first one that covers the */
1493
1494         if (next != track_views.rend()) {
1495                 ensure_time_axis_view_is_visible (**next, true);
1496                 return true;
1497         }
1498
1499         return false;
1500 }
1501
1502 bool
1503 Editor::scroll_up_one_track (bool skip_child_views)
1504 {
1505         TrackViewList::iterator prev = track_views.end();
1506         double top_of_trackviews = vertical_adjustment.get_value ();
1507
1508         for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1509
1510                 if ((*t)->hidden()) {
1511                         continue;
1512                 }
1513
1514                 /* find the trackview at the top of the trackview group
1515                  *
1516                  * Note that covers_y_position() is recursive and includes child views
1517                  */
1518                 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1519
1520                 if (res.first) {
1521                         if (skip_child_views) {
1522                                 break;
1523                         }
1524                         /* automation lane (one level, non-recursive)
1525                          *
1526                          * - if no automation lane exists -> move to prev tack
1527                          * - if no y-axis match is found -> the current track is at the top -> move to prev track
1528                          *     (actually last automation lane of previous track, see below)
1529                          * - if first (top-most) lane is at the top -> move to this track
1530                          * - else move up one lane
1531                          */
1532                         TimeAxisView::Children kids = (*t)->get_child_list();
1533                         TimeAxisView::Children::iterator pkid = kids.end();
1534
1535                         for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1536                                 if ((*ci)->hidden()) {
1537                                         continue;
1538                                 }
1539
1540                                 std::pair<TimeAxisView*,double> dev;
1541                                 dev = (*ci)->covers_y_position (top_of_trackviews);
1542                                 if (dev.first) {
1543                                         /* some automation lane is currently at the top */
1544                                         if (ci == kids.begin()) {
1545                                                 /* first (top-most) autmation lane is at the top.
1546                                                  * jump directly to this track's top
1547                                                  */
1548                                                 ensure_time_axis_view_is_visible (**t, true);
1549                                                 return true;
1550                                         }
1551                                         else if (pkid != kids.end()) {
1552                                                 /* some other automation lane is at the top.
1553                                                  * move up to prev automation lane.
1554                                                  */
1555                                                 ensure_time_axis_view_is_visible (**pkid, true);
1556                                                 return true;
1557                                         }
1558                                         assert(0); // not reached
1559                                         break;
1560                                 }
1561                                 pkid = ci;
1562                         }
1563                         break;
1564                 }
1565
1566                 prev = t;
1567         }
1568
1569         if (prev != track_views.end()) {
1570                 // move to bottom-most automation-lane of the previous track
1571                 TimeAxisView::Children kids = (*prev)->get_child_list();
1572                 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1573                 if (!skip_child_views) {
1574                         // find the last visible lane
1575                         for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1576                                 if (!(*ci)->hidden()) {
1577                                         pkid = ci;
1578                                         break;
1579                                 }
1580                         }
1581                 }
1582                 if (pkid != kids.rend()) {
1583                         ensure_time_axis_view_is_visible (**pkid, true);
1584                 } else  {
1585                         ensure_time_axis_view_is_visible (**prev, true);
1586                 }
1587                 return true;
1588         }
1589
1590         return false;
1591 }
1592
1593 void
1594 Editor::scroll_left_step ()
1595 {
1596         framepos_t xdelta = (current_page_samples() / 8);
1597
1598         if (leftmost_frame > xdelta) {
1599                 reset_x_origin (leftmost_frame - xdelta);
1600         } else {
1601                 reset_x_origin (0);
1602         }
1603 }
1604
1605
1606 void
1607 Editor::scroll_right_step ()
1608 {
1609         framepos_t xdelta = (current_page_samples() / 8);
1610
1611         if (max_framepos - xdelta > leftmost_frame) {
1612                 reset_x_origin (leftmost_frame + xdelta);
1613         } else {
1614                 reset_x_origin (max_framepos - current_page_samples());
1615         }
1616 }
1617
1618 void
1619 Editor::scroll_left_half_page ()
1620 {
1621         framepos_t xdelta = (current_page_samples() / 2);
1622         if (leftmost_frame > xdelta) {
1623                 reset_x_origin (leftmost_frame - xdelta);
1624         } else {
1625                 reset_x_origin (0);
1626         }
1627 }
1628
1629 void
1630 Editor::scroll_right_half_page ()
1631 {
1632         framepos_t xdelta = (current_page_samples() / 2);
1633         if (max_framepos - xdelta > leftmost_frame) {
1634                 reset_x_origin (leftmost_frame + xdelta);
1635         } else {
1636                 reset_x_origin (max_framepos - current_page_samples());
1637         }
1638 }
1639
1640 /* ZOOM */
1641
1642 void
1643 Editor::tav_zoom_step (bool coarser)
1644 {
1645         DisplaySuspender ds;
1646
1647         TrackViewList* ts;
1648
1649         if (selection->tracks.empty()) {
1650                 ts = &track_views;
1651         } else {
1652                 ts = &selection->tracks;
1653         }
1654
1655         for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1656                 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1657                         tv->step_height (coarser);
1658         }
1659 }
1660
1661 void
1662 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1663 {
1664         DisplaySuspender ds;
1665
1666         TrackViewList* ts;
1667
1668         if (selection->tracks.empty() || force_all) {
1669                 ts = &track_views;
1670         } else {
1671                 ts = &selection->tracks;
1672         }
1673
1674         for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1675                 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1676                 uint32_t h = tv->current_height ();
1677
1678                 if (coarser) {
1679                         if (h > 5) {
1680                                 h -= 5; // pixels
1681                                 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1682                                         tv->set_height (h);
1683                                 }
1684                         }
1685                 } else {
1686                         tv->set_height (h + 5);
1687                 }
1688         }
1689 }
1690
1691 void
1692 Editor::temporal_zoom_step_mouse_focus (bool coarser)
1693 {
1694         Editing::ZoomFocus temp_focus = zoom_focus;
1695         zoom_focus = Editing::ZoomFocusMouse;
1696         temporal_zoom_step (coarser);
1697         zoom_focus = temp_focus;
1698 }
1699
1700 void
1701 Editor::temporal_zoom_step (bool coarser)
1702 {
1703         ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1704
1705         framecnt_t nspp = samples_per_pixel;
1706
1707         if (coarser) {
1708                 nspp *= 2;
1709         } else {
1710                 nspp /= 2;
1711         }
1712
1713         temporal_zoom (nspp);
1714 }
1715
1716 void
1717 Editor::temporal_zoom (framecnt_t fpp)
1718 {
1719         if (!_session) {
1720                 return;
1721         }
1722
1723         framepos_t current_page = current_page_samples();
1724         framepos_t current_leftmost = leftmost_frame;
1725         framepos_t current_rightmost;
1726         framepos_t current_center;
1727         framepos_t new_page_size;
1728         framepos_t half_page_size;
1729         framepos_t leftmost_after_zoom = 0;
1730         framepos_t where;
1731         bool in_track_canvas;
1732         framecnt_t nfpp;
1733         double l;
1734
1735         if (fpp == samples_per_pixel) {
1736                 return;
1737         }
1738
1739         // Imposing an arbitrary limit to zoom out as too much zoom out produces
1740         // segfaults for lack of memory. If somebody decides this is not high enough I
1741         // believe it can be raisen to higher values but some limit must be in place.
1742         //
1743         // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1744         // all of which is used for the editor track displays. The whole day
1745         // would be 4147200000 samples, so 2592000 samples per pixel.
1746
1747         nfpp = min (fpp, (framecnt_t) 2592000);
1748         nfpp = max ((framecnt_t) 1, nfpp);
1749
1750         new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1751         half_page_size = new_page_size / 2;
1752
1753         switch (zoom_focus) {
1754         case ZoomFocusLeft:
1755                 leftmost_after_zoom = current_leftmost;
1756                 break;
1757
1758         case ZoomFocusRight:
1759                 current_rightmost = leftmost_frame + current_page;
1760                 if (current_rightmost < new_page_size) {
1761                         leftmost_after_zoom = 0;
1762                 } else {
1763                         leftmost_after_zoom = current_rightmost - new_page_size;
1764                 }
1765                 break;
1766
1767         case ZoomFocusCenter:
1768                 current_center = current_leftmost + (current_page/2);
1769                 if (current_center < half_page_size) {
1770                         leftmost_after_zoom = 0;
1771                 } else {
1772                         leftmost_after_zoom = current_center - half_page_size;
1773                 }
1774                 break;
1775
1776         case ZoomFocusPlayhead:
1777                 /* centre playhead */
1778                 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1779
1780                 if (l < 0) {
1781                         leftmost_after_zoom = 0;
1782                 } else if (l > max_framepos) {
1783                         leftmost_after_zoom = max_framepos - new_page_size;
1784                 } else {
1785                         leftmost_after_zoom = (framepos_t) l;
1786                 }
1787                 break;
1788
1789         case ZoomFocusMouse:
1790                 /* try to keep the mouse over the same point in the display */
1791
1792                 if (!mouse_frame (where, in_track_canvas)) {
1793                         /* use playhead instead */
1794                         where = playhead_cursor->current_frame ();
1795
1796                         if (where < half_page_size) {
1797                                 leftmost_after_zoom = 0;
1798                         } else {
1799                                 leftmost_after_zoom = where - half_page_size;
1800                         }
1801
1802                 } else {
1803
1804                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1805
1806                         if (l < 0) {
1807                                 leftmost_after_zoom = 0;
1808                         } else if (l > max_framepos) {
1809                                 leftmost_after_zoom = max_framepos - new_page_size;
1810                         } else {
1811                                 leftmost_after_zoom = (framepos_t) l;
1812                         }
1813                 }
1814
1815                 break;
1816
1817         case ZoomFocusEdit:
1818                 /* try to keep the edit point in the same place */
1819                 where = get_preferred_edit_position ();
1820
1821                 if (where > 0) {
1822
1823                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1824
1825                         if (l < 0) {
1826                                 leftmost_after_zoom = 0;
1827                         } else if (l > max_framepos) {
1828                                 leftmost_after_zoom = max_framepos - new_page_size;
1829                         } else {
1830                                 leftmost_after_zoom = (framepos_t) l;
1831                         }
1832
1833                 } else {
1834                         /* edit point not defined */
1835                         return;
1836                 }
1837                 break;
1838
1839         }
1840
1841         // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1842
1843         reposition_and_zoom (leftmost_after_zoom, nfpp);
1844 }
1845
1846 void
1847 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1848 {
1849         /* this func helps make sure we leave a little space
1850            at each end of the editor so that the zoom doesn't fit the region
1851            precisely to the screen.
1852         */
1853
1854         GdkScreen* screen = gdk_screen_get_default ();
1855         const gint pixwidth = gdk_screen_get_width (screen);
1856         const gint mmwidth = gdk_screen_get_width_mm (screen);
1857         const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1858         const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1859
1860         const framepos_t range = end - start;
1861         const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1862         const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1863
1864         if (start > extra_samples) {
1865                 start -= extra_samples;
1866         } else {
1867                 start = 0;
1868         }
1869
1870         if (max_framepos - extra_samples > end) {
1871                 end += extra_samples;
1872         } else {
1873                 end = max_framepos;
1874         }
1875 }
1876
1877 void
1878 Editor::temporal_zoom_region (bool both_axes)
1879 {
1880         framepos_t start = max_framepos;
1881         framepos_t end = 0;
1882         set<TimeAxisView*> tracks;
1883
1884         if ( !get_selection_extents(start, end) )
1885                 return;
1886
1887         calc_extra_zoom_edges (start, end);
1888
1889         /* if we're zooming on both axes we need to save track heights etc.
1890          */
1891
1892         undo_visual_stack.push_back (current_visual_state (both_axes));
1893
1894         PBD::Unwinder<bool> nsv (no_save_visual, true);
1895
1896         temporal_zoom_by_frame (start, end);
1897
1898         if (both_axes) {
1899                 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1900
1901                 /* set visible track heights appropriately */
1902
1903                 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1904                         (*t)->set_height (per_track_height);
1905                 }
1906
1907                 /* hide irrelevant tracks */
1908
1909                 DisplaySuspender ds;
1910
1911                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1912                         if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1913                                 hide_track_in_display (*i);
1914                         }
1915                 }
1916
1917                 vertical_adjustment.set_value (0.0);
1918         }
1919
1920         redo_visual_stack.push_back (current_visual_state (both_axes));
1921 }
1922
1923
1924 bool
1925 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1926 {
1927         start = max_framepos;
1928         end = 0;
1929         bool ret = true;
1930
1931         //ToDo:  if notes are selected, set extents to that selection
1932
1933         //ToDo:  if control points are selected, set extents to that selection
1934
1935         if ( !selection->regions.empty() ) {
1936                 RegionSelection rs = get_regions_from_selection_and_entered ();
1937
1938                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1939
1940                         if ((*i)->region()->position() < start) {
1941                                 start = (*i)->region()->position();
1942                         }
1943
1944                         if ((*i)->region()->last_frame() + 1 > end) {
1945                                 end = (*i)->region()->last_frame() + 1;
1946                         }
1947                 }
1948
1949         } else if (!selection->time.empty()) {
1950                 start = selection->time.start();
1951                 end = selection->time.end_frame();
1952         } else
1953                 ret = false;  //no selection found
1954
1955         //range check
1956         if ((start == 0 && end == 0) || end < start) {
1957                 ret = false;
1958         }
1959
1960         return ret;
1961 }
1962
1963
1964 void
1965 Editor::temporal_zoom_selection (bool both_axes)
1966 {
1967         if (!selection) return;
1968
1969         //ToDo:  if notes are selected, zoom to that
1970
1971         //ToDo:  if control points are selected, zoom to that
1972
1973         //if region(s) are selected, zoom to that
1974         if ( !selection->regions.empty() )
1975                 temporal_zoom_region (both_axes);
1976
1977         //if a range is selected, zoom to that
1978         if (!selection->time.empty()) {
1979
1980                 framepos_t start,  end;
1981                 if (get_selection_extents (start, end)) {
1982                         calc_extra_zoom_edges(start, end);
1983                         temporal_zoom_by_frame (start, end);
1984                 }
1985
1986                 if (both_axes)
1987                         fit_selection();
1988         }
1989
1990 }
1991
1992 void
1993 Editor::temporal_zoom_session ()
1994 {
1995         ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1996
1997         if (_session) {
1998                 framecnt_t start = _session->current_start_frame();
1999                 framecnt_t end = _session->current_end_frame();
2000
2001                 if (_session->actively_recording () ) {
2002                         framepos_t cur = playhead_cursor->current_frame ();
2003                         if (cur > end) {
2004                                 /* recording beyond the end marker; zoom out
2005                                  * by 5 seconds more so that if 'follow
2006                                  * playhead' is active we don't immediately
2007                                  * scroll.
2008                                  */
2009                                 end = cur + _session->frame_rate() * 5;
2010                         }
2011                 }
2012
2013                 if ((start == 0 && end == 0) || end < start) {
2014                         return;
2015                 }
2016
2017                 calc_extra_zoom_edges(start, end);
2018
2019                 temporal_zoom_by_frame (start, end);
2020         }
2021 }
2022
2023 void
2024 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2025 {
2026         if (!_session) return;
2027
2028         if ((start == 0 && end == 0) || end < start) {
2029                 return;
2030         }
2031
2032         framepos_t range = end - start;
2033
2034         const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2035
2036         framepos_t new_page = range;
2037         framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2038         framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2039
2040         if (new_leftmost > middle) {
2041                 new_leftmost = 0;
2042         }
2043
2044         if (new_leftmost < 0) {
2045                 new_leftmost = 0;
2046         }
2047
2048         reposition_and_zoom (new_leftmost, new_fpp);
2049 }
2050
2051 void
2052 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2053 {
2054         if (!_session) {
2055                 return;
2056         }
2057
2058         framecnt_t range_before = frame - leftmost_frame;
2059         framecnt_t new_spp;
2060
2061         if (coarser) {
2062                 if (samples_per_pixel <= 1) {
2063                         new_spp = 2;
2064                 } else {
2065                         new_spp = samples_per_pixel + (samples_per_pixel/2);
2066                 }
2067                 range_before += range_before/2;
2068         } else {
2069                 if (samples_per_pixel >= 1) {
2070                         new_spp = samples_per_pixel - (samples_per_pixel/2);
2071                 } else {
2072                         /* could bail out here since we cannot zoom any finer,
2073                            but leave that to the equality test below
2074                         */
2075                         new_spp = samples_per_pixel;
2076                 }
2077
2078                 range_before -= range_before/2;
2079         }
2080
2081         if (new_spp == samples_per_pixel)  {
2082                 return;
2083         }
2084
2085         /* zoom focus is automatically taken as @param frame when this
2086            method is used.
2087         */
2088
2089         framepos_t new_leftmost = frame - (framepos_t)range_before;
2090
2091         if (new_leftmost > frame) {
2092                 new_leftmost = 0;
2093         }
2094
2095         if (new_leftmost < 0) {
2096                 new_leftmost = 0;
2097         }
2098
2099         reposition_and_zoom (new_leftmost, new_spp);
2100 }
2101
2102
2103 bool
2104 Editor::choose_new_marker_name(string &name) {
2105
2106         if (!UIConfiguration::instance().get_name_new_markers()) {
2107                 /* don't prompt user for a new name */
2108                 return true;
2109         }
2110
2111         ArdourPrompter dialog (true);
2112
2113         dialog.set_prompt (_("New Name:"));
2114
2115         dialog.set_title (_("New Location Marker"));
2116
2117         dialog.set_name ("MarkNameWindow");
2118         dialog.set_size_request (250, -1);
2119         dialog.set_position (Gtk::WIN_POS_MOUSE);
2120
2121         dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2122         dialog.set_initial_text (name);
2123
2124         dialog.show ();
2125
2126         switch (dialog.run ()) {
2127         case RESPONSE_ACCEPT:
2128                 break;
2129         default:
2130                 return false;
2131         }
2132
2133         dialog.get_result(name);
2134         return true;
2135
2136 }
2137
2138
2139 void
2140 Editor::add_location_from_selection ()
2141 {
2142         string rangename;
2143
2144         if (selection->time.empty()) {
2145                 return;
2146         }
2147
2148         if (_session == 0 || clicked_axisview == 0) {
2149                 return;
2150         }
2151
2152         framepos_t start = selection->time[clicked_selection].start;
2153         framepos_t end = selection->time[clicked_selection].end;
2154
2155         _session->locations()->next_available_name(rangename,"selection");
2156         Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2157
2158         begin_reversible_command (_("add marker"));
2159
2160         XMLNode &before = _session->locations()->get_state();
2161         _session->locations()->add (location, true);
2162         XMLNode &after = _session->locations()->get_state();
2163         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2164
2165         commit_reversible_command ();
2166 }
2167
2168 void
2169 Editor::add_location_mark (framepos_t where)
2170 {
2171         string markername;
2172
2173         select_new_marker = true;
2174
2175         _session->locations()->next_available_name(markername,"mark");
2176         if (!choose_new_marker_name(markername)) {
2177                 return;
2178         }
2179         Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2180         begin_reversible_command (_("add marker"));
2181
2182         XMLNode &before = _session->locations()->get_state();
2183         _session->locations()->add (location, true);
2184         XMLNode &after = _session->locations()->get_state();
2185         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2186
2187         commit_reversible_command ();
2188 }
2189
2190 void
2191 Editor::set_session_start_from_playhead ()
2192 {
2193         if (!_session)
2194                 return;
2195
2196         Location* loc;
2197         if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
2198                 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2199         } else {
2200                 XMLNode &before = loc->get_state();
2201
2202                 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2203
2204                 XMLNode &after = loc->get_state();
2205
2206                 begin_reversible_command (_("Set session start"));
2207
2208                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2209
2210                 commit_reversible_command ();
2211         }
2212 }
2213
2214 void
2215 Editor::set_session_end_from_playhead ()
2216 {
2217         if (!_session)
2218                 return;
2219
2220         Location* loc;
2221         if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
2222                 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2223         } else {
2224                 XMLNode &before = loc->get_state();
2225
2226                 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2227
2228                 XMLNode &after = loc->get_state();
2229
2230                 begin_reversible_command (_("Set session start"));
2231
2232                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2233
2234                 commit_reversible_command ();
2235         }
2236 }
2237
2238 void
2239 Editor::add_location_from_playhead_cursor ()
2240 {
2241         add_location_mark (_session->audible_frame());
2242 }
2243
2244 void
2245 Editor::remove_location_at_playhead_cursor ()
2246 {
2247         if (_session) {
2248                 //set up for undo
2249                 XMLNode &before = _session->locations()->get_state();
2250                 bool removed = false;
2251
2252                 //find location(s) at this time
2253                 Locations::LocationList locs;
2254                 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2255                 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2256                         if ((*i)->is_mark()) {
2257                                 _session->locations()->remove (*i);
2258                                 removed = true;
2259                         }
2260                 }
2261
2262                 //store undo
2263                 if (removed) {
2264                         begin_reversible_command (_("remove marker"));
2265                         XMLNode &after = _session->locations()->get_state();
2266                         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2267                         commit_reversible_command ();
2268                 }
2269         }
2270 }
2271
2272 /** Add a range marker around each selected region */
2273 void
2274 Editor::add_locations_from_region ()
2275 {
2276         RegionSelection rs = get_regions_from_selection_and_entered ();
2277
2278         if (rs.empty()) {
2279                 return;
2280         }
2281         bool commit = false;
2282
2283         XMLNode &before = _session->locations()->get_state();
2284
2285         for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2286
2287                 boost::shared_ptr<Region> region = (*i)->region ();
2288
2289                 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2290
2291                 _session->locations()->add (location, true);
2292                 commit = true;
2293         }
2294
2295         if (commit) {
2296                 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2297                 XMLNode &after = _session->locations()->get_state();
2298                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2299                 commit_reversible_command ();
2300         }
2301 }
2302
2303 /** Add a single range marker around all selected regions */
2304 void
2305 Editor::add_location_from_region ()
2306 {
2307         RegionSelection rs = get_regions_from_selection_and_entered ();
2308
2309         if (rs.empty()) {
2310                 return;
2311         }
2312
2313         XMLNode &before = _session->locations()->get_state();
2314
2315         string markername;
2316
2317         if (rs.size() > 1) {
2318                 _session->locations()->next_available_name(markername, "regions");
2319         } else {
2320                 RegionView* rv = *(rs.begin());
2321                 boost::shared_ptr<Region> region = rv->region();
2322                 markername = region->name();
2323         }
2324
2325         if (!choose_new_marker_name(markername)) {
2326                 return;
2327         }
2328
2329         // single range spanning all selected
2330         Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2331         _session->locations()->add (location, true);
2332
2333         begin_reversible_command (_("add marker"));
2334         XMLNode &after = _session->locations()->get_state();
2335         _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2336         commit_reversible_command ();
2337 }
2338
2339 /* MARKS */
2340
2341 void
2342 Editor::jump_forward_to_mark ()
2343 {
2344         if (!_session) {
2345                 return;
2346         }
2347
2348         framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2349
2350         if (pos < 0) {
2351                 return;
2352         }
2353
2354         _session->request_locate (pos, _session->transport_rolling());
2355 }
2356
2357 void
2358 Editor::jump_backward_to_mark ()
2359 {
2360         if (!_session) {
2361                 return;
2362         }
2363
2364         framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2365
2366         if (pos < 0) {
2367                 return;
2368         }
2369
2370         _session->request_locate (pos, _session->transport_rolling());
2371 }
2372
2373 void
2374 Editor::set_mark ()
2375 {
2376         framepos_t const pos = _session->audible_frame ();
2377
2378         string markername;
2379         _session->locations()->next_available_name (markername, "mark");
2380
2381         if (!choose_new_marker_name (markername)) {
2382                 return;
2383         }
2384
2385         _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2386 }
2387
2388 void
2389 Editor::clear_markers ()
2390 {
2391         if (_session) {
2392                 begin_reversible_command (_("clear markers"));
2393
2394                 XMLNode &before = _session->locations()->get_state();
2395                 _session->locations()->clear_markers ();
2396                 XMLNode &after = _session->locations()->get_state();
2397                 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2398
2399                 commit_reversible_command ();
2400         }
2401 }
2402
2403 void
2404 Editor::clear_ranges ()
2405 {
2406         if (_session) {
2407                 begin_reversible_command (_("clear ranges"));
2408
2409                 XMLNode &before = _session->locations()->get_state();
2410
2411                 _session->locations()->clear_ranges ();
2412
2413                 XMLNode &after = _session->locations()->get_state();
2414                 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2415
2416                 commit_reversible_command ();
2417         }
2418 }
2419
2420 void
2421 Editor::clear_locations ()
2422 {
2423         begin_reversible_command (_("clear locations"));
2424
2425         XMLNode &before = _session->locations()->get_state();
2426         _session->locations()->clear ();
2427         XMLNode &after = _session->locations()->get_state();
2428         _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2429
2430         commit_reversible_command ();
2431 }
2432
2433 void
2434 Editor::unhide_markers ()
2435 {
2436         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2437                 Location *l = (*i).first;
2438                 if (l->is_hidden() && l->is_mark()) {
2439                         l->set_hidden(false, this);
2440                 }
2441         }
2442 }
2443
2444 void
2445 Editor::unhide_ranges ()
2446 {
2447         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2448                 Location *l = (*i).first;
2449                 if (l->is_hidden() && l->is_range_marker()) {
2450                         l->set_hidden(false, this);
2451                 }
2452         }
2453 }
2454
2455 /* INSERT/REPLACE */
2456
2457 void
2458 Editor::insert_region_list_selection (float times)
2459 {
2460         RouteTimeAxisView *tv = 0;
2461         boost::shared_ptr<Playlist> playlist;
2462
2463         if (clicked_routeview != 0) {
2464                 tv = clicked_routeview;
2465         } else if (!selection->tracks.empty()) {
2466                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2467                         return;
2468                 }
2469         } else if (entered_track != 0) {
2470                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2471                         return;
2472                 }
2473         } else {
2474                 return;
2475         }
2476
2477         if ((playlist = tv->playlist()) == 0) {
2478                 return;
2479         }
2480
2481         boost::shared_ptr<Region> region = _regions->get_single_selection ();
2482         if (region == 0) {
2483                 return;
2484         }
2485
2486         begin_reversible_command (_("insert region"));
2487         playlist->clear_changes ();
2488         playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2489         if (Config->get_edit_mode() == Ripple)
2490                 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2491
2492         _session->add_command(new StatefulDiffCommand (playlist));
2493         commit_reversible_command ();
2494 }
2495
2496 /* BUILT-IN EFFECTS */
2497
2498 void
2499 Editor::reverse_selection ()
2500 {
2501
2502 }
2503
2504 /* GAIN ENVELOPE EDITING */
2505
2506 void
2507 Editor::edit_envelope ()
2508 {
2509 }
2510
2511 /* PLAYBACK */
2512
2513 void
2514 Editor::transition_to_rolling (bool fwd)
2515 {
2516         if (!_session) {
2517                 return;
2518         }
2519
2520         if (_session->config.get_external_sync()) {
2521                 switch (Config->get_sync_source()) {
2522                 case Engine:
2523                         break;
2524                 default:
2525                         /* transport controlled by the master */
2526                         return;
2527                 }
2528         }
2529
2530         if (_session->is_auditioning()) {
2531                 _session->cancel_audition ();
2532                 return;
2533         }
2534
2535         _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2536 }
2537
2538 void
2539 Editor::play_from_start ()
2540 {
2541         _session->request_locate (_session->current_start_frame(), true);
2542 }
2543
2544 void
2545 Editor::play_from_edit_point ()
2546 {
2547         _session->request_locate (get_preferred_edit_position(), true);
2548 }
2549
2550 void
2551 Editor::play_from_edit_point_and_return ()
2552 {
2553         framepos_t start_frame;
2554         framepos_t return_frame;
2555
2556         start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2557
2558         if (_session->transport_rolling()) {
2559                 _session->request_locate (start_frame, false);
2560                 return;
2561         }
2562
2563         /* don't reset the return frame if its already set */
2564
2565         if ((return_frame = _session->requested_return_frame()) < 0) {
2566                 return_frame = _session->audible_frame();
2567         }
2568
2569         if (start_frame >= 0) {
2570                 _session->request_roll_at_and_return (start_frame, return_frame);
2571         }
2572 }
2573
2574 void
2575 Editor::play_selection ()
2576 {
2577         framepos_t start, end;
2578         if (!get_selection_extents ( start, end))
2579                 return;
2580
2581         AudioRange ar (start, end, 0);
2582         list<AudioRange> lar;
2583         lar.push_back (ar);
2584
2585         _session->request_play_range (&lar, true);
2586 }
2587
2588 framepos_t
2589 Editor::get_preroll ()
2590 {
2591         return Config->get_preroll_seconds() * _session->frame_rate();
2592 }
2593
2594
2595 void
2596 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2597 {
2598         if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2599                 return;
2600
2601         location -= get_preroll();
2602
2603         //don't try to locate before the beginning of time
2604         if ( location < 0 )
2605                 location = 0;
2606
2607         //if follow_playhead is on, keep the playhead on the screen
2608         if ( _follow_playhead )
2609                 if ( location < leftmost_frame )
2610                         location = leftmost_frame;
2611
2612         _session->request_locate( location );
2613 }
2614
2615 void
2616 Editor::play_with_preroll ()
2617 {
2618         {
2619                 framepos_t preroll = get_preroll();
2620
2621                 framepos_t start, end;
2622                 if (!get_selection_extents ( start, end))
2623                         return;
2624
2625                 if (start > preroll)
2626                         start = start - preroll;
2627
2628                 end = end + preroll;  //"post-roll"
2629
2630                 AudioRange ar (start, end, 0);
2631                 list<AudioRange> lar;
2632                 lar.push_back (ar);
2633
2634                 _session->request_play_range (&lar, true);
2635         }
2636 }
2637
2638 void
2639 Editor::play_location (Location& location)
2640 {
2641         if (location.start() <= location.end()) {
2642                 return;
2643         }
2644
2645         _session->request_bounded_roll (location.start(), location.end());
2646 }
2647
2648 void
2649 Editor::loop_location (Location& location)
2650 {
2651         if (location.start() <= location.end()) {
2652                 return;
2653         }
2654
2655         Location* tll;
2656
2657         if ((tll = transport_loop_location()) != 0) {
2658                 tll->set (location.start(), location.end());
2659
2660                 // enable looping, reposition and start rolling
2661                 _session->request_locate (tll->start(), true);
2662                 _session->request_play_loop (true);
2663         }
2664 }
2665
2666 void
2667 Editor::do_layer_operation (LayerOperation op)
2668 {
2669         if (selection->regions.empty ()) {
2670                 return;
2671         }
2672
2673         bool const multiple = selection->regions.size() > 1;
2674         switch (op) {
2675         case Raise:
2676                 if (multiple) {
2677                         begin_reversible_command (_("raise regions"));
2678                 } else {
2679                         begin_reversible_command (_("raise region"));
2680                 }
2681                 break;
2682
2683         case RaiseToTop:
2684                 if (multiple) {
2685                         begin_reversible_command (_("raise regions to top"));
2686                 } else {
2687                         begin_reversible_command (_("raise region to top"));
2688                 }
2689                 break;
2690
2691         case Lower:
2692                 if (multiple) {
2693                         begin_reversible_command (_("lower regions"));
2694                 } else {
2695                         begin_reversible_command (_("lower region"));
2696                 }
2697                 break;
2698
2699         case LowerToBottom:
2700                 if (multiple) {
2701                         begin_reversible_command (_("lower regions to bottom"));
2702                 } else {
2703                         begin_reversible_command (_("lower region"));
2704                 }
2705                 break;
2706         }
2707
2708         set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2709         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2710                 (*i)->clear_owned_changes ();
2711         }
2712
2713         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2714                 boost::shared_ptr<Region> r = (*i)->region ();
2715                 switch (op) {
2716                 case Raise:
2717                         r->raise ();
2718                         break;
2719                 case RaiseToTop:
2720                         r->raise_to_top ();
2721                         break;
2722                 case Lower:
2723                         r->lower ();
2724                         break;
2725                 case LowerToBottom:
2726                         r->lower_to_bottom ();
2727                 }
2728         }
2729
2730         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2731                 vector<Command*> cmds;
2732                 (*i)->rdiff (cmds);
2733                 _session->add_commands (cmds);
2734         }
2735
2736         commit_reversible_command ();
2737 }
2738
2739 void
2740 Editor::raise_region ()
2741 {
2742         do_layer_operation (Raise);
2743 }
2744
2745 void
2746 Editor::raise_region_to_top ()
2747 {
2748         do_layer_operation (RaiseToTop);
2749 }
2750
2751 void
2752 Editor::lower_region ()
2753 {
2754         do_layer_operation (Lower);
2755 }
2756
2757 void
2758 Editor::lower_region_to_bottom ()
2759 {
2760         do_layer_operation (LowerToBottom);
2761 }
2762
2763 /** Show the region editor for the selected regions */
2764 void
2765 Editor::show_region_properties ()
2766 {
2767         selection->foreach_regionview (&RegionView::show_region_editor);
2768 }
2769
2770 /** Show the midi list editor for the selected MIDI regions */
2771 void
2772 Editor::show_midi_list_editor ()
2773 {
2774         selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2775 }
2776
2777 void
2778 Editor::rename_region ()
2779 {
2780         RegionSelection rs = get_regions_from_selection_and_entered ();
2781
2782         if (rs.empty()) {
2783                 return;
2784         }
2785
2786         ArdourDialog d (_("Rename Region"), true, false);
2787         Entry entry;
2788         Label label (_("New name:"));
2789         HBox hbox;
2790
2791         hbox.set_spacing (6);
2792         hbox.pack_start (label, false, false);
2793         hbox.pack_start (entry, true, true);
2794
2795         d.get_vbox()->set_border_width (12);
2796         d.get_vbox()->pack_start (hbox, false, false);
2797
2798         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2799         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2800
2801         d.set_size_request (300, -1);
2802
2803         entry.set_text (rs.front()->region()->name());
2804         entry.select_region (0, -1);
2805
2806         entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2807
2808         d.show_all ();
2809
2810         entry.grab_focus();
2811
2812         int const ret = d.run();
2813
2814         d.hide ();
2815
2816         if (ret != RESPONSE_OK) {
2817                 return;
2818         }
2819
2820         std::string str = entry.get_text();
2821         strip_whitespace_edges (str);
2822         if (!str.empty()) {
2823                 rs.front()->region()->set_name (str);
2824                 _regions->redisplay ();
2825         }
2826 }
2827
2828 /** Start an audition of the first selected region */
2829 void
2830 Editor::play_edit_range ()
2831 {
2832         framepos_t start, end;
2833
2834         if (get_edit_op_range (start, end)) {
2835                 _session->request_bounded_roll (start, end);
2836         }
2837 }
2838
2839 void
2840 Editor::play_selected_region ()
2841 {
2842         framepos_t start = max_framepos;
2843         framepos_t end = 0;
2844
2845         RegionSelection rs = get_regions_from_selection_and_entered ();
2846
2847         if (rs.empty()) {
2848                 return;
2849         }
2850
2851         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2852                 if ((*i)->region()->position() < start) {
2853                         start = (*i)->region()->position();
2854                 }
2855                 if ((*i)->region()->last_frame() + 1 > end) {
2856                         end = (*i)->region()->last_frame() + 1;
2857                 }
2858         }
2859
2860         _session->request_bounded_roll (start, end);
2861 }
2862
2863 void
2864 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2865 {
2866         _session->audition_region (region);
2867 }
2868
2869 void
2870 Editor::region_from_selection ()
2871 {
2872         if (clicked_axisview == 0) {
2873                 return;
2874         }
2875
2876         if (selection->time.empty()) {
2877                 return;
2878         }
2879
2880         framepos_t start = selection->time[clicked_selection].start;
2881         framepos_t end = selection->time[clicked_selection].end;
2882
2883         TrackViewList tracks = get_tracks_for_range_action ();
2884
2885         framepos_t selection_cnt = end - start + 1;
2886
2887         for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2888                 boost::shared_ptr<Region> current;
2889                 boost::shared_ptr<Playlist> pl;
2890                 framepos_t internal_start;
2891                 string new_name;
2892
2893                 if ((pl = (*i)->playlist()) == 0) {
2894                         continue;
2895                 }
2896
2897                 if ((current = pl->top_region_at (start)) == 0) {
2898                         continue;
2899                 }
2900
2901                 internal_start = start - current->position();
2902                 RegionFactory::region_name (new_name, current->name(), true);
2903
2904                 PropertyList plist;
2905
2906                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2907                 plist.add (ARDOUR::Properties::length, selection_cnt);
2908                 plist.add (ARDOUR::Properties::name, new_name);
2909                 plist.add (ARDOUR::Properties::layer, 0);
2910
2911                 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2912         }
2913 }
2914
2915 void
2916 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2917 {
2918         if (selection->time.empty() || selection->tracks.empty()) {
2919                 return;
2920         }
2921
2922         framepos_t start, end;
2923         if (clicked_selection) {
2924                 start = selection->time[clicked_selection].start;
2925                 end = selection->time[clicked_selection].end;
2926         } else {
2927                 start = selection->time.start();
2928                 end = selection->time.end_frame();
2929         }
2930
2931         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2932         sort_track_selection (ts);
2933
2934         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2935                 boost::shared_ptr<Region> current;
2936                 boost::shared_ptr<Playlist> playlist;
2937                 framepos_t internal_start;
2938                 string new_name;
2939
2940                 if ((playlist = (*i)->playlist()) == 0) {
2941                         continue;
2942                 }
2943
2944                 if ((current = playlist->top_region_at(start)) == 0) {
2945                         continue;
2946                 }
2947
2948                 internal_start = start - current->position();
2949                 RegionFactory::region_name (new_name, current->name(), true);
2950
2951                 PropertyList plist;
2952
2953                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2954                 plist.add (ARDOUR::Properties::length, end - start + 1);
2955                 plist.add (ARDOUR::Properties::name, new_name);
2956
2957                 new_regions.push_back (RegionFactory::create (current, plist));
2958         }
2959 }
2960
2961 void
2962 Editor::split_multichannel_region ()
2963 {
2964         RegionSelection rs = get_regions_from_selection_and_entered ();
2965
2966         if (rs.empty()) {
2967                 return;
2968         }
2969
2970         vector< boost::shared_ptr<Region> > v;
2971
2972         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2973                 (*x)->region()->separate_by_channel (*_session, v);
2974         }
2975 }
2976
2977 void
2978 Editor::new_region_from_selection ()
2979 {
2980         region_from_selection ();
2981         cancel_selection ();
2982 }
2983
2984 static void
2985 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2986 {
2987         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2988         // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
2989         case Evoral::OverlapNone:
2990                 break;
2991         default:
2992                 rs->push_back (rv);
2993         }
2994 }
2995
2996 /** Return either:
2997  *    - selected tracks, or if there are none...
2998  *    - tracks containing selected regions, or if there are none...
2999  *    - all tracks
3000  * @return tracks.
3001  */
3002 TrackViewList
3003 Editor::get_tracks_for_range_action () const
3004 {
3005         TrackViewList t;
3006
3007         if (selection->tracks.empty()) {
3008
3009                 /* use tracks with selected regions */
3010
3011                 RegionSelection rs = selection->regions;
3012
3013                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3014                         TimeAxisView* tv = &(*i)->get_time_axis_view();
3015
3016                         if (!t.contains (tv)) {
3017                                 t.push_back (tv);
3018                         }
3019                 }
3020
3021                 if (t.empty()) {
3022                         /* no regions and no tracks: use all tracks */
3023                         t = track_views;
3024                 }
3025
3026         } else {
3027
3028                 t = selection->tracks;
3029         }
3030
3031         return t.filter_to_unique_playlists();
3032 }
3033
3034 void
3035 Editor::separate_regions_between (const TimeSelection& ts)
3036 {
3037         bool in_command = false;
3038         boost::shared_ptr<Playlist> playlist;
3039         RegionSelection new_selection;
3040
3041         TrackViewList tmptracks = get_tracks_for_range_action ();
3042         sort_track_selection (tmptracks);
3043
3044         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3045
3046                 RouteTimeAxisView* rtv = 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                 split_regions_at (where, rs);
6155         }
6156 }
6157
6158 void
6159 Editor::select_next_route()
6160 {
6161         if (selection->tracks.empty()) {
6162                 selection->set (track_views.front());
6163                 return;
6164         }
6165
6166         TimeAxisView* current = selection->tracks.front();
6167
6168         RouteUI *rui;
6169         do {
6170                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6171
6172                         if (*i == current) {
6173                                 ++i;
6174                                 if (i != track_views.end()) {
6175                                         current = (*i);
6176                                 } else {
6177                                         current = (*(track_views.begin()));
6178                                         //selection->set (*(track_views.begin()));
6179                                 }
6180                                 break;
6181                         }
6182                 }
6183
6184                 rui = dynamic_cast<RouteUI *>(current);
6185
6186         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6187
6188         selection->set (current);
6189
6190         ensure_time_axis_view_is_visible (*current, false);
6191 }
6192
6193 void
6194 Editor::select_prev_route()
6195 {
6196         if (selection->tracks.empty()) {
6197                 selection->set (track_views.front());
6198                 return;
6199         }
6200
6201         TimeAxisView* current = selection->tracks.front();
6202
6203         RouteUI *rui;
6204         do {
6205                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6206
6207                         if (*i == current) {
6208                                 ++i;
6209                                 if (i != track_views.rend()) {
6210                                         current = (*i);
6211                                 } else {
6212                                         current = *(track_views.rbegin());
6213                                 }
6214                                 break;
6215                         }
6216                 }
6217                 rui = dynamic_cast<RouteUI *>(current);
6218
6219         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6220
6221         selection->set (current);
6222
6223         ensure_time_axis_view_is_visible (*current, false);
6224 }
6225
6226 void
6227 Editor::set_loop_from_selection (bool play)
6228 {
6229         if (_session == 0) {
6230                 return;
6231         }
6232
6233         framepos_t start, end;
6234         if (!get_selection_extents ( start, end))
6235                 return;
6236
6237         set_loop_range (start, end,  _("set loop range from selection"));
6238
6239         if (play) {
6240                 _session->request_play_loop (true, true);
6241         }
6242 }
6243
6244 void
6245 Editor::set_loop_from_region (bool play)
6246 {
6247         framepos_t start, end;
6248         if (!get_selection_extents ( start, end))
6249                 return;
6250
6251         set_loop_range (start, end, _("set loop range from region"));
6252
6253         if (play) {
6254                 _session->request_locate (start, true);
6255                 _session->request_play_loop (true);
6256         }
6257 }
6258
6259 void
6260 Editor::set_punch_from_selection ()
6261 {
6262         if (_session == 0) {
6263                 return;
6264         }
6265
6266         framepos_t start, end;
6267         if (!get_selection_extents ( start, end))
6268                 return;
6269
6270         set_punch_range (start, end,  _("set punch range from selection"));
6271 }
6272
6273 void
6274 Editor::set_session_extents_from_selection ()
6275 {
6276         if (_session == 0) {
6277                 return;
6278         }
6279
6280         framepos_t start, end;
6281         if (!get_selection_extents ( start, end))
6282                 return;
6283
6284         Location* loc;
6285         if ((loc = _session->locations()->session_range_location()) == 0) {
6286                 _session->set_session_extents ( start, end );  // this will create a new session range;  no need for UNDO
6287         } else {
6288                 XMLNode &before = loc->get_state();
6289
6290                 _session->set_session_extents ( start, end );
6291
6292                 XMLNode &after = loc->get_state();
6293
6294                 begin_reversible_command (_("set session start/end from selection"));
6295
6296                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6297
6298                 commit_reversible_command ();
6299         }
6300 }
6301
6302 void
6303 Editor::set_punch_start_from_edit_point ()
6304 {
6305         if (_session) {
6306
6307                 framepos_t start = 0;
6308                 framepos_t end = max_framepos;
6309
6310                 //use the existing punch end, if any
6311                 Location* tpl = transport_punch_location();
6312                 if (tpl) {
6313                         end = tpl->end();
6314                 }
6315
6316                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6317                         start = _session->audible_frame();
6318                 } else {
6319                         start = get_preferred_edit_position();
6320                 }
6321
6322                 //snap the selection start/end
6323                 snap_to(start);
6324
6325                 //if there's not already a sensible selection endpoint, go "forever"
6326                 if ( start > end ) {
6327                         end = max_framepos;
6328                 }
6329
6330                 set_punch_range (start, end, _("set punch start from EP"));
6331         }
6332
6333 }
6334
6335 void
6336 Editor::set_punch_end_from_edit_point ()
6337 {
6338         if (_session) {
6339
6340                 framepos_t start = 0;
6341                 framepos_t end = max_framepos;
6342
6343                 //use the existing punch start, if any
6344                 Location* tpl = transport_punch_location();
6345                 if (tpl) {
6346                         start = tpl->start();
6347                 }
6348
6349                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6350                         end = _session->audible_frame();
6351                 } else {
6352                         end = get_preferred_edit_position();
6353                 }
6354
6355                 //snap the selection start/end
6356                 snap_to(end);
6357
6358                 set_punch_range (start, end, _("set punch end from EP"));
6359
6360         }
6361 }
6362
6363 void
6364 Editor::set_loop_start_from_edit_point ()
6365 {
6366         if (_session) {
6367
6368                 framepos_t start = 0;
6369                 framepos_t end = max_framepos;
6370
6371                 //use the existing loop end, if any
6372                 Location* tpl = transport_loop_location();
6373                 if (tpl) {
6374                         end = tpl->end();
6375                 }
6376
6377                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6378                         start = _session->audible_frame();
6379                 } else {
6380                         start = get_preferred_edit_position();
6381                 }
6382
6383                 //snap the selection start/end
6384                 snap_to(start);
6385
6386                 //if there's not already a sensible selection endpoint, go "forever"
6387                 if ( start > end ) {
6388                         end = max_framepos;
6389                 }
6390
6391                 set_loop_range (start, end, _("set loop start from EP"));
6392         }
6393
6394 }
6395
6396 void
6397 Editor::set_loop_end_from_edit_point ()
6398 {
6399         if (_session) {
6400
6401                 framepos_t start = 0;
6402                 framepos_t end = max_framepos;
6403
6404                 //use the existing loop start, if any
6405                 Location* tpl = transport_loop_location();
6406                 if (tpl) {
6407                         start = tpl->start();
6408                 }
6409
6410                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6411                         end = _session->audible_frame();
6412                 } else {
6413                         end = get_preferred_edit_position();
6414                 }
6415
6416                 //snap the selection start/end
6417                 snap_to(end);
6418
6419                 set_loop_range (start, end, _("set loop end from EP"));
6420         }
6421 }
6422
6423 void
6424 Editor::set_punch_from_region ()
6425 {
6426         framepos_t start, end;
6427         if (!get_selection_extents ( start, end))
6428                 return;
6429
6430         set_punch_range (start, end, _("set punch range from region"));
6431 }
6432
6433 void
6434 Editor::pitch_shift_region ()
6435 {
6436         RegionSelection rs = get_regions_from_selection_and_entered ();
6437
6438         RegionSelection audio_rs;
6439         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6440                 if (dynamic_cast<AudioRegionView*> (*i)) {
6441                         audio_rs.push_back (*i);
6442                 }
6443         }
6444
6445         if (audio_rs.empty()) {
6446                 return;
6447         }
6448
6449         pitch_shift (audio_rs, 1.2);
6450 }
6451
6452 void
6453 Editor::set_tempo_from_region ()
6454 {
6455         RegionSelection rs = get_regions_from_selection_and_entered ();
6456
6457         if (!_session || rs.empty()) {
6458                 return;
6459         }
6460
6461         RegionView* rv = rs.front();
6462
6463         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6464 }
6465
6466 void
6467 Editor::use_range_as_bar ()
6468 {
6469         framepos_t start, end;
6470         if (get_edit_op_range (start, end)) {
6471                 define_one_bar (start, end);
6472         }
6473 }
6474
6475 void
6476 Editor::define_one_bar (framepos_t start, framepos_t end)
6477 {
6478         framepos_t length = end - start;
6479
6480         const Meter& m (_session->tempo_map().meter_at_frame (start));
6481
6482         /* length = 1 bar */
6483
6484         /* We're going to deliver a constant tempo here,
6485            so we can use frames per beat to determine length.
6486            now we want frames per beat.
6487            we have frames per bar, and beats per bar, so ...
6488         */
6489
6490         /* XXXX METER MATH */
6491
6492         double frames_per_beat = length / m.divisions_per_bar();
6493
6494         /* beats per minute = */
6495
6496         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6497
6498         /* now decide whether to:
6499
6500             (a) set global tempo
6501             (b) add a new tempo marker
6502
6503         */
6504
6505         const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6506
6507         bool do_global = false;
6508
6509         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6510
6511                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6512                    at the start, or create a new marker
6513                 */
6514
6515                 vector<string> options;
6516                 options.push_back (_("Cancel"));
6517                 options.push_back (_("Add new marker"));
6518                 options.push_back (_("Set global tempo"));
6519
6520                 Choice c (
6521                         _("Define one bar"),
6522                         _("Do you want to set the global tempo or add a new tempo marker?"),
6523                         options
6524                         );
6525
6526                 c.set_default_response (2);
6527
6528                 switch (c.run()) {
6529                 case 0:
6530                         return;
6531
6532                 case 2:
6533                         do_global = true;
6534                         break;
6535
6536                 default:
6537                         do_global = false;
6538                 }
6539
6540         } else {
6541
6542                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6543                    if the marker is at the region starter, change it, otherwise add
6544                    a new tempo marker
6545                 */
6546         }
6547
6548         begin_reversible_command (_("set tempo from region"));
6549         XMLNode& before (_session->tempo_map().get_state());
6550
6551         if (do_global) {
6552                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6553         } else if (t.frame() == start) {
6554                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6555         } else {
6556                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), 0.0, start, TempoSection::Constant, AudioTime);
6557         }
6558
6559         XMLNode& after (_session->tempo_map().get_state());
6560
6561         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6562         commit_reversible_command ();
6563 }
6564
6565 void
6566 Editor::split_region_at_transients ()
6567 {
6568         AnalysisFeatureList positions;
6569
6570         RegionSelection rs = get_regions_from_selection_and_entered ();
6571
6572         if (!_session || rs.empty()) {
6573                 return;
6574         }
6575
6576         begin_reversible_command (_("split regions"));
6577
6578         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6579
6580                 RegionSelection::iterator tmp;
6581
6582                 tmp = i;
6583                 ++tmp;
6584
6585                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6586
6587                 if (ar) {
6588                         ar->transients (positions);
6589                         split_region_at_points ((*i)->region(), positions, true);
6590                         positions.clear ();
6591                 }
6592
6593                 i = tmp;
6594         }
6595
6596         commit_reversible_command ();
6597
6598 }
6599
6600 void
6601 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6602 {
6603         bool use_rhythmic_rodent = false;
6604
6605         boost::shared_ptr<Playlist> pl = r->playlist();
6606
6607         list<boost::shared_ptr<Region> > new_regions;
6608
6609         if (!pl) {
6610                 return;
6611         }
6612
6613         if (positions.empty()) {
6614                 return;
6615         }
6616
6617         if (positions.size() > 20 && can_ferret) {
6618                 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);
6619                 MessageDialog msg (msgstr,
6620                                    false,
6621                                    Gtk::MESSAGE_INFO,
6622                                    Gtk::BUTTONS_OK_CANCEL);
6623
6624                 if (can_ferret) {
6625                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6626                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6627                 } else {
6628                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6629                 }
6630
6631                 msg.set_title (_("Excessive split?"));
6632                 msg.present ();
6633
6634                 int response = msg.run();
6635                 msg.hide ();
6636
6637                 switch (response) {
6638                 case RESPONSE_OK:
6639                         break;
6640                 case RESPONSE_APPLY:
6641                         use_rhythmic_rodent = true;
6642                         break;
6643                 default:
6644                         return;
6645                 }
6646         }
6647
6648         if (use_rhythmic_rodent) {
6649                 show_rhythm_ferret ();
6650                 return;
6651         }
6652
6653         AnalysisFeatureList::const_iterator x;
6654
6655         pl->clear_changes ();
6656         pl->clear_owned_changes ();
6657
6658         x = positions.begin();
6659
6660         if (x == positions.end()) {
6661                 return;
6662         }
6663
6664         pl->freeze ();
6665         pl->remove_region (r);
6666
6667         framepos_t pos = 0;
6668
6669         framepos_t rstart = r->first_frame ();
6670         framepos_t rend = r->last_frame ();
6671
6672         while (x != positions.end()) {
6673
6674                 /* deal with positons that are out of scope of present region bounds */
6675                 if (*x <= rstart || *x > rend) {
6676                         ++x;
6677                         continue;
6678                 }
6679
6680                 /* file start = original start + how far we from the initial position ?  */
6681
6682                 framepos_t file_start = r->start() + pos;
6683
6684                 /* length = next position - current position */
6685
6686                 framepos_t len = (*x) - pos - rstart;
6687
6688                 /* XXX we do we really want to allow even single-sample regions?
6689                  * shouldn't we have some kind of lower limit on region size?
6690                  */
6691
6692                 if (len <= 0) {
6693                         break;
6694                 }
6695
6696                 string new_name;
6697
6698                 if (RegionFactory::region_name (new_name, r->name())) {
6699                         break;
6700                 }
6701
6702                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6703
6704                 PropertyList plist;
6705
6706                 plist.add (ARDOUR::Properties::start, file_start);
6707                 plist.add (ARDOUR::Properties::length, len);
6708                 plist.add (ARDOUR::Properties::name, new_name);
6709                 plist.add (ARDOUR::Properties::layer, 0);
6710                 // TODO set transients_offset
6711
6712                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6713                 /* because we set annouce to false, manually add the new region to the
6714                  * RegionFactory map
6715                  */
6716                 RegionFactory::map_add (nr);
6717
6718                 pl->add_region (nr, rstart + pos);
6719
6720                 if (select_new) {
6721                         new_regions.push_front(nr);
6722                 }
6723
6724                 pos += len;
6725                 ++x;
6726         }
6727
6728         string new_name;
6729
6730         RegionFactory::region_name (new_name, r->name());
6731
6732         /* Add the final region */
6733         PropertyList plist;
6734
6735         plist.add (ARDOUR::Properties::start, r->start() + pos);
6736         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6737         plist.add (ARDOUR::Properties::name, new_name);
6738         plist.add (ARDOUR::Properties::layer, 0);
6739
6740         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6741         /* because we set annouce to false, manually add the new region to the
6742            RegionFactory map
6743         */
6744         RegionFactory::map_add (nr);
6745         pl->add_region (nr, r->position() + pos);
6746
6747         if (select_new) {
6748                 new_regions.push_front(nr);
6749         }
6750
6751         pl->thaw ();
6752
6753         /* We might have removed regions, which alters other regions' layering_index,
6754            so we need to do a recursive diff here.
6755         */
6756         vector<Command*> cmds;
6757         pl->rdiff (cmds);
6758         _session->add_commands (cmds);
6759
6760         _session->add_command (new StatefulDiffCommand (pl));
6761
6762         if (select_new) {
6763
6764                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6765                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6766                 }
6767         }
6768 }
6769
6770 void
6771 Editor::place_transient()
6772 {
6773         if (!_session) {
6774                 return;
6775         }
6776
6777         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6778
6779         if (rs.empty()) {
6780                 return;
6781         }
6782
6783         framepos_t where = get_preferred_edit_position();
6784
6785         begin_reversible_command (_("place transient"));
6786
6787         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6788                 (*r)->region()->add_transient(where);
6789         }
6790
6791         commit_reversible_command ();
6792 }
6793
6794 void
6795 Editor::remove_transient(ArdourCanvas::Item* item)
6796 {
6797         if (!_session) {
6798                 return;
6799         }
6800
6801         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6802         assert (_line);
6803
6804         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6805         _arv->remove_transient (*(float*) _line->get_data ("position"));
6806 }
6807
6808 void
6809 Editor::snap_regions_to_grid ()
6810 {
6811         list <boost::shared_ptr<Playlist > > used_playlists;
6812
6813         RegionSelection rs = get_regions_from_selection_and_entered ();
6814
6815         if (!_session || rs.empty()) {
6816                 return;
6817         }
6818
6819         begin_reversible_command (_("snap regions to grid"));
6820
6821         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6822
6823                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6824
6825                 if (!pl->frozen()) {
6826                         /* we haven't seen this playlist before */
6827
6828                         /* remember used playlists so we can thaw them later */
6829                         used_playlists.push_back(pl);
6830                         pl->freeze();
6831                 }
6832
6833                 framepos_t start_frame = (*r)->region()->first_frame ();
6834                 snap_to (start_frame);
6835                 (*r)->region()->set_position (start_frame);
6836         }
6837
6838         while (used_playlists.size() > 0) {
6839                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6840                 (*i)->thaw();
6841                 used_playlists.pop_front();
6842         }
6843
6844         commit_reversible_command ();
6845 }
6846
6847 void
6848 Editor::close_region_gaps ()
6849 {
6850         list <boost::shared_ptr<Playlist > > used_playlists;
6851
6852         RegionSelection rs = get_regions_from_selection_and_entered ();
6853
6854         if (!_session || rs.empty()) {
6855                 return;
6856         }
6857
6858         Dialog dialog (_("Close Region Gaps"));
6859
6860         Table table (2, 3);
6861         table.set_spacings (12);
6862         table.set_border_width (12);
6863         Label* l = manage (left_aligned_label (_("Crossfade length")));
6864         table.attach (*l, 0, 1, 0, 1);
6865
6866         SpinButton spin_crossfade (1, 0);
6867         spin_crossfade.set_range (0, 15);
6868         spin_crossfade.set_increments (1, 1);
6869         spin_crossfade.set_value (5);
6870         table.attach (spin_crossfade, 1, 2, 0, 1);
6871
6872         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6873
6874         l = manage (left_aligned_label (_("Pull-back length")));
6875         table.attach (*l, 0, 1, 1, 2);
6876
6877         SpinButton spin_pullback (1, 0);
6878         spin_pullback.set_range (0, 100);
6879         spin_pullback.set_increments (1, 1);
6880         spin_pullback.set_value(30);
6881         table.attach (spin_pullback, 1, 2, 1, 2);
6882
6883         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6884
6885         dialog.get_vbox()->pack_start (table);
6886         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6887         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6888         dialog.show_all ();
6889
6890         if (dialog.run () == RESPONSE_CANCEL) {
6891                 return;
6892         }
6893
6894         framepos_t crossfade_len = spin_crossfade.get_value();
6895         framepos_t pull_back_frames = spin_pullback.get_value();
6896
6897         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6898         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6899
6900         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6901
6902         begin_reversible_command (_("close region gaps"));
6903
6904         int idx = 0;
6905         boost::shared_ptr<Region> last_region;
6906
6907         rs.sort_by_position_and_track();
6908
6909         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6910
6911                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6912
6913                 if (!pl->frozen()) {
6914                         /* we haven't seen this playlist before */
6915
6916                         /* remember used playlists so we can thaw them later */
6917                         used_playlists.push_back(pl);
6918                         pl->freeze();
6919                 }
6920
6921                 framepos_t position = (*r)->region()->position();
6922
6923                 if (idx == 0 || position < last_region->position()){
6924                         last_region = (*r)->region();
6925                         idx++;
6926                         continue;
6927                 }
6928
6929                 (*r)->region()->trim_front( (position - pull_back_frames));
6930                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6931
6932                 last_region = (*r)->region();
6933
6934                 idx++;
6935         }
6936
6937         while (used_playlists.size() > 0) {
6938                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6939                 (*i)->thaw();
6940                 used_playlists.pop_front();
6941         }
6942
6943         commit_reversible_command ();
6944 }
6945
6946 void
6947 Editor::tab_to_transient (bool forward)
6948 {
6949         AnalysisFeatureList positions;
6950
6951         RegionSelection rs = get_regions_from_selection_and_entered ();
6952
6953         if (!_session) {
6954                 return;
6955         }
6956
6957         framepos_t pos = _session->audible_frame ();
6958
6959         if (!selection->tracks.empty()) {
6960
6961                 /* don't waste time searching for transients in duplicate playlists.
6962                  */
6963
6964                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6965
6966                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6967
6968                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6969
6970                         if (rtv) {
6971                                 boost::shared_ptr<Track> tr = rtv->track();
6972                                 if (tr) {
6973                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6974                                         if (pl) {
6975                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6976
6977                                                 if (result >= 0) {
6978                                                         positions.push_back (result);
6979                                                 }
6980                                         }
6981                                 }
6982                         }
6983                 }
6984
6985         } else {
6986
6987                 if (rs.empty()) {
6988                         return;
6989                 }
6990
6991                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6992                         (*r)->region()->get_transients (positions);
6993                 }
6994         }
6995
6996         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6997
6998         if (forward) {
6999                 AnalysisFeatureList::iterator x;
7000
7001                 for (x = positions.begin(); x != positions.end(); ++x) {
7002                         if ((*x) > pos) {
7003                                 break;
7004                         }
7005                 }
7006
7007                 if (x != positions.end ()) {
7008                         _session->request_locate (*x);
7009                 }
7010
7011         } else {
7012                 AnalysisFeatureList::reverse_iterator x;
7013
7014                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7015                         if ((*x) < pos) {
7016                                 break;
7017                         }
7018                 }
7019
7020                 if (x != positions.rend ()) {
7021                         _session->request_locate (*x);
7022                 }
7023         }
7024 }
7025
7026 void
7027 Editor::playhead_forward_to_grid ()
7028 {
7029         if (!_session) {
7030                 return;
7031         }
7032
7033         framepos_t pos = playhead_cursor->current_frame ();
7034         if (pos < max_framepos - 1) {
7035                 pos += 2;
7036                 snap_to_internal (pos, RoundUpAlways, false);
7037                 _session->request_locate (pos);
7038         }
7039 }
7040
7041
7042 void
7043 Editor::playhead_backward_to_grid ()
7044 {
7045         if (!_session) {
7046                 return;
7047         }
7048
7049         framepos_t pos = playhead_cursor->current_frame ();
7050         if (pos > 2) {
7051                 pos -= 2;
7052                 snap_to_internal (pos, RoundDownAlways, false);
7053                 _session->request_locate (pos);
7054         }
7055 }
7056
7057 void
7058 Editor::set_track_height (Height h)
7059 {
7060         TrackSelection& ts (selection->tracks);
7061
7062         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7063                 (*x)->set_height_enum (h);
7064         }
7065 }
7066
7067 void
7068 Editor::toggle_tracks_active ()
7069 {
7070         TrackSelection& ts (selection->tracks);
7071         bool first = true;
7072         bool target = false;
7073
7074         if (ts.empty()) {
7075                 return;
7076         }
7077
7078         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7079                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7080
7081                 if (rtv) {
7082                         if (first) {
7083                                 target = !rtv->_route->active();
7084                                 first = false;
7085                         }
7086                         rtv->_route->set_active (target, this);
7087                 }
7088         }
7089 }
7090
7091 void
7092 Editor::remove_tracks ()
7093 {
7094         /* this will delete GUI objects that may be the subject of an event
7095            handler in which this method is called. Defer actual deletion to the
7096            next idle callback, when all event handling is finished.
7097         */
7098         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7099 }
7100
7101 bool
7102 Editor::idle_remove_tracks ()
7103 {
7104         Session::StateProtector sp (_session);
7105         _remove_tracks ();
7106         return false; /* do not call again */
7107 }
7108
7109 void
7110 Editor::_remove_tracks ()
7111 {
7112         TrackSelection& ts (selection->tracks);
7113
7114         if (ts.empty()) {
7115                 return;
7116         }
7117
7118         vector<string> choices;
7119         string prompt;
7120         int ntracks = 0;
7121         int nbusses = 0;
7122         const char* trackstr;
7123         const char* busstr;
7124         vector<boost::shared_ptr<Route> > routes;
7125         bool special_bus = false;
7126
7127         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7128                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7129                 if (!rtv) {
7130                         continue;
7131                 }
7132                 if (rtv->is_track()) {
7133                         ntracks++;
7134                 } else {
7135                         nbusses++;
7136                 }
7137                 routes.push_back (rtv->_route);
7138
7139                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7140                         special_bus = true;
7141                 }
7142         }
7143
7144         if (special_bus && !Config->get_allow_special_bus_removal()) {
7145                 MessageDialog msg (_("That would be bad news ...."),
7146                                    false,
7147                                    Gtk::MESSAGE_INFO,
7148                                    Gtk::BUTTONS_OK);
7149                 msg.set_secondary_text (string_compose (_(
7150                                                                 "Removing the master or monitor bus is such a bad idea\n\
7151 that %1 is not going to allow it.\n\
7152 \n\
7153 If you really want to do this sort of thing\n\
7154 edit your ardour.rc file to set the\n\
7155 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7156
7157                 msg.present ();
7158                 msg.run ();
7159                 return;
7160         }
7161
7162         if (ntracks + nbusses == 0) {
7163                 return;
7164         }
7165
7166         trackstr = P_("track", "tracks", ntracks);
7167         busstr = P_("bus", "busses", nbusses);
7168
7169         if (ntracks) {
7170                 if (nbusses) {
7171                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7172                                                     "(You may also lose the playlists associated with the %2)\n\n"
7173                                                     "This action cannot be undone, and the session file will be overwritten!"),
7174                                                   ntracks, trackstr, nbusses, busstr);
7175                 } else {
7176                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7177                                                     "(You may also lose the playlists associated with the %2)\n\n"
7178                                                     "This action cannot be undone, and the session file will be overwritten!"),
7179                                                   ntracks, trackstr);
7180                 }
7181         } else if (nbusses) {
7182                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7183                                             "This action cannot be undone, and the session file will be overwritten"),
7184                                           nbusses, busstr);
7185         }
7186
7187         choices.push_back (_("No, do nothing."));
7188         if (ntracks + nbusses > 1) {
7189                 choices.push_back (_("Yes, remove them."));
7190         } else {
7191                 choices.push_back (_("Yes, remove it."));
7192         }
7193
7194         string title;
7195         if (ntracks) {
7196                 title = string_compose (_("Remove %1"), trackstr);
7197         } else {
7198                 title = string_compose (_("Remove %1"), busstr);
7199         }
7200
7201         Choice prompter (title, prompt, choices);
7202
7203         if (prompter.run () != 1) {
7204                 return;
7205         }
7206
7207         {
7208                 DisplaySuspender ds;
7209                 boost::shared_ptr<RouteList> rl (new RouteList);
7210                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7211                         rl->push_back (*x);
7212                 }
7213                 _session->remove_routes (rl);
7214         }
7215         /* TrackSelection and RouteList leave scope,
7216          * destructors are called,
7217          * diskstream drops references, save_state is called (again for every track)
7218          */
7219 }
7220
7221 void
7222 Editor::do_insert_time ()
7223 {
7224         if (selection->tracks.empty()) {
7225                 return;
7226         }
7227
7228         InsertRemoveTimeDialog d (*this);
7229         int response = d.run ();
7230
7231         if (response != RESPONSE_OK) {
7232                 return;
7233         }
7234
7235         if (d.distance() == 0) {
7236                 return;
7237         }
7238
7239         insert_time (
7240                 get_preferred_edit_position (EDIT_IGNORE_MOUSE),
7241                 d.distance(),
7242                 d.intersected_region_action (),
7243                 d.all_playlists(),
7244                 d.move_glued(),
7245                 d.move_markers(),
7246                 d.move_glued_markers(),
7247                 d.move_locked_markers(),
7248                 d.move_tempos()
7249                 );
7250 }
7251
7252 void
7253 Editor::insert_time (
7254         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7255         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7256         )
7257 {
7258
7259         if (Config->get_edit_mode() == Lock) {
7260                 return;
7261         }
7262         bool in_command = false;
7263
7264         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7265
7266         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7267
7268                 /* regions */
7269
7270                 /* don't operate on any playlist more than once, which could
7271                  * happen if "all playlists" is enabled, but there is more
7272                  * than 1 track using playlists "from" a given track.
7273                  */
7274
7275                 set<boost::shared_ptr<Playlist> > pl;
7276
7277                 if (all_playlists) {
7278                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7279                         if (rtav && rtav->track ()) {
7280                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7281                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7282                                         pl.insert (*p);
7283                                 }
7284                         }
7285                 } else {
7286                         if ((*x)->playlist ()) {
7287                                 pl.insert ((*x)->playlist ());
7288                         }
7289                 }
7290
7291                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7292
7293                         (*i)->clear_changes ();
7294                         (*i)->clear_owned_changes ();
7295
7296                         if (opt == SplitIntersected) {
7297                                 (*i)->split (pos);
7298                         }
7299
7300                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7301
7302                         if (!in_command) {
7303                                 begin_reversible_command (_("insert time"));
7304                                 in_command = true;
7305                         }
7306                         vector<Command*> cmds;
7307                         (*i)->rdiff (cmds);
7308                         _session->add_commands (cmds);
7309
7310                         _session->add_command (new StatefulDiffCommand (*i));
7311                 }
7312
7313                 /* automation */
7314                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7315                 if (rtav) {
7316                         if (!in_command) {
7317                                 begin_reversible_command (_("insert time"));
7318                                 in_command = true;
7319                         }
7320                         rtav->route ()->shift (pos, frames);
7321                 }
7322         }
7323
7324         /* markers */
7325         if (markers_too) {
7326                 bool moved = false;
7327                 XMLNode& before (_session->locations()->get_state());
7328                 Locations::LocationList copy (_session->locations()->list());
7329
7330                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7331
7332                         Locations::LocationList::const_iterator tmp;
7333
7334                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7335                                 bool const was_locked = (*i)->locked ();
7336                                 if (locked_markers_too) {
7337                                         (*i)->unlock ();
7338                                 }
7339
7340                                 if ((*i)->start() >= pos) {
7341                                         // move end first, in case we're moving by more than the length of the range
7342                                         if (!(*i)->is_mark()) {
7343                                                 (*i)->set_end ((*i)->end() + frames);
7344                                         }
7345                                         (*i)->set_start ((*i)->start() + frames);
7346                                         moved = true;
7347                                 }
7348
7349                                 if (was_locked) {
7350                                         (*i)->lock ();
7351                                 }
7352                         }
7353                 }
7354
7355                 if (moved) {
7356                         if (!in_command) {
7357                                 begin_reversible_command (_("insert time"));
7358                                 in_command = true;
7359                         }
7360                         XMLNode& after (_session->locations()->get_state());
7361                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7362                 }
7363         }
7364
7365         if (tempo_too) {
7366                 if (!in_command) {
7367                         begin_reversible_command (_("insert time"));
7368                         in_command = true;
7369                 }
7370                 XMLNode& before (_session->tempo_map().get_state());
7371                 _session->tempo_map().insert_time (pos, frames);
7372                 XMLNode& after (_session->tempo_map().get_state());
7373                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7374         }
7375
7376         if (in_command) {
7377                 commit_reversible_command ();
7378         }
7379 }
7380
7381 void
7382 Editor::do_remove_time ()
7383 {
7384         if (selection->tracks.empty()) {
7385                 return;
7386         }
7387
7388         framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
7389         InsertRemoveTimeDialog d (*this, true);
7390
7391         int response = d.run ();
7392
7393         if (response != RESPONSE_OK) {
7394                 return;
7395         }
7396
7397         framecnt_t distance = d.distance();
7398
7399         if (distance == 0) {
7400                 return;
7401         }
7402
7403         remove_time (
7404                 pos,
7405                 distance,
7406                 SplitIntersected,
7407                 d.move_glued(),
7408                 d.move_markers(),
7409                 d.move_glued_markers(),
7410                 d.move_locked_markers(),
7411                 d.move_tempos()
7412         );
7413 }
7414
7415 void
7416 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7417                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7418 {
7419         if (Config->get_edit_mode() == Lock) {
7420                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7421                 return;
7422         }
7423         bool in_command = false;
7424
7425         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7426                 /* regions */
7427                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7428
7429                 if (pl) {
7430
7431                         XMLNode &before = pl->get_state();
7432
7433                         std::list<AudioRange> rl;
7434                         AudioRange ar(pos, pos+frames, 0);
7435                         rl.push_back(ar);
7436                         pl->cut (rl);
7437                         pl->shift (pos, -frames, true, ignore_music_glue);
7438
7439                         if (!in_command) {
7440                                 begin_reversible_command (_("remove time"));
7441                                 in_command = true;
7442                         }
7443                         XMLNode &after = pl->get_state();
7444
7445                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7446                 }
7447
7448                 /* automation */
7449                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7450                 if (rtav) {
7451                         if (!in_command) {
7452                                 begin_reversible_command (_("remove time"));
7453                                 in_command = true;
7454                         }
7455                         rtav->route ()->shift (pos, -frames);
7456                 }
7457         }
7458
7459         std::list<Location*> loc_kill_list;
7460
7461         /* markers */
7462         if (markers_too) {
7463                 bool moved = false;
7464                 XMLNode& before (_session->locations()->get_state());
7465                 Locations::LocationList copy (_session->locations()->list());
7466
7467                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7468                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7469
7470                                 bool const was_locked = (*i)->locked ();
7471                                 if (locked_markers_too) {
7472                                         (*i)->unlock ();
7473                                 }
7474
7475                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7476                                         if ((*i)->end() >= pos
7477                                         && (*i)->end() < pos+frames
7478                                         && (*i)->start() >= pos
7479                                         && (*i)->end() < pos+frames) {  // range is completely enclosed;  kill it
7480                                                 moved = true;
7481                                                 loc_kill_list.push_back(*i);
7482                                         } else {  // only start or end is included, try to do the right thing
7483                                                 // move start before moving end, to avoid trying to move the end to before the start
7484                                                 // if we're removing more time than the length of the range
7485                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7486                                                         // start is within cut
7487                                                         (*i)->set_start (pos);  // bring the start marker to the beginning of the cut
7488                                                         moved = true;
7489                                                 } else if ((*i)->start() >= pos+frames) {
7490                                                         // start (and thus entire range) lies beyond end of cut
7491                                                         (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7492                                                         moved = true;
7493                                                 }
7494                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7495                                                         // end is inside cut
7496                                                         (*i)->set_end (pos);  // bring the end to the cut
7497                                                         moved = true;
7498                                                 } else if ((*i)->end() >= pos+frames) {
7499                                                         // end is beyond end of cut
7500                                                         (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7501                                                         moved = true;
7502                                                 }
7503
7504                                         }
7505                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7506                                         loc_kill_list.push_back(*i);
7507                                         moved = true;
7508                                 } else if ((*i)->start() >= pos) {
7509                                         (*i)->set_start ((*i)->start() -frames);
7510                                         moved = true;
7511                                 }
7512
7513                                 if (was_locked) {
7514                                         (*i)->lock ();
7515                                 }
7516                         }
7517                 }
7518
7519                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7520                         _session->locations()->remove( *i );
7521                 }
7522
7523                 if (moved) {
7524                         if (!in_command) {
7525                                 begin_reversible_command (_("remove time"));
7526                                 in_command = true;
7527                         }
7528                         XMLNode& after (_session->locations()->get_state());
7529                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7530                 }
7531         }
7532
7533         if (tempo_too) {
7534                 XMLNode& before (_session->tempo_map().get_state());
7535
7536                 if (_session->tempo_map().remove_time (pos, frames) ) {
7537                         if (!in_command) {
7538                                 begin_reversible_command (_("remove time"));
7539                                 in_command = true;
7540                         }
7541                         XMLNode& after (_session->tempo_map().get_state());
7542                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7543                 }
7544         }
7545
7546         if (in_command) {
7547                 commit_reversible_command ();
7548         }
7549 }
7550
7551 void
7552 Editor::fit_selection ()
7553 {
7554         if (!selection->tracks.empty()) {
7555                 fit_tracks (selection->tracks);
7556         } else {
7557                 TrackViewList tvl;
7558
7559                 /* no selected tracks - use tracks with selected regions */
7560
7561                 if (!selection->regions.empty()) {
7562                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7563                                 tvl.push_back (&(*r)->get_time_axis_view ());
7564                         }
7565
7566                         if (!tvl.empty()) {
7567                                 fit_tracks (tvl);
7568                         }
7569                 } else if (internal_editing()) {
7570                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7571                            the entered track
7572                         */
7573                         if (entered_track) {
7574                                 tvl.push_back (entered_track);
7575                                 fit_tracks (tvl);
7576                         }
7577                 }
7578         }
7579
7580 }
7581
7582 void
7583 Editor::fit_tracks (TrackViewList & tracks)
7584 {
7585         if (tracks.empty()) {
7586                 return;
7587         }
7588
7589         uint32_t child_heights = 0;
7590         int visible_tracks = 0;
7591
7592         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7593
7594                 if (!(*t)->marked_for_display()) {
7595                         continue;
7596                 }
7597
7598                 child_heights += (*t)->effective_height() - (*t)->current_height();
7599                 ++visible_tracks;
7600         }
7601
7602         /* compute the per-track height from:
7603
7604            total canvas visible height -
7605                  height that will be taken by visible children of selected
7606                  tracks - height of the ruler/hscroll area
7607         */
7608         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7609         double first_y_pos = DBL_MAX;
7610
7611         if (h < TimeAxisView::preset_height (HeightSmall)) {
7612                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7613                 /* too small to be displayed */
7614                 return;
7615         }
7616
7617         undo_visual_stack.push_back (current_visual_state (true));
7618         PBD::Unwinder<bool> nsv (no_save_visual, true);
7619
7620         /* build a list of all tracks, including children */
7621
7622         TrackViewList all;
7623         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7624                 all.push_back (*i);
7625                 TimeAxisView::Children c = (*i)->get_child_list ();
7626                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7627                         all.push_back (j->get());
7628                 }
7629         }
7630
7631
7632         // find selection range.
7633         // if someone knows how to user TrackViewList::iterator for this
7634         // I'm all ears.
7635         int selected_top = -1;
7636         int selected_bottom = -1;
7637         int i = 0;
7638         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7639                 if ((*t)->marked_for_display ()) {
7640                         if (tracks.contains(*t)) {
7641                                 if (selected_top == -1) {
7642                                         selected_top = i;
7643                                 }
7644                                 selected_bottom = i;
7645                         }
7646                 }
7647         }
7648
7649         i = 0;
7650         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7651                 if ((*t)->marked_for_display ()) {
7652                         if (tracks.contains(*t)) {
7653                                 (*t)->set_height (h);
7654                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7655                         } else {
7656                                 if (i > selected_top && i < selected_bottom) {
7657                                         hide_track_in_display (*t);
7658                                 }
7659                         }
7660                 }
7661         }
7662
7663         /*
7664            set the controls_layout height now, because waiting for its size
7665            request signal handler will cause the vertical adjustment setting to fail
7666         */
7667
7668         controls_layout.property_height () = _full_canvas_height;
7669         vertical_adjustment.set_value (first_y_pos);
7670
7671         redo_visual_stack.push_back (current_visual_state (true));
7672
7673         visible_tracks_selector.set_text (_("Sel"));
7674 }
7675
7676 void
7677 Editor::save_visual_state (uint32_t n)
7678 {
7679         while (visual_states.size() <= n) {
7680                 visual_states.push_back (0);
7681         }
7682
7683         if (visual_states[n] != 0) {
7684                 delete visual_states[n];
7685         }
7686
7687         visual_states[n] = current_visual_state (true);
7688         gdk_beep ();
7689 }
7690
7691 void
7692 Editor::goto_visual_state (uint32_t n)
7693 {
7694         if (visual_states.size() <= n) {
7695                 return;
7696         }
7697
7698         if (visual_states[n] == 0) {
7699                 return;
7700         }
7701
7702         use_visual_state (*visual_states[n]);
7703 }
7704
7705 void
7706 Editor::start_visual_state_op (uint32_t n)
7707 {
7708         save_visual_state (n);
7709
7710         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7711         char buf[32];
7712         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7713         pup->set_text (buf);
7714         pup->touch();
7715 }
7716
7717 void
7718 Editor::cancel_visual_state_op (uint32_t n)
7719 {
7720         goto_visual_state (n);
7721 }
7722
7723 void
7724 Editor::toggle_region_mute ()
7725 {
7726         if (_ignore_region_action) {
7727                 return;
7728         }
7729
7730         RegionSelection rs = get_regions_from_selection_and_entered ();
7731
7732         if (rs.empty ()) {
7733                 return;
7734         }
7735
7736         if (rs.size() > 1) {
7737                 begin_reversible_command (_("mute regions"));
7738         } else {
7739                 begin_reversible_command (_("mute region"));
7740         }
7741
7742         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7743
7744                 (*i)->region()->playlist()->clear_changes ();
7745                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7746                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7747
7748         }
7749
7750         commit_reversible_command ();
7751 }
7752
7753 void
7754 Editor::combine_regions ()
7755 {
7756         /* foreach track with selected regions, take all selected regions
7757            and join them into a new region containing the subregions (as a
7758            playlist)
7759         */
7760
7761         typedef set<RouteTimeAxisView*> RTVS;
7762         RTVS tracks;
7763
7764         if (selection->regions.empty()) {
7765                 return;
7766         }
7767
7768         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7769                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7770
7771                 if (rtv) {
7772                         tracks.insert (rtv);
7773                 }
7774         }
7775
7776         begin_reversible_command (_("combine regions"));
7777
7778         vector<RegionView*> new_selection;
7779
7780         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7781                 RegionView* rv;
7782
7783                 if ((rv = (*i)->combine_regions ()) != 0) {
7784                         new_selection.push_back (rv);
7785                 }
7786         }
7787
7788         selection->clear_regions ();
7789         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7790                 selection->add (*i);
7791         }
7792
7793         commit_reversible_command ();
7794 }
7795
7796 void
7797 Editor::uncombine_regions ()
7798 {
7799         typedef set<RouteTimeAxisView*> RTVS;
7800         RTVS tracks;
7801
7802         if (selection->regions.empty()) {
7803                 return;
7804         }
7805
7806         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7807                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7808
7809                 if (rtv) {
7810                         tracks.insert (rtv);
7811                 }
7812         }
7813
7814         begin_reversible_command (_("uncombine regions"));
7815
7816         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7817                 (*i)->uncombine_regions ();
7818         }
7819
7820         commit_reversible_command ();
7821 }
7822
7823 void
7824 Editor::toggle_midi_input_active (bool flip_others)
7825 {
7826         bool onoff = false;
7827         boost::shared_ptr<RouteList> rl (new RouteList);
7828
7829         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7830                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7831
7832                 if (!rtav) {
7833                         continue;
7834                 }
7835
7836                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7837
7838                 if (mt) {
7839                         rl->push_back (rtav->route());
7840                         onoff = !mt->input_active();
7841                 }
7842         }
7843
7844         _session->set_exclusive_input_active (rl, onoff, flip_others);
7845 }
7846
7847 void
7848 Editor::lock ()
7849 {
7850         if (!lock_dialog) {
7851                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
7852
7853                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
7854                 lock_dialog->get_vbox()->pack_start (*padlock);
7855
7856                 ArdourButton* b = manage (new ArdourButton);
7857                 b->set_name ("lock button");
7858                 b->set_text (_("Click to unlock"));
7859                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
7860                 lock_dialog->get_vbox()->pack_start (*b);
7861
7862                 lock_dialog->get_vbox()->show_all ();
7863                 lock_dialog->set_size_request (200, 200);
7864         }
7865
7866         delete _main_menu_disabler;
7867         _main_menu_disabler = new MainMenuDisabler;
7868
7869         lock_dialog->present ();
7870 }
7871
7872 void
7873 Editor::unlock ()
7874 {
7875         lock_dialog->hide ();
7876
7877         delete _main_menu_disabler;
7878         _main_menu_disabler = 0;
7879
7880         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
7881                 start_lock_event_timing ();
7882         }
7883 }
7884
7885 void
7886 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7887 {
7888         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
7889 }
7890
7891 void
7892 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7893 {
7894         Timers::TimerSuspender t;
7895         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
7896         Gtkmm2ext::UI::instance()->flush_pending ();
7897 }
7898
7899 void
7900 Editor::bring_all_sources_into_session ()
7901 {
7902         if (!_session) {
7903                 return;
7904         }
7905
7906         Gtk::Label msg;
7907         ArdourDialog w (_("Moving embedded files into session folder"));
7908         w.get_vbox()->pack_start (msg);
7909         w.present ();
7910
7911         /* flush all pending GUI events because we're about to start copying
7912          * files
7913          */
7914
7915         Timers::TimerSuspender t;
7916         Gtkmm2ext::UI::instance()->flush_pending ();
7917
7918         cerr << " Do it\n";
7919
7920         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
7921 }