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