Merge branch 'export-dialog' into cairocanvas
[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         _session->add_command(new StatefulDiffCommand (playlist));
2123         commit_reversible_command ();
2124 }
2125
2126 void
2127 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
2128 {
2129         double cx, cy;
2130         RouteTimeAxisView *dest_rtv = 0;
2131         RouteTimeAxisView *source_rtv = 0;
2132
2133         GdkEvent event;
2134         event.type = GDK_BUTTON_RELEASE;
2135         event.button.x = x;
2136         event.button.y = y;
2137
2138         window_event_sample (&event, &cx, &cy);
2139
2140         std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2141         if (tv.first == 0) {
2142                 return;
2143         }
2144
2145         if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2146                 return;
2147         }
2148
2149         /* use this drag source to add underlay to a track. But we really don't care
2150            about the Route, only the view of the route, so find it first */
2151         for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2152                 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2153                         continue;
2154                 }
2155
2156                 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2157                         dest_rtv->add_underlay(source_rtv->view());
2158                         break;
2159                 }
2160         }
2161 }
2162
2163 void
2164 Editor::insert_region_list_selection (float times)
2165 {
2166         RouteTimeAxisView *tv = 0;
2167         boost::shared_ptr<Playlist> playlist;
2168
2169         if (clicked_routeview != 0) {
2170                 tv = clicked_routeview;
2171         } else if (!selection->tracks.empty()) {
2172                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2173                         return;
2174                 }
2175         } else if (entered_track != 0) {
2176                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2177                         return;
2178                 }
2179         } else {
2180                 return;
2181         }
2182
2183         if ((playlist = tv->playlist()) == 0) {
2184                 return;
2185         }
2186
2187         boost::shared_ptr<Region> region = _regions->get_single_selection ();
2188         if (region == 0) {
2189                 return;
2190         }
2191
2192         begin_reversible_command (_("insert region"));
2193         playlist->clear_changes ();
2194         playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2195         _session->add_command(new StatefulDiffCommand (playlist));
2196         commit_reversible_command ();
2197 }
2198
2199 /* BUILT-IN EFFECTS */
2200
2201 void
2202 Editor::reverse_selection ()
2203 {
2204
2205 }
2206
2207 /* GAIN ENVELOPE EDITING */
2208
2209 void
2210 Editor::edit_envelope ()
2211 {
2212 }
2213
2214 /* PLAYBACK */
2215
2216 void
2217 Editor::transition_to_rolling (bool fwd)
2218 {
2219         if (!_session) {
2220                 return;
2221         }
2222
2223         if (_session->config.get_external_sync()) {
2224                 switch (Config->get_sync_source()) {
2225                 case Engine:
2226                         break;
2227                 default:
2228                         /* transport controlled by the master */
2229                         return;
2230                 }
2231         }
2232
2233         if (_session->is_auditioning()) {
2234                 _session->cancel_audition ();
2235                 return;
2236         }
2237
2238         _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2239 }
2240
2241 void
2242 Editor::play_from_start ()
2243 {
2244         _session->request_locate (_session->current_start_frame(), true);
2245 }
2246
2247 void
2248 Editor::play_from_edit_point ()
2249 {
2250         _session->request_locate (get_preferred_edit_position(), true);
2251 }
2252
2253 void
2254 Editor::play_from_edit_point_and_return ()
2255 {
2256         framepos_t start_frame;
2257         framepos_t return_frame;
2258
2259         start_frame = get_preferred_edit_position (true);
2260
2261         if (_session->transport_rolling()) {
2262                 _session->request_locate (start_frame, false);
2263                 return;
2264         }
2265
2266         /* don't reset the return frame if its already set */
2267
2268         if ((return_frame = _session->requested_return_frame()) < 0) {
2269                 return_frame = _session->audible_frame();
2270         }
2271
2272         if (start_frame >= 0) {
2273                 _session->request_roll_at_and_return (start_frame, return_frame);
2274         }
2275 }
2276
2277 void
2278 Editor::play_selection ()
2279 {
2280         if (selection->time.empty()) {
2281                 return;
2282         }
2283
2284         _session->request_play_range (&selection->time, true);
2285 }
2286
2287 framepos_t
2288 Editor::get_preroll ()
2289 {
2290         return 1.0 /*Config->get_edit_preroll_seconds()*/ * _session->frame_rate();
2291 }
2292
2293
2294 void
2295 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2296 {
2297         if ( _session->transport_rolling() || !Config->get_always_play_range() )
2298                 return;
2299
2300         location -= get_preroll();
2301         
2302         //don't try to locate before the beginning of time
2303         if ( location < 0 ) 
2304                 location = 0;
2305                 
2306         //if follow_playhead is on, keep the playhead on the screen
2307         if ( _follow_playhead )
2308                 if ( location < leftmost_frame ) 
2309                         location = leftmost_frame;
2310
2311         _session->request_locate( location );
2312 }
2313
2314 void
2315 Editor::play_with_preroll ()
2316 {
2317         if (selection->time.empty()) {
2318                 return;
2319         } else {
2320                 framepos_t preroll = get_preroll();
2321                 
2322                 framepos_t start = 0;
2323                 if (selection->time[clicked_selection].start > preroll)
2324                         start = selection->time[clicked_selection].start - preroll;
2325                 
2326                 framepos_t end = selection->time[clicked_selection].end + preroll;
2327                 
2328                 AudioRange ar (start, end, 0);
2329                 list<AudioRange> lar;
2330                 lar.push_back (ar);
2331
2332                 _session->request_play_range (&lar, true);
2333         }
2334 }
2335
2336 void
2337 Editor::play_location (Location& location)
2338 {
2339         if (location.start() <= location.end()) {
2340                 return;
2341         }
2342
2343         _session->request_bounded_roll (location.start(), location.end());
2344 }
2345
2346 void
2347 Editor::loop_location (Location& location)
2348 {
2349         if (location.start() <= location.end()) {
2350                 return;
2351         }
2352
2353         Location* tll;
2354
2355         if ((tll = transport_loop_location()) != 0) {
2356                 tll->set (location.start(), location.end());
2357
2358                 // enable looping, reposition and start rolling
2359                 _session->request_play_loop (true);
2360                 _session->request_locate (tll->start(), true);
2361         }
2362 }
2363
2364 void
2365 Editor::do_layer_operation (LayerOperation op)
2366 {
2367         if (selection->regions.empty ()) {
2368                 return;
2369         }
2370
2371         bool const multiple = selection->regions.size() > 1;
2372         switch (op) {
2373         case Raise:
2374                 if (multiple) {
2375                         begin_reversible_command (_("raise regions"));
2376                 } else {
2377                         begin_reversible_command (_("raise region"));
2378                 }
2379                 break;
2380
2381         case RaiseToTop:
2382                 if (multiple) {
2383                         begin_reversible_command (_("raise regions to top"));
2384                 } else {
2385                         begin_reversible_command (_("raise region to top"));
2386                 }
2387                 break;
2388                 
2389         case Lower:
2390                 if (multiple) {
2391                         begin_reversible_command (_("lower regions"));
2392                 } else {
2393                         begin_reversible_command (_("lower region"));
2394                 }
2395                 break;
2396                 
2397         case LowerToBottom:
2398                 if (multiple) {
2399                         begin_reversible_command (_("lower regions to bottom"));
2400                 } else {
2401                         begin_reversible_command (_("lower region"));
2402                 }
2403                 break;
2404         }
2405
2406         set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2407         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2408                 (*i)->clear_owned_changes ();
2409         }
2410         
2411         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2412                 boost::shared_ptr<Region> r = (*i)->region ();
2413                 switch (op) {
2414                 case Raise:
2415                         r->raise ();
2416                         break;
2417                 case RaiseToTop:
2418                         r->raise_to_top ();
2419                         break;
2420                 case Lower:
2421                         r->lower ();
2422                         break;
2423                 case LowerToBottom:
2424                         r->lower_to_bottom ();
2425                 }
2426         }
2427
2428         for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2429                 vector<Command*> cmds;
2430                 (*i)->rdiff (cmds);
2431                 _session->add_commands (cmds);
2432         }
2433         
2434         commit_reversible_command ();
2435 }
2436
2437 void
2438 Editor::raise_region ()
2439 {
2440         do_layer_operation (Raise);
2441 }
2442
2443 void
2444 Editor::raise_region_to_top ()
2445 {
2446         do_layer_operation (RaiseToTop);
2447 }
2448
2449 void
2450 Editor::lower_region ()
2451 {
2452         do_layer_operation (Lower);
2453 }
2454
2455 void
2456 Editor::lower_region_to_bottom ()
2457 {
2458         do_layer_operation (LowerToBottom);
2459 }
2460
2461 /** Show the region editor for the selected regions */
2462 void
2463 Editor::show_region_properties ()
2464 {
2465         selection->foreach_regionview (&RegionView::show_region_editor);
2466 }
2467
2468 /** Show the midi list editor for the selected MIDI regions */
2469 void
2470 Editor::show_midi_list_editor ()
2471 {
2472         selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2473 }
2474
2475 void
2476 Editor::rename_region ()
2477 {
2478         RegionSelection rs = get_regions_from_selection_and_entered ();
2479
2480         if (rs.empty()) {
2481                 return;
2482         }
2483
2484         ArdourDialog d (*this, _("Rename Region"), true, false);
2485         Entry entry;
2486         Label label (_("New name:"));
2487         HBox hbox;
2488
2489         hbox.set_spacing (6);
2490         hbox.pack_start (label, false, false);
2491         hbox.pack_start (entry, true, true);
2492
2493         d.get_vbox()->set_border_width (12);
2494         d.get_vbox()->pack_start (hbox, false, false);
2495
2496         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2497         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2498
2499         d.set_size_request (300, -1);
2500
2501         entry.set_text (rs.front()->region()->name());
2502         entry.select_region (0, -1);
2503
2504         entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2505
2506         d.show_all ();
2507
2508         entry.grab_focus();
2509
2510         int const ret = d.run();
2511
2512         d.hide ();
2513
2514         if (ret != RESPONSE_OK) {
2515                 return;
2516         }
2517
2518         std::string str = entry.get_text();
2519         strip_whitespace_edges (str);
2520         if (!str.empty()) {
2521                 rs.front()->region()->set_name (str);
2522                 _regions->redisplay ();
2523         }
2524 }
2525
2526 void
2527 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2528 {
2529         if (_session->is_auditioning()) {
2530                 _session->cancel_audition ();
2531         }
2532
2533         // note: some potential for creativity here, because region doesn't
2534         // have to belong to the playlist that Route is handling
2535
2536         // bool was_soloed = route.soloed();
2537
2538         route.set_solo (true, this);
2539
2540         _session->request_bounded_roll (region->position(), region->position() + region->length());
2541
2542         /* XXX how to unset the solo state ? */
2543 }
2544
2545 /** Start an audition of the first selected region */
2546 void
2547 Editor::play_edit_range ()
2548 {
2549         framepos_t start, end;
2550
2551         if (get_edit_op_range (start, end)) {
2552                 _session->request_bounded_roll (start, end);
2553         }
2554 }
2555
2556 void
2557 Editor::play_selected_region ()
2558 {
2559         framepos_t start = max_framepos;
2560         framepos_t end = 0;
2561
2562         RegionSelection rs = get_regions_from_selection_and_entered ();
2563
2564         if (rs.empty()) {
2565                 return;
2566         }
2567
2568         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2569                 if ((*i)->region()->position() < start) {
2570                         start = (*i)->region()->position();
2571                 }
2572                 if ((*i)->region()->last_frame() + 1 > end) {
2573                         end = (*i)->region()->last_frame() + 1;
2574                 }
2575         }
2576
2577         _session->request_bounded_roll (start, end);
2578 }
2579
2580 void
2581 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2582 {
2583         _session->audition_region (region);
2584 }
2585
2586 void
2587 Editor::region_from_selection ()
2588 {
2589         if (clicked_axisview == 0) {
2590                 return;
2591         }
2592
2593         if (selection->time.empty()) {
2594                 return;
2595         }
2596
2597         framepos_t start = selection->time[clicked_selection].start;
2598         framepos_t end = selection->time[clicked_selection].end;
2599
2600         TrackViewList tracks = get_tracks_for_range_action ();
2601
2602         framepos_t selection_cnt = end - start + 1;
2603
2604         for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2605                 boost::shared_ptr<Region> current;
2606                 boost::shared_ptr<Playlist> pl;
2607                 framepos_t internal_start;
2608                 string new_name;
2609
2610                 if ((pl = (*i)->playlist()) == 0) {
2611                         continue;
2612                 }
2613
2614                 if ((current = pl->top_region_at (start)) == 0) {
2615                         continue;
2616                 }
2617
2618                 internal_start = start - current->position();
2619                 RegionFactory::region_name (new_name, current->name(), true);
2620
2621                 PropertyList plist;
2622
2623                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2624                 plist.add (ARDOUR::Properties::length, selection_cnt);
2625                 plist.add (ARDOUR::Properties::name, new_name);
2626                 plist.add (ARDOUR::Properties::layer, 0);
2627
2628                 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2629         }
2630 }
2631
2632 void
2633 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2634 {
2635         if (selection->time.empty() || selection->tracks.empty()) {
2636                 return;
2637         }
2638
2639         framepos_t start = selection->time[clicked_selection].start;
2640         framepos_t end = selection->time[clicked_selection].end;
2641
2642         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2643         sort_track_selection (ts);
2644
2645         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2646                 boost::shared_ptr<Region> current;
2647                 boost::shared_ptr<Playlist> playlist;
2648                 framepos_t internal_start;
2649                 string new_name;
2650
2651                 if ((playlist = (*i)->playlist()) == 0) {
2652                         continue;
2653                 }
2654
2655                 if ((current = playlist->top_region_at(start)) == 0) {
2656                         continue;
2657                 }
2658
2659                 internal_start = start - current->position();
2660                 RegionFactory::region_name (new_name, current->name(), true);
2661
2662                 PropertyList plist;
2663
2664                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2665                 plist.add (ARDOUR::Properties::length, end - start + 1);
2666                 plist.add (ARDOUR::Properties::name, new_name);
2667
2668                 new_regions.push_back (RegionFactory::create (current, plist));
2669         }
2670 }
2671
2672 void
2673 Editor::split_multichannel_region ()
2674 {
2675         RegionSelection rs = get_regions_from_selection_and_entered ();
2676
2677         if (rs.empty()) {
2678                 return;
2679         }
2680
2681         vector< boost::shared_ptr<Region> > v;
2682
2683         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2684                 (*x)->region()->separate_by_channel (*_session, v);
2685         }
2686 }
2687
2688 void
2689 Editor::new_region_from_selection ()
2690 {
2691         region_from_selection ();
2692         cancel_selection ();
2693 }
2694
2695 static void
2696 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2697 {
2698         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2699         case Evoral::OverlapNone:
2700                 break;
2701         default:
2702                 rs->push_back (rv);
2703         }
2704 }
2705
2706 /** Return either:
2707  *    - selected tracks, or if there are none...
2708  *    - tracks containing selected regions, or if there are none...
2709  *    - all tracks
2710  * @return tracks.
2711  */
2712 TrackViewList
2713 Editor::get_tracks_for_range_action () const
2714 {
2715         TrackViewList t;
2716
2717         if (selection->tracks.empty()) {
2718
2719                 /* use tracks with selected regions */
2720
2721                 RegionSelection rs = selection->regions;
2722
2723                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2724                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2725
2726                         if (!t.contains (tv)) {
2727                                 t.push_back (tv);
2728                         }
2729                 }
2730
2731                 if (t.empty()) {
2732                         /* no regions and no tracks: use all tracks */
2733                         t = track_views;
2734                 }
2735
2736         } else {
2737
2738                 t = selection->tracks;
2739         }
2740
2741         return t.filter_to_unique_playlists();
2742 }
2743
2744 void
2745 Editor::separate_regions_between (const TimeSelection& ts)
2746 {
2747         bool in_command = false;
2748         boost::shared_ptr<Playlist> playlist;
2749         RegionSelection new_selection;
2750
2751         TrackViewList tmptracks = get_tracks_for_range_action ();
2752         sort_track_selection (tmptracks);
2753
2754         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2755
2756                 RouteTimeAxisView* rtv;
2757
2758                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2759
2760                         if (rtv->is_track()) {
2761
2762                                 /* no edits to destructive tracks */
2763
2764                                 if (rtv->track()->destructive()) {
2765                                         continue;
2766                                 }
2767
2768                                 if ((playlist = rtv->playlist()) != 0) {
2769
2770                                         playlist->clear_changes ();
2771
2772                                         /* XXX need to consider musical time selections here at some point */
2773
2774                                         double speed = rtv->track()->speed();
2775
2776
2777                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2778
2779                                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2780                                                                 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2781
2782                                                 latest_regionviews.clear ();
2783
2784                                                 playlist->partition ((framepos_t)((*t).start * speed),
2785                                                                 (framepos_t)((*t).end * speed), false);
2786
2787                                                 c.disconnect ();
2788
2789                                                 if (!latest_regionviews.empty()) {
2790
2791                                                         rtv->view()->foreach_regionview (sigc::bind (
2792                                                                                 sigc::ptr_fun (add_if_covered),
2793                                                                                 &(*t), &new_selection));
2794
2795                                                         if (!in_command) {
2796                                                                 begin_reversible_command (_("separate"));
2797                                                                 in_command = true;
2798                                                         }
2799
2800                                                         /* pick up changes to existing regions */
2801
2802                                                         vector<Command*> cmds;
2803                                                         playlist->rdiff (cmds);
2804                                                         _session->add_commands (cmds);
2805
2806                                                         /* pick up changes to the playlist itself (adds/removes)
2807                                                          */
2808
2809                                                         _session->add_command(new StatefulDiffCommand (playlist));
2810                                                 }
2811                                         }
2812                                 }
2813                         }
2814                 }
2815         }
2816
2817         if (in_command) {
2818                 selection->set (new_selection);
2819                 set_mouse_mode (MouseObject);
2820
2821                 commit_reversible_command ();
2822         }
2823 }
2824
2825 struct PlaylistState {
2826     boost::shared_ptr<Playlist> playlist;
2827     XMLNode*  before;
2828 };
2829
2830 /** Take tracks from get_tracks_for_range_action and cut any regions
2831  *  on those tracks so that the tracks are empty over the time
2832  *  selection.
2833  */
2834 void
2835 Editor::separate_region_from_selection ()
2836 {
2837         /* preferentially use *all* ranges in the time selection if we're in range mode
2838            to allow discontiguous operation, since get_edit_op_range() currently
2839            returns a single range.
2840         */
2841
2842         if (!selection->time.empty()) {
2843
2844                 separate_regions_between (selection->time);
2845
2846         } else {
2847
2848                 framepos_t start;
2849                 framepos_t end;
2850
2851                 if (get_edit_op_range (start, end)) {
2852
2853                         AudioRange ar (start, end, 1);
2854                         TimeSelection ts;
2855                         ts.push_back (ar);
2856
2857                         separate_regions_between (ts);
2858                 }
2859         }
2860 }
2861
2862 void
2863 Editor::separate_region_from_punch ()
2864 {
2865         Location* loc  = _session->locations()->auto_punch_location();
2866         if (loc) {
2867                 separate_regions_using_location (*loc);
2868         }
2869 }
2870
2871 void
2872 Editor::separate_region_from_loop ()
2873 {
2874         Location* loc  = _session->locations()->auto_loop_location();
2875         if (loc) {
2876                 separate_regions_using_location (*loc);
2877         }
2878 }
2879
2880 void
2881 Editor::separate_regions_using_location (Location& loc)
2882 {
2883         if (loc.is_mark()) {
2884                 return;
2885         }
2886
2887         AudioRange ar (loc.start(), loc.end(), 1);
2888         TimeSelection ts;
2889
2890         ts.push_back (ar);
2891
2892         separate_regions_between (ts);
2893 }
2894
2895 /** Separate regions under the selected region */
2896 void
2897 Editor::separate_under_selected_regions ()
2898 {
2899         vector<PlaylistState> playlists;
2900
2901         RegionSelection rs;
2902
2903         rs = get_regions_from_selection_and_entered();
2904
2905         if (!_session || rs.empty()) {
2906                 return;
2907         }
2908
2909         begin_reversible_command (_("separate region under"));
2910
2911         list<boost::shared_ptr<Region> > regions_to_remove;
2912
2913         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2914                 // we can't just remove the region(s) in this loop because
2915                 // this removes them from the RegionSelection, and they thus
2916                 // disappear from underneath the iterator, and the ++i above
2917                 // SEGVs in a puzzling fashion.
2918
2919                 // so, first iterate over the regions to be removed from rs and
2920                 // add them to the regions_to_remove list, and then
2921                 // iterate over the list to actually remove them.
2922
2923                 regions_to_remove.push_back ((*i)->region());
2924         }
2925
2926         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2927
2928                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2929
2930                 if (!playlist) {
2931                         // is this check necessary?
2932                         continue;
2933                 }
2934
2935                 vector<PlaylistState>::iterator i;
2936
2937                 //only take state if this is a new playlist.
2938                 for (i = playlists.begin(); i != playlists.end(); ++i) {
2939                         if ((*i).playlist == playlist) {
2940                                 break;
2941                         }
2942                 }
2943
2944                 if (i == playlists.end()) {
2945
2946                         PlaylistState before;
2947                         before.playlist = playlist;
2948                         before.before = &playlist->get_state();
2949
2950                         playlist->freeze ();
2951                         playlists.push_back(before);
2952                 }
2953
2954                 //Partition on the region bounds
2955                 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2956
2957                 //Re-add region that was just removed due to the partition operation
2958                 playlist->add_region( (*rl), (*rl)->first_frame() );
2959         }
2960
2961         vector<PlaylistState>::iterator pl;
2962
2963         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2964                 (*pl).playlist->thaw ();
2965                 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2966         }
2967
2968         commit_reversible_command ();
2969 }
2970
2971 void
2972 Editor::crop_region_to_selection ()
2973 {
2974         if (!selection->time.empty()) {
2975
2976                 crop_region_to (selection->time.start(), selection->time.end_frame());
2977
2978         } else {
2979
2980                 framepos_t start;
2981                 framepos_t end;
2982
2983                 if (get_edit_op_range (start, end)) {
2984                         crop_region_to (start, end);
2985                 }
2986         }
2987
2988 }
2989
2990 void
2991 Editor::crop_region_to (framepos_t start, framepos_t end)
2992 {
2993         vector<boost::shared_ptr<Playlist> > playlists;
2994         boost::shared_ptr<Playlist> playlist;
2995         TrackViewList ts;
2996
2997         if (selection->tracks.empty()) {
2998                 ts = track_views.filter_to_unique_playlists();
2999         } else {
3000                 ts = selection->tracks.filter_to_unique_playlists ();
3001         }
3002
3003         sort_track_selection (ts);
3004
3005         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3006
3007                 RouteTimeAxisView* rtv;
3008
3009                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
3010
3011                         boost::shared_ptr<Track> t = rtv->track();
3012
3013                         if (t != 0 && ! t->destructive()) {
3014
3015                                 if ((playlist = rtv->playlist()) != 0) {
3016                                         playlists.push_back (playlist);
3017                                 }
3018                         }
3019                 }
3020         }
3021
3022         if (playlists.empty()) {
3023                 return;
3024         }
3025
3026         framepos_t the_start;
3027         framepos_t the_end;
3028         framepos_t cnt;
3029
3030         begin_reversible_command (_("trim to selection"));
3031
3032         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3033
3034                 boost::shared_ptr<Region> region;
3035
3036                 the_start = start;
3037
3038                 if ((region = (*i)->top_region_at(the_start)) == 0) {
3039                         continue;
3040                 }
3041
3042                 /* now adjust lengths to that we do the right thing
3043                    if the selection extends beyond the region
3044                 */
3045
3046                 the_start = max (the_start, (framepos_t) region->position());
3047                 if (max_framepos - the_start < region->length()) {
3048                         the_end = the_start + region->length() - 1;
3049                 } else {
3050                         the_end = max_framepos;
3051                 }
3052                 the_end = min (end, the_end);
3053                 cnt = the_end - the_start + 1;
3054
3055                 region->clear_changes ();
3056                 region->trim_to (the_start, cnt);
3057                 _session->add_command (new StatefulDiffCommand (region));
3058         }
3059
3060         commit_reversible_command ();
3061 }
3062
3063 void
3064 Editor::region_fill_track ()
3065 {
3066         RegionSelection rs = get_regions_from_selection_and_entered ();
3067
3068         if (!_session || rs.empty()) {
3069                 return;
3070         }
3071
3072         framepos_t const end = _session->current_end_frame ();
3073
3074         begin_reversible_command (Operations::region_fill);
3075
3076         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3077
3078                 boost::shared_ptr<Region> region ((*i)->region());
3079
3080                 boost::shared_ptr<Playlist> pl = region->playlist();
3081
3082                 if (end <= region->last_frame()) {
3083                         return;
3084                 }
3085
3086                 double times = (double) (end - region->last_frame()) / (double) region->length();
3087
3088                 if (times == 0) {
3089                         return;
3090                 }
3091
3092                 pl->clear_changes ();
3093                 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
3094                 _session->add_command (new StatefulDiffCommand (pl));
3095         }
3096
3097         commit_reversible_command ();
3098 }
3099
3100 void
3101 Editor::region_fill_selection ()
3102 {
3103         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3104                 return;
3105         }
3106
3107         if (selection->time.empty()) {
3108                 return;
3109         }
3110
3111         boost::shared_ptr<Region> region = _regions->get_single_selection ();
3112         if (region == 0) {
3113                 return;
3114         }
3115
3116         framepos_t start = selection->time[clicked_selection].start;
3117         framepos_t end = selection->time[clicked_selection].end;
3118
3119         boost::shared_ptr<Playlist> playlist;
3120
3121         if (selection->tracks.empty()) {
3122                 return;
3123         }
3124
3125         framepos_t selection_length = end - start;
3126         float times = (float)selection_length / region->length();
3127
3128         begin_reversible_command (Operations::fill_selection);
3129
3130         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3131
3132         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3133
3134                 if ((playlist = (*i)->playlist()) == 0) {
3135                         continue;
3136                 }
3137
3138                 playlist->clear_changes ();
3139                 playlist->add_region (RegionFactory::create (region, true), start, times);
3140                 _session->add_command (new StatefulDiffCommand (playlist));
3141         }
3142
3143         commit_reversible_command ();
3144 }
3145
3146 void
3147 Editor::set_region_sync_position ()
3148 {
3149         set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3150 }
3151
3152 void
3153 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3154 {
3155         bool in_command = false;
3156
3157         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3158
3159                 if (!(*r)->region()->covers (where)) {
3160                         continue;
3161                 }
3162
3163                 boost::shared_ptr<Region> region ((*r)->region());
3164
3165                 if (!in_command) {
3166                         begin_reversible_command (_("set sync point"));
3167                         in_command = true;
3168                 }
3169
3170                 region->clear_changes ();
3171                 region->set_sync_position (where);
3172                 _session->add_command(new StatefulDiffCommand (region));
3173         }
3174
3175         if (in_command) {
3176                 commit_reversible_command ();
3177         }
3178 }
3179
3180 /** Remove the sync positions of the selection */
3181 void
3182 Editor::remove_region_sync ()
3183 {
3184         RegionSelection rs = get_regions_from_selection_and_entered ();
3185
3186         if (rs.empty()) {
3187                 return;
3188         }
3189
3190         begin_reversible_command (_("remove region sync"));
3191
3192         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3193
3194                 (*i)->region()->clear_changes ();
3195                 (*i)->region()->clear_sync_position ();
3196                 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3197         }
3198
3199         commit_reversible_command ();
3200 }
3201
3202 void
3203 Editor::naturalize_region ()
3204 {
3205         RegionSelection rs = get_regions_from_selection_and_entered ();
3206
3207         if (rs.empty()) {
3208                 return;
3209         }
3210
3211         if (rs.size() > 1) {
3212                 begin_reversible_command (_("move regions to original position"));
3213         } else {
3214                 begin_reversible_command (_("move region to original position"));
3215         }
3216
3217         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3218                 (*i)->region()->clear_changes ();
3219                 (*i)->region()->move_to_natural_position ();
3220                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3221         }
3222
3223         commit_reversible_command ();
3224 }
3225
3226 void
3227 Editor::align_regions (RegionPoint what)
3228 {
3229         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3230
3231         if (rs.empty()) {
3232                 return;
3233         }
3234
3235         begin_reversible_command (_("align selection"));
3236
3237         framepos_t const position = get_preferred_edit_position ();
3238
3239         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3240                 align_region_internal ((*i)->region(), what, position);
3241         }
3242
3243         commit_reversible_command ();
3244 }
3245
3246 struct RegionSortByTime {
3247     bool operator() (const RegionView* a, const RegionView* b) {
3248             return a->region()->position() < b->region()->position();
3249     }
3250 };
3251
3252 void
3253 Editor::align_regions_relative (RegionPoint point)
3254 {
3255         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3256
3257         if (rs.empty()) {
3258                 return;
3259         }
3260
3261         framepos_t const position = get_preferred_edit_position ();
3262
3263         framepos_t distance = 0;
3264         framepos_t pos = 0;
3265         int dir = 1;
3266
3267         list<RegionView*> sorted;
3268         rs.by_position (sorted);
3269
3270         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3271
3272         switch (point) {
3273         case Start:
3274                 pos = position;
3275                 if (position > r->position()) {
3276                         distance = position - r->position();
3277                 } else {
3278                         distance = r->position() - position;
3279                         dir = -1;
3280                 }
3281                 break;
3282
3283         case End:
3284                 if (position > r->last_frame()) {
3285                         distance = position - r->last_frame();
3286                         pos = r->position() + distance;
3287                 } else {
3288                         distance = r->last_frame() - position;
3289                         pos = r->position() - distance;
3290                         dir = -1;
3291                 }
3292                 break;
3293
3294         case SyncPoint:
3295                 pos = r->adjust_to_sync (position);
3296                 if (pos > r->position()) {
3297                         distance = pos - r->position();
3298                 } else {
3299                         distance = r->position() - pos;
3300                         dir = -1;
3301                 }
3302                 break;
3303         }
3304
3305         if (pos == r->position()) {
3306                 return;
3307         }
3308
3309         begin_reversible_command (_("align selection (relative)"));
3310
3311         /* move first one specially */
3312
3313         r->clear_changes ();
3314         r->set_position (pos);
3315         _session->add_command(new StatefulDiffCommand (r));
3316
3317         /* move rest by the same amount */
3318
3319         sorted.pop_front();
3320
3321         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3322
3323                 boost::shared_ptr<Region> region ((*i)->region());
3324
3325                 region->clear_changes ();
3326
3327                 if (dir > 0) {
3328                         region->set_position (region->position() + distance);
3329                 } else {
3330                         region->set_position (region->position() - distance);
3331                 }
3332
3333                 _session->add_command(new StatefulDiffCommand (region));
3334
3335         }
3336
3337         commit_reversible_command ();
3338 }
3339
3340 void
3341 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3342 {
3343         begin_reversible_command (_("align region"));
3344         align_region_internal (region, point, position);
3345         commit_reversible_command ();
3346 }
3347
3348 void
3349 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3350 {
3351         region->clear_changes ();
3352
3353         switch (point) {
3354         case SyncPoint:
3355                 region->set_position (region->adjust_to_sync (position));
3356                 break;
3357
3358         case End:
3359                 if (position > region->length()) {
3360                         region->set_position (position - region->length());
3361                 }
3362                 break;
3363
3364         case Start:
3365                 region->set_position (position);
3366                 break;
3367         }
3368
3369         _session->add_command(new StatefulDiffCommand (region));
3370 }
3371
3372 void
3373 Editor::trim_region_front ()
3374 {
3375         trim_region (true);
3376 }
3377
3378 void
3379 Editor::trim_region_back ()
3380 {
3381         trim_region (false);
3382 }
3383
3384 void
3385 Editor::trim_region (bool front)
3386 {
3387         framepos_t where = get_preferred_edit_position();
3388         RegionSelection rs = get_regions_from_selection_and_edit_point ();
3389
3390         if (rs.empty()) {
3391                 return;
3392         }
3393
3394         begin_reversible_command (front ? _("trim front") : _("trim back"));
3395
3396         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3397                 if (!(*i)->region()->locked()) {
3398
3399                         (*i)->region()->clear_changes ();
3400
3401                         if (front) {
3402                                 (*i)->region()->trim_front (where);
3403                                 maybe_locate_with_edit_preroll ( where );
3404                         } else {
3405                                 (*i)->region()->trim_end (where);
3406                                 maybe_locate_with_edit_preroll ( where );
3407                         }
3408
3409                         _session->add_command (new StatefulDiffCommand ((*i)->region()));
3410                 }
3411         }
3412
3413         commit_reversible_command ();
3414 }
3415
3416 /** Trim the end of the selected regions to the position of the edit cursor */
3417 void
3418 Editor::trim_region_to_loop ()
3419 {
3420         Location* loc = _session->locations()->auto_loop_location();
3421         if (!loc) {
3422                 return;
3423         }
3424         trim_region_to_location (*loc, _("trim to loop"));
3425 }
3426
3427 void
3428 Editor::trim_region_to_punch ()
3429 {
3430         Location* loc = _session->locations()->auto_punch_location();
3431         if (!loc) {
3432                 return;
3433         }
3434         trim_region_to_location (*loc, _("trim to punch"));
3435 }
3436
3437 void
3438 Editor::trim_region_to_location (const Location& loc, const char* str)
3439 {
3440         RegionSelection rs = get_regions_from_selection_and_entered ();
3441
3442         begin_reversible_command (str);
3443
3444         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3445                 RegionView* rv = (*x);
3446
3447                 /* require region to span proposed trim */
3448                 switch (rv->region()->coverage (loc.start(), loc.end())) {
3449                 case Evoral::OverlapInternal:
3450                         break;
3451                 default:
3452                         continue;
3453                 }
3454
3455                 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3456                 if (!tav) {
3457                         return;
3458                 }
3459
3460                 float speed = 1.0;
3461                 framepos_t start;
3462                 framepos_t end;
3463
3464                 if (tav->track() != 0) {
3465                         speed = tav->track()->speed();
3466                 }
3467
3468                 start = session_frame_to_track_frame (loc.start(), speed);
3469                 end = session_frame_to_track_frame (loc.end(), speed);
3470
3471                 rv->region()->clear_changes ();
3472                 rv->region()->trim_to (start, (end - start));
3473                 _session->add_command(new StatefulDiffCommand (rv->region()));
3474         }
3475
3476         commit_reversible_command ();
3477 }
3478
3479 void
3480 Editor::trim_region_to_previous_region_end ()
3481 {
3482         return trim_to_region(false);
3483 }
3484
3485 void
3486 Editor::trim_region_to_next_region_start ()
3487 {
3488         return trim_to_region(true);
3489 }
3490
3491 void
3492 Editor::trim_to_region(bool forward)
3493 {
3494         RegionSelection rs = get_regions_from_selection_and_entered ();
3495
3496         begin_reversible_command (_("trim to region"));
3497
3498         boost::shared_ptr<Region> next_region;
3499
3500         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3501
3502                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3503
3504                 if (!arv) {
3505                         continue;
3506                 }
3507
3508                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3509
3510                 if (!atav) {
3511                         return;
3512                 }
3513
3514                 float speed = 1.0;
3515
3516                 if (atav->track() != 0) {
3517                         speed = atav->track()->speed();
3518                 }
3519
3520
3521                 boost::shared_ptr<Region> region = arv->region();
3522                 boost::shared_ptr<Playlist> playlist (region->playlist());
3523
3524                 region->clear_changes ();
3525
3526                 if (forward) {
3527
3528                     next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3529
3530                     if (!next_region) {
3531                         continue;
3532                     }
3533
3534                     region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3535                     arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3536                 }
3537                 else {
3538
3539                     next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3540
3541                     if(!next_region){
3542                         continue;
3543                     }
3544
3545                     region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3546
3547                     arv->region_changed (ARDOUR::bounds_change);
3548                 }
3549
3550                 _session->add_command(new StatefulDiffCommand (region));
3551         }
3552
3553         commit_reversible_command ();
3554 }
3555
3556 void
3557 Editor::unfreeze_route ()
3558 {
3559         if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3560                 return;
3561         }
3562
3563         clicked_routeview->track()->unfreeze ();
3564 }
3565
3566 void*
3567 Editor::_freeze_thread (void* arg)
3568 {
3569         return static_cast<Editor*>(arg)->freeze_thread ();
3570 }
3571
3572 void*
3573 Editor::freeze_thread ()
3574 {
3575         /* create event pool because we may need to talk to the session */
3576         SessionEvent::create_per_thread_pool ("freeze events", 64);
3577         /* create per-thread buffers for process() tree to use */
3578         current_interthread_info->process_thread.get_buffers ();
3579         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3580         current_interthread_info->done = true;
3581         current_interthread_info->process_thread.drop_buffers();
3582         return 0;
3583 }
3584
3585 void
3586 Editor::freeze_route ()
3587 {
3588         if (!_session) {
3589                 return;
3590         }
3591
3592         /* stop transport before we start. this is important */
3593
3594         _session->request_transport_speed (0.0);
3595         
3596         /* wait for just a little while, because the above call is asynchronous */
3597
3598         Glib::usleep (250000);
3599
3600         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3601                 return;
3602         }
3603
3604         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3605                 MessageDialog d (
3606                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3607                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3608                         );
3609                 d.set_title (_("Cannot freeze"));
3610                 d.run ();
3611                 return;
3612         }
3613
3614         if (clicked_routeview->track()->has_external_redirects()) {
3615                 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"
3616                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3617                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3618
3619                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3620                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3621                 d.set_title (_("Freeze Limits"));
3622
3623                 int response = d.run ();
3624
3625                 switch (response) {
3626                 case Gtk::RESPONSE_CANCEL:
3627                         return;
3628                 default:
3629                         break;
3630                 }
3631         }
3632
3633         InterThreadInfo itt;
3634         current_interthread_info = &itt;
3635
3636         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3637
3638         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3639
3640         set_canvas_cursor (_cursors->wait);
3641
3642         while (!itt.done && !itt.cancel) {
3643                 gtk_main_iteration ();
3644         }
3645
3646         current_interthread_info = 0;
3647         set_canvas_cursor (current_canvas_cursor);
3648 }
3649
3650 void
3651 Editor::bounce_range_selection (bool replace, bool enable_processing)
3652 {
3653         if (selection->time.empty()) {
3654                 return;
3655         }
3656
3657         TrackSelection views = selection->tracks;
3658
3659         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3660
3661                 if (enable_processing) {
3662
3663                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3664
3665                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3666                                 MessageDialog d (
3667                                         _("You can't perform this operation because the processing of the signal "
3668                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3669                                           "You can do this without processing, which is a different operation.")
3670                                         );
3671                                 d.set_title (_("Cannot bounce"));
3672                                 d.run ();
3673                                 return;
3674                         }
3675                 }
3676         }
3677
3678         framepos_t start = selection->time[clicked_selection].start;
3679         framepos_t end = selection->time[clicked_selection].end;
3680         framepos_t cnt = end - start + 1;
3681
3682         begin_reversible_command (_("bounce range"));
3683
3684         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3685
3686                 RouteTimeAxisView* rtv;
3687
3688                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3689                         continue;
3690                 }
3691
3692                 boost::shared_ptr<Playlist> playlist;
3693
3694                 if ((playlist = rtv->playlist()) == 0) {
3695                         return;
3696                 }
3697
3698                 InterThreadInfo itt;
3699
3700                 playlist->clear_changes ();
3701                 playlist->clear_owned_changes ();
3702
3703                 boost::shared_ptr<Region> r;
3704
3705                 if (enable_processing) {
3706                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3707                 } else {
3708                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3709                 }
3710
3711                 if (!r) {
3712                         continue;
3713                 }
3714
3715                 if (replace) {
3716                         list<AudioRange> ranges;
3717                         ranges.push_back (AudioRange (start, start+cnt, 0));
3718                         playlist->cut (ranges); // discard result
3719                         playlist->add_region (r, start);
3720                 }
3721
3722                 vector<Command*> cmds;
3723                 playlist->rdiff (cmds);
3724                 _session->add_commands (cmds);
3725
3726                 _session->add_command (new StatefulDiffCommand (playlist));
3727         }
3728
3729         commit_reversible_command ();
3730 }
3731
3732 /** Delete selected regions, automation points or a time range */
3733 void
3734 Editor::delete_ ()
3735 {
3736         cut_copy (Delete);
3737 }
3738
3739 /** Cut selected regions, automation points or a time range */
3740 void
3741 Editor::cut ()
3742 {
3743         cut_copy (Cut);
3744 }
3745
3746 /** Copy selected regions, automation points or a time range */
3747 void
3748 Editor::copy ()
3749 {
3750         cut_copy (Copy);
3751 }
3752
3753
3754 /** @return true if a Cut, Copy or Clear is possible */
3755 bool
3756 Editor::can_cut_copy () const
3757 {
3758         switch (effective_mouse_mode()) {
3759
3760         case MouseObject:
3761                 if (!selection->regions.empty() || !selection->points.empty()) {
3762                         return true;
3763                 }
3764                 break;
3765
3766         case MouseRange:
3767                 if (!selection->time.empty()) {
3768                         return true;
3769                 }
3770                 break;
3771
3772         default:
3773                 break;
3774         }
3775
3776         return false;
3777 }
3778
3779
3780 /** Cut, copy or clear selected regions, automation points or a time range.
3781  * @param op Operation (Cut, Copy or Clear)
3782  */
3783 void
3784 Editor::cut_copy (CutCopyOp op)
3785 {
3786         /* only cancel selection if cut/copy is successful.*/
3787
3788         string opname;
3789
3790         switch (op) {
3791         case Delete:
3792                 opname = _("delete");
3793                 break;
3794         case Cut:
3795                 opname = _("cut");
3796                 break;
3797         case Copy:
3798                 opname = _("copy");
3799                 break;
3800         case Clear:
3801                 opname = _("clear");
3802                 break;
3803         }
3804
3805         /* if we're deleting something, and the mouse is still pressed,
3806            the thing we started a drag for will be gone when we release
3807            the mouse button(s). avoid this. see part 2 at the end of
3808            this function.
3809         */
3810
3811         if (op == Delete || op == Cut || op == Clear) {
3812                 if (_drags->active ()) {
3813                         _drags->abort ();
3814                 }
3815         }
3816
3817         if ( op != Clear )  //"Delete" doesn't change copy/paste buf
3818                 cut_buffer->clear ();
3819
3820         if (entered_marker) {
3821
3822                 /* cut/delete op while pointing at a marker */
3823
3824                 bool ignored;
3825                 Location* loc = find_location_from_marker (entered_marker, ignored);
3826
3827                 if (_session && loc) {
3828                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3829                 }
3830
3831                 _drags->abort ();
3832                 return;
3833         }
3834
3835         if (internal_editing()) {
3836
3837                 switch (effective_mouse_mode()) {
3838                 case MouseObject:
3839                 case MouseRange:
3840                         begin_reversible_command (opname + ' ' + X_("MIDI"));
3841                         cut_copy_midi (op);
3842                         commit_reversible_command ();
3843                         break;
3844                 default:
3845                         break;
3846                 }
3847
3848                 return;
3849         }
3850
3851         bool did_edit = false;
3852
3853         switch (effective_mouse_mode()) {
3854         case MouseGain:
3855                 if (!selection->points.empty()) {
3856                         begin_reversible_command (opname + _(" points"));
3857                         did_edit = true;
3858                         cut_copy_points (op);
3859                         if (op == Cut || op == Delete) {
3860                                 selection->clear_points ();
3861                         }
3862                 }
3863                 break;
3864                 
3865         case MouseObject: 
3866
3867                 if (!selection->regions.empty() || !selection->points.empty()) {
3868
3869                         string thing_name;
3870
3871                         if (selection->regions.empty()) {
3872                                 thing_name = _("points");
3873                         } else if (selection->points.empty()) {
3874                                 thing_name = _("regions");
3875                         } else {
3876                                 thing_name = _("objects");
3877                         }
3878                 
3879                         begin_reversible_command (opname + ' ' + thing_name);
3880                         did_edit = true;
3881
3882                         if (!selection->regions.empty()) {
3883                                 cut_copy_regions (op, selection->regions);
3884                                 
3885                                 if (op == Cut || op == Delete) {
3886                                         selection->clear_regions ();
3887                                 }
3888                         }
3889                         
3890                         if (!selection->points.empty()) {
3891                                 cut_copy_points (op);
3892                                 
3893                                 if (op == Cut || op == Delete) {
3894                                         selection->clear_points ();
3895                                 }
3896                         }
3897                 } 
3898                 break;
3899                         
3900         case MouseRange:
3901                 if (selection->time.empty()) {
3902                         framepos_t start, end;
3903                         /* no time selection, see if we can get an edit range
3904                            and use that.
3905                         */
3906                         if (get_edit_op_range (start, end)) {
3907                                 selection->set (start, end);
3908                         }
3909                 }
3910                 if (!selection->time.empty()) {
3911                         begin_reversible_command (opname + _(" range"));
3912
3913                         did_edit = true;
3914                         cut_copy_ranges (op);
3915                         
3916                         if (op == Cut || op == Delete) {
3917                                 selection->clear_time ();
3918                         }
3919                 }
3920                 break;
3921                 
3922         default:
3923                 break;
3924         }
3925         
3926         if (did_edit) {
3927                 commit_reversible_command ();   
3928         }
3929         
3930         if (op == Delete || op == Cut || op == Clear) {
3931                 _drags->abort ();
3932         }
3933 }
3934
3935 struct AutomationRecord {
3936         AutomationRecord () : state (0) {}
3937         AutomationRecord (XMLNode* s) : state (s) {}
3938         
3939         XMLNode* state; ///< state before any operation
3940         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3941 };
3942
3943 /** Cut, copy or clear selected automation points.
3944  *  @param op Operation (Cut, Copy or Clear)
3945  */
3946 void
3947 Editor::cut_copy_points (CutCopyOp op)
3948 {
3949         if (selection->points.empty ()) {
3950                 return;
3951         }
3952
3953         /* XXX: not ideal, as there may be more than one track involved in the point selection */
3954         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3955
3956         /* Keep a record of the AutomationLists that we end up using in this operation */
3957         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3958         Lists lists;
3959
3960         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3961         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3962                 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3963                 if (lists.find (al) == lists.end ()) {
3964                         /* We haven't seen this list yet, so make a record for it.  This includes
3965                            taking a copy of its current state, in case this is needed for undo later.
3966                         */
3967                         lists[al] = AutomationRecord (&al->get_state ());
3968                 }
3969         }
3970
3971         if (op == Cut || op == Copy) {
3972                 /* This operation will involve putting things in the cut buffer, so create an empty
3973                    ControlList for each of our source lists to put the cut buffer data in.
3974                 */
3975                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3976                         i->second.copy = i->first->create (i->first->parameter ());
3977                 }
3978
3979                 /* Add all selected points to the relevant copy ControlLists */
3980                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3981                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3982                         AutomationList::const_iterator j = (*i)->model ();
3983                         lists[al].copy->add ((*j)->when, (*j)->value);
3984                 }
3985
3986                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3987                         /* Correct this copy list so that it starts at time 0 */
3988                         double const start = i->second.copy->front()->when;
3989                         for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3990                                 (*j)->when -= start;
3991                         }
3992
3993                         /* And add it to the cut buffer */
3994                         cut_buffer->add (i->second.copy);
3995                 }
3996         }
3997                 
3998         if (op == Delete || op == Cut) {
3999                 /* This operation needs to remove things from the main AutomationList, so do that now */
4000                 
4001                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4002                         i->first->freeze ();
4003                 }
4004
4005                 /* Remove each selected point from its AutomationList */
4006                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4007                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
4008                         al->erase ((*i)->model ());
4009                 }
4010
4011                 /* Thaw the lists and add undo records for them */
4012                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4013                         boost::shared_ptr<AutomationList> al = i->first;
4014                         al->thaw ();
4015                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4016                 }
4017         }
4018 }
4019
4020 /** Cut, copy or clear selected automation points.
4021  * @param op Operation (Cut, Copy or Clear)
4022  */
4023 void
4024 Editor::cut_copy_midi (CutCopyOp op)
4025 {
4026         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4027                 MidiRegionView* mrv = *i;
4028                 mrv->cut_copy_clear (op);
4029         }
4030 }
4031
4032
4033
4034 struct lt_playlist {
4035     bool operator () (const PlaylistState& a, const PlaylistState& b) {
4036             return a.playlist < b.playlist;
4037     }
4038 };
4039
4040 struct PlaylistMapping {
4041     TimeAxisView* tv;
4042     boost::shared_ptr<Playlist> pl;
4043
4044     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4045 };
4046
4047 /** Remove `clicked_regionview' */
4048 void
4049 Editor::remove_clicked_region ()
4050 {
4051         if (clicked_routeview == 0 || clicked_regionview == 0) {
4052                 return;
4053         }
4054
4055         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4056
4057         begin_reversible_command (_("remove region"));
4058         playlist->clear_changes ();
4059         playlist->clear_owned_changes ();
4060         playlist->remove_region (clicked_regionview->region());
4061
4062         /* We might have removed regions, which alters other regions' layering_index,
4063            so we need to do a recursive diff here.
4064         */
4065         vector<Command*> cmds;
4066         playlist->rdiff (cmds);
4067         _session->add_commands (cmds);
4068         
4069         _session->add_command(new StatefulDiffCommand (playlist));
4070         commit_reversible_command ();
4071 }
4072
4073
4074 /** Remove the selected regions */
4075 void
4076 Editor::remove_selected_regions ()
4077 {
4078         RegionSelection rs = get_regions_from_selection_and_entered ();
4079
4080         if (!_session || rs.empty()) {
4081                 return;
4082         }
4083
4084         begin_reversible_command (_("remove region"));
4085
4086         list<boost::shared_ptr<Region> > regions_to_remove;
4087
4088         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4089                 // we can't just remove the region(s) in this loop because
4090                 // this removes them from the RegionSelection, and they thus
4091                 // disappear from underneath the iterator, and the ++i above
4092                 // SEGVs in a puzzling fashion.
4093
4094                 // so, first iterate over the regions to be removed from rs and
4095                 // add them to the regions_to_remove list, and then
4096                 // iterate over the list to actually remove them.
4097
4098                 regions_to_remove.push_back ((*i)->region());
4099         }
4100
4101         vector<boost::shared_ptr<Playlist> > playlists;
4102
4103         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4104
4105                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4106
4107                 if (!playlist) {
4108                         // is this check necessary?
4109                         continue;
4110                 }
4111
4112                 /* get_regions_from_selection_and_entered() guarantees that
4113                    the playlists involved are unique, so there is no need
4114                    to check here.
4115                 */
4116
4117                 playlists.push_back (playlist);
4118
4119                 playlist->clear_changes ();
4120                 playlist->clear_owned_changes ();
4121                 playlist->freeze ();
4122                 playlist->remove_region (*rl);
4123         }
4124
4125         vector<boost::shared_ptr<Playlist> >::iterator pl;
4126
4127         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4128                 (*pl)->thaw ();
4129
4130                 /* We might have removed regions, which alters other regions' layering_index,
4131                    so we need to do a recursive diff here.
4132                 */
4133                 vector<Command*> cmds;
4134                 (*pl)->rdiff (cmds);
4135                 _session->add_commands (cmds);
4136                 
4137                 _session->add_command(new StatefulDiffCommand (*pl));
4138         }
4139
4140         commit_reversible_command ();
4141 }
4142
4143 /** Cut, copy or clear selected regions.
4144  * @param op Operation (Cut, Copy or Clear)
4145  */
4146 void
4147 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4148 {
4149         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4150            a map when we want ordered access to both elements. i think.
4151         */
4152
4153         vector<PlaylistMapping> pmap;
4154
4155         framepos_t first_position = max_framepos;
4156
4157         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4158         FreezeList freezelist;
4159
4160         /* get ordering correct before we cut/copy */
4161
4162         rs.sort_by_position_and_track ();
4163
4164         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4165
4166                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4167
4168                 if (op == Cut || op == Clear || op == Delete) {
4169                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4170
4171                         if (pl) {
4172                                 FreezeList::iterator fl;
4173
4174                                 // only take state if this is a new playlist.
4175                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4176                                         if ((*fl) == pl) {
4177                                                 break;
4178                                         }
4179                                 }
4180
4181                                 if (fl == freezelist.end()) {
4182                                         pl->clear_changes();
4183                                         pl->clear_owned_changes ();
4184                                         pl->freeze ();
4185                                         freezelist.insert (pl);
4186                                 }
4187                         }
4188                 }
4189
4190                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4191                 vector<PlaylistMapping>::iterator z;
4192
4193                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4194                         if ((*z).tv == tv) {
4195                                 break;
4196                         }
4197                 }
4198
4199                 if (z == pmap.end()) {
4200                         pmap.push_back (PlaylistMapping (tv));
4201                 }
4202         }
4203
4204         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4205
4206                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4207
4208                 if (!pl) {
4209                         /* region not yet associated with a playlist (e.g. unfinished
4210                            capture pass.
4211                         */
4212                         ++x;
4213                         continue;
4214                 }
4215
4216                 TimeAxisView& tv = (*x)->get_time_axis_view();
4217                 boost::shared_ptr<Playlist> npl;
4218                 RegionSelection::iterator tmp;
4219
4220                 tmp = x;
4221                 ++tmp;
4222
4223                 if (op != Delete) {
4224
4225                         vector<PlaylistMapping>::iterator z;
4226                         
4227                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4228                                 if ((*z).tv == &tv) {
4229                                         break;
4230                                 }
4231                         }
4232                         
4233                         assert (z != pmap.end());
4234                         
4235                         if (!(*z).pl) {
4236                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4237                                 npl->freeze();
4238                                 (*z).pl = npl;
4239                         } else {
4240                                 npl = (*z).pl;
4241                         }
4242                 }
4243
4244                 boost::shared_ptr<Region> r = (*x)->region();
4245                 boost::shared_ptr<Region> _xx;
4246
4247                 assert (r != 0);
4248
4249                 switch (op) {
4250                 case Delete:
4251                         pl->remove_region (r);
4252                         break;
4253                         
4254                 case Cut:
4255                         _xx = RegionFactory::create (r);
4256                         npl->add_region (_xx, r->position() - first_position);
4257                         pl->remove_region (r);
4258                         break;
4259
4260                 case Copy:
4261                         /* copy region before adding, so we're not putting same object into two different playlists */
4262                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4263                         break;
4264
4265                 case Clear:
4266                         pl->remove_region (r);  
4267                         break;
4268                 }
4269
4270                 x = tmp;
4271         }
4272
4273         if (op != Delete) {
4274
4275                 list<boost::shared_ptr<Playlist> > foo;
4276                 
4277                 /* the pmap is in the same order as the tracks in which selected regions occured */
4278                 
4279                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4280                         if ((*i).pl) {
4281                                 (*i).pl->thaw();
4282                                 foo.push_back ((*i).pl);
4283                         }
4284                 }
4285                 
4286                 if (!foo.empty()) {
4287                         cut_buffer->set (foo);
4288                 }
4289                 
4290                 if (pmap.empty()) {
4291                         _last_cut_copy_source_track = 0;
4292                 } else {
4293                         _last_cut_copy_source_track = pmap.front().tv;
4294                 }
4295         }
4296
4297         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4298                 (*pl)->thaw ();
4299
4300                 /* We might have removed regions, which alters other regions' layering_index,
4301                    so we need to do a recursive diff here.
4302                 */
4303                 vector<Command*> cmds;
4304                 (*pl)->rdiff (cmds);
4305                 _session->add_commands (cmds);
4306                 
4307                 _session->add_command (new StatefulDiffCommand (*pl));
4308         }
4309 }
4310
4311 void
4312 Editor::cut_copy_ranges (CutCopyOp op)
4313 {
4314         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4315
4316         /* Sort the track selection now, so that it if is used, the playlists
4317            selected by the calls below to cut_copy_clear are in the order that
4318            their tracks appear in the editor.  This makes things like paste
4319            of ranges work properly.
4320         */
4321
4322         sort_track_selection (ts);
4323
4324         if (ts.empty()) {
4325                 if (!entered_track) {
4326                         return;
4327                 }
4328                 ts.push_back (entered_track);
4329         } 
4330
4331         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4332                 (*i)->cut_copy_clear (*selection, op);
4333         }
4334 }
4335
4336 void
4337 Editor::paste (float times, bool from_context)
4338 {
4339         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4340
4341         paste_internal (get_preferred_edit_position (false, from_context), times);
4342 }
4343
4344 void
4345 Editor::mouse_paste ()
4346 {
4347         framepos_t where;
4348         bool ignored;
4349
4350         if (!mouse_frame (where, ignored)) {
4351                 return;
4352         }
4353
4354         snap_to (where);
4355         paste_internal (where, 1);
4356 }
4357
4358 void
4359 Editor::paste_internal (framepos_t position, float times)
4360 {
4361         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4362
4363         if (internal_editing()) {
4364                 if (cut_buffer->midi_notes.empty()) {
4365                         return;
4366                 }
4367         } else {
4368                 if (cut_buffer->empty()) {
4369                         return;
4370                 }
4371         }
4372
4373         if (position == max_framepos) {
4374                 position = get_preferred_edit_position();
4375                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4376         }
4377
4378         TrackViewList ts;
4379         TrackViewList::iterator i;
4380         size_t nth;
4381
4382         /* get everything in the correct order */
4383
4384         if (_edit_point == Editing::EditAtMouse && entered_track) {
4385                 /* With the mouse edit point, paste onto the track under the mouse */
4386                 ts.push_back (entered_track);
4387         } else if (!selection->tracks.empty()) {
4388                 /* Otherwise, if there are some selected tracks, paste to them */
4389                 ts = selection->tracks.filter_to_unique_playlists ();
4390                 sort_track_selection (ts);
4391         } else if (_last_cut_copy_source_track) {
4392                 /* Otherwise paste to the track that the cut/copy came from;
4393                    see discussion in mantis #3333.
4394                 */
4395                 ts.push_back (_last_cut_copy_source_track);
4396         }
4397
4398         if (internal_editing ()) {
4399
4400                 /* undo/redo is handled by individual tracks/regions */
4401
4402                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4403
4404                         RegionSelection rs;
4405                         RegionSelection::iterator r;
4406                         MidiNoteSelection::iterator cb;
4407
4408                         get_regions_at (rs, position, ts);
4409
4410                         for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4411                              cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4412                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4413                                 if (mrv) {
4414                                         mrv->paste (position, times, **cb);
4415                                         ++cb;
4416                                 }
4417                         }
4418                 }
4419
4420         } else {
4421
4422                 /* we do redo (do you do voodoo?) */
4423
4424                 begin_reversible_command (Operations::paste);
4425
4426                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4427                         (*i)->paste (position, times, *cut_buffer, nth);
4428                 }
4429
4430                 commit_reversible_command ();
4431         }
4432 }
4433
4434 void
4435 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4436 {
4437         boost::shared_ptr<Playlist> playlist;
4438         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4439         RegionSelection foo;
4440
4441         framepos_t const start_frame = regions.start ();
4442         framepos_t const end_frame = regions.end_frame ();
4443
4444         begin_reversible_command (Operations::duplicate_region);
4445
4446         selection->clear_regions ();
4447
4448         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4449
4450                 boost::shared_ptr<Region> r ((*i)->region());
4451
4452                 TimeAxisView& tv = (*i)->get_time_axis_view();
4453                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4454                 latest_regionviews.clear ();
4455                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4456
4457                 playlist = (*i)->region()->playlist();
4458                 playlist->clear_changes ();
4459                 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4460                 _session->add_command(new StatefulDiffCommand (playlist));
4461
4462                 c.disconnect ();
4463
4464                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4465         }
4466
4467         commit_reversible_command ();
4468
4469         if (!foo.empty()) {
4470                 selection->set (foo);
4471         }
4472 }
4473
4474 void
4475 Editor::duplicate_selection (float times)
4476 {
4477         if (selection->time.empty() || selection->tracks.empty()) {
4478                 return;
4479         }
4480
4481         boost::shared_ptr<Playlist> playlist;
4482         vector<boost::shared_ptr<Region> > new_regions;
4483         vector<boost::shared_ptr<Region> >::iterator ri;
4484
4485         create_region_from_selection (new_regions);
4486
4487         if (new_regions.empty()) {
4488                 return;
4489         }
4490
4491         begin_reversible_command (_("duplicate selection"));
4492
4493         ri = new_regions.begin();
4494
4495         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4496
4497         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4498                 if ((playlist = (*i)->playlist()) == 0) {
4499                         continue;
4500                 }
4501                 playlist->clear_changes ();
4502                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4503                 _session->add_command (new StatefulDiffCommand (playlist));
4504
4505                 ++ri;
4506                 if (ri == new_regions.end()) {
4507                         --ri;
4508                 }
4509         }
4510
4511         commit_reversible_command ();
4512 }
4513
4514 /** Reset all selected points to the relevant default value */
4515 void
4516 Editor::reset_point_selection ()
4517 {
4518         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4519                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4520                 (*j)->value = (*i)->line().the_list()->default_value ();
4521         }
4522 }
4523
4524 void
4525 Editor::center_playhead ()
4526 {
4527         float const page = _visible_canvas_width * samples_per_pixel;
4528         center_screen_internal (playhead_cursor->current_frame (), page);
4529 }
4530
4531 void
4532 Editor::center_edit_point ()
4533 {
4534         float const page = _visible_canvas_width * samples_per_pixel;
4535         center_screen_internal (get_preferred_edit_position(), page);
4536 }
4537
4538 /** Caller must begin and commit a reversible command */
4539 void
4540 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4541 {
4542         playlist->clear_changes ();
4543         playlist->clear ();
4544         _session->add_command (new StatefulDiffCommand (playlist));
4545 }
4546
4547 void
4548 Editor::nudge_track (bool use_edit, bool forwards)
4549 {
4550         boost::shared_ptr<Playlist> playlist;
4551         framepos_t distance;
4552         framepos_t next_distance;
4553         framepos_t start;
4554
4555         if (use_edit) {
4556                 start = get_preferred_edit_position();
4557         } else {
4558                 start = 0;
4559         }
4560
4561         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4562                 return;
4563         }
4564
4565         if (selection->tracks.empty()) {
4566                 return;
4567         }
4568
4569         begin_reversible_command (_("nudge track"));
4570
4571         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4572
4573         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4574
4575                 if ((playlist = (*i)->playlist()) == 0) {
4576                         continue;
4577                 }
4578
4579                 playlist->clear_changes ();
4580                 playlist->clear_owned_changes ();
4581
4582                 playlist->nudge_after (start, distance, forwards);
4583
4584                 vector<Command*> cmds;
4585
4586                 playlist->rdiff (cmds);
4587                 _session->add_commands (cmds);
4588
4589                 _session->add_command (new StatefulDiffCommand (playlist));
4590         }
4591
4592         commit_reversible_command ();
4593 }
4594
4595 void
4596 Editor::remove_last_capture ()
4597 {
4598         vector<string> choices;
4599         string prompt;
4600
4601         if (!_session) {
4602                 return;
4603         }
4604
4605         if (Config->get_verify_remove_last_capture()) {
4606                 prompt  = _("Do you really want to destroy the last capture?"
4607                             "\n(This is destructive and cannot be undone)");
4608
4609                 choices.push_back (_("No, do nothing."));
4610                 choices.push_back (_("Yes, destroy it."));
4611
4612                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4613
4614                 if (prompter.run () == 1) {
4615                         _session->remove_last_capture ();
4616                         _regions->redisplay ();
4617                 }
4618
4619         } else {
4620                 _session->remove_last_capture();
4621                 _regions->redisplay ();
4622         }
4623 }
4624
4625 void
4626 Editor::normalize_region ()
4627 {
4628         if (!_session) {
4629                 return;
4630         }
4631
4632         RegionSelection rs = get_regions_from_selection_and_entered ();
4633
4634         if (rs.empty()) {
4635                 return;
4636         }
4637
4638         NormalizeDialog dialog (rs.size() > 1);
4639
4640         if (dialog.run () == RESPONSE_CANCEL) {
4641                 return;
4642         }
4643
4644         set_canvas_cursor (_cursors->wait);
4645         gdk_flush ();
4646
4647         /* XXX: should really only count audio regions here */
4648         int const regions = rs.size ();
4649
4650         /* Make a list of the selected audio regions' maximum amplitudes, and also
4651            obtain the maximum amplitude of them all.
4652         */
4653         list<double> max_amps;
4654         double max_amp = 0;
4655         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4656                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4657                 if (arv) {
4658                         dialog.descend (1.0 / regions);
4659                         double const a = arv->audio_region()->maximum_amplitude (&dialog);
4660
4661                         if (a == -1) {
4662                                 /* the user cancelled the operation */
4663                                 set_canvas_cursor (current_canvas_cursor);
4664                                 return;
4665                         }
4666
4667                         max_amps.push_back (a);
4668                         max_amp = max (max_amp, a);
4669                         dialog.ascend ();
4670                 }
4671         }
4672
4673         begin_reversible_command (_("normalize"));
4674
4675         list<double>::const_iterator a = max_amps.begin ();
4676
4677         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4678                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4679                 if (!arv) {
4680                         continue;
4681                 }
4682
4683                 arv->region()->clear_changes ();
4684
4685                 double const amp = dialog.normalize_individually() ? *a : max_amp;
4686
4687                 arv->audio_region()->normalize (amp, dialog.target ());
4688                 _session->add_command (new StatefulDiffCommand (arv->region()));
4689
4690                 ++a;
4691         }
4692
4693         commit_reversible_command ();
4694         set_canvas_cursor (current_canvas_cursor);
4695 }
4696
4697
4698 void
4699 Editor::reset_region_scale_amplitude ()
4700 {
4701         if (!_session) {
4702                 return;
4703         }
4704
4705         RegionSelection rs = get_regions_from_selection_and_entered ();
4706
4707         if (rs.empty()) {
4708                 return;
4709         }
4710
4711         begin_reversible_command ("reset gain");
4712
4713         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4714                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4715                 if (!arv)
4716                         continue;
4717                 arv->region()->clear_changes ();
4718                 arv->audio_region()->set_scale_amplitude (1.0f);
4719                 _session->add_command (new StatefulDiffCommand (arv->region()));
4720         }
4721
4722         commit_reversible_command ();
4723 }
4724
4725 void
4726 Editor::adjust_region_gain (bool up)
4727 {
4728         RegionSelection rs = get_regions_from_selection_and_entered ();
4729
4730         if (!_session || rs.empty()) {
4731                 return;
4732         }
4733
4734         begin_reversible_command ("adjust region gain");
4735
4736         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4737                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4738                 if (!arv) {
4739                         continue;
4740                 }
4741
4742                 arv->region()->clear_changes ();
4743
4744                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4745
4746                 if (up) {
4747                         dB += 1;
4748                 } else {
4749                         dB -= 1;
4750                 }
4751
4752                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4753                 _session->add_command (new StatefulDiffCommand (arv->region()));
4754         }
4755
4756         commit_reversible_command ();
4757 }
4758
4759
4760 void
4761 Editor::reverse_region ()
4762 {
4763         if (!_session) {
4764                 return;
4765         }
4766
4767         Reverse rev (*_session);
4768         apply_filter (rev, _("reverse regions"));
4769 }
4770
4771 void
4772 Editor::strip_region_silence ()
4773 {
4774         if (!_session) {
4775                 return;
4776         }
4777
4778         RegionSelection rs = get_regions_from_selection_and_entered ();
4779
4780         if (rs.empty()) {
4781                 return;
4782         }
4783
4784         std::list<RegionView*> audio_only;
4785
4786         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4787                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4788                 if (arv) {
4789                         audio_only.push_back (arv);
4790                 }
4791         }
4792
4793         StripSilenceDialog d (_session, audio_only);
4794         int const r = d.run ();
4795
4796         d.drop_rects ();
4797
4798         if (r == Gtk::RESPONSE_OK) {
4799                 ARDOUR::AudioIntervalMap silences;
4800                 d.silences (silences);
4801                 StripSilence s (*_session, silences, d.fade_length());
4802                 apply_filter (s, _("strip silence"), &d);
4803         }
4804 }
4805
4806 Command*
4807 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4808 {
4809         Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4810         mrv.selection_as_notelist (selected, true);
4811
4812         vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4813         v.push_back (selected);
4814
4815         framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
4816         double     pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4817
4818         return op (mrv.midi_region()->model(), pos_beats, v);
4819 }
4820
4821 void
4822 Editor::apply_midi_note_edit_op (MidiOperator& op)
4823 {
4824         Command* cmd;
4825
4826         RegionSelection rs = get_regions_from_selection_and_entered ();
4827
4828         if (rs.empty()) {
4829                 return;
4830         }
4831
4832         begin_reversible_command (op.name ());
4833
4834         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4835                 RegionSelection::iterator tmp = r;
4836                 ++tmp;
4837
4838                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4839
4840                 if (mrv) {
4841                         cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4842                         if (cmd) {
4843                                 (*cmd)();
4844                                 _session->add_command (cmd);
4845                         }
4846                 }
4847
4848                 r = tmp;
4849         }
4850
4851         commit_reversible_command ();
4852 }
4853
4854 void
4855 Editor::fork_region ()
4856 {
4857         RegionSelection rs = get_regions_from_selection_and_entered ();
4858
4859         if (rs.empty()) {
4860                 return;
4861         }
4862
4863         begin_reversible_command (_("Fork Region(s)"));
4864
4865         set_canvas_cursor (_cursors->wait);
4866         gdk_flush ();
4867
4868         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4869                 RegionSelection::iterator tmp = r;
4870                 ++tmp;
4871
4872                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4873
4874                 if (mrv) {
4875                         try {
4876                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4877                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
4878                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
4879                                 
4880                                 playlist->clear_changes ();
4881                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4882                                 _session->add_command(new StatefulDiffCommand (playlist));
4883                         } catch (...) {
4884                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
4885                         }
4886                 }
4887
4888                 r = tmp;
4889         }
4890
4891         commit_reversible_command ();
4892
4893         set_canvas_cursor (current_canvas_cursor);
4894 }
4895
4896 void
4897 Editor::quantize_region ()
4898 {
4899         int selected_midi_region_cnt = 0;
4900
4901         if (!_session) {
4902                 return;
4903         }
4904
4905         RegionSelection rs = get_regions_from_selection_and_entered ();
4906
4907         if (rs.empty()) {
4908                 return;
4909         }
4910
4911         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4912                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4913                 if (mrv) {
4914                         selected_midi_region_cnt++;
4915                 }
4916         }
4917
4918         if (selected_midi_region_cnt == 0) {
4919                 return;
4920         }
4921
4922         QuantizeDialog* qd = new QuantizeDialog (*this);
4923
4924         qd->present ();
4925         const int r = qd->run ();
4926         qd->hide ();
4927
4928         if (r == Gtk::RESPONSE_OK) {
4929                 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4930                                 qd->start_grid_size(), qd->end_grid_size(),
4931                                 qd->strength(), qd->swing(), qd->threshold());
4932
4933                 apply_midi_note_edit_op (quant);
4934         }
4935 }
4936
4937 void
4938 Editor::insert_patch_change (bool from_context)
4939 {
4940         RegionSelection rs = get_regions_from_selection_and_entered ();
4941
4942         if (rs.empty ()) {
4943                 return;
4944         }
4945
4946         const framepos_t p = get_preferred_edit_position (false, from_context);
4947
4948         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4949            there may be more than one, but the PatchChangeDialog can only offer
4950            one set of patch menus.
4951         */
4952         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4953
4954         Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4955         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4956
4957         if (d.run() == RESPONSE_CANCEL) {
4958                 return;
4959         }
4960
4961         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4962                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4963                 if (mrv) {
4964                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4965                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4966                         }
4967                 }
4968         }
4969 }
4970
4971 void
4972 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4973 {
4974         RegionSelection rs = get_regions_from_selection_and_entered ();
4975
4976         if (rs.empty()) {
4977                 return;
4978         }
4979
4980         begin_reversible_command (command);
4981
4982         set_canvas_cursor (_cursors->wait);
4983         gdk_flush ();
4984
4985         int n = 0;
4986         int const N = rs.size ();
4987
4988         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4989                 RegionSelection::iterator tmp = r;
4990                 ++tmp;
4991
4992                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4993                 if (arv) {
4994                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4995
4996                         if (progress) {
4997                                 progress->descend (1.0 / N);
4998                         }
4999
5000                         if (arv->audio_region()->apply (filter, progress) == 0) {
5001
5002                                 playlist->clear_changes ();
5003                                 playlist->clear_owned_changes ();
5004
5005                                 if (filter.results.empty ()) {
5006
5007                                         /* no regions returned; remove the old one */
5008                                         playlist->remove_region (arv->region ());
5009
5010                                 } else {
5011
5012                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5013
5014                                         /* first region replaces the old one */
5015                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5016                                         ++res;
5017
5018                                         /* add the rest */
5019                                         while (res != filter.results.end()) {
5020                                                 playlist->add_region (*res, (*res)->position());
5021                                                 ++res;
5022                                         }
5023
5024                                 }
5025
5026                                 /* We might have removed regions, which alters other regions' layering_index,
5027                                    so we need to do a recursive diff here.
5028                                 */
5029                                 vector<Command*> cmds;
5030                                 playlist->rdiff (cmds);
5031                                 _session->add_commands (cmds);
5032                                 
5033                                 _session->add_command(new StatefulDiffCommand (playlist));
5034                         } else {
5035                                 goto out;
5036                         }
5037
5038                         if (progress) {
5039                                 progress->ascend ();
5040                         }
5041                 }
5042
5043                 r = tmp;
5044                 ++n;
5045         }
5046
5047         commit_reversible_command ();
5048
5049   out:
5050         set_canvas_cursor (current_canvas_cursor);
5051 }
5052
5053 void
5054 Editor::external_edit_region ()
5055 {
5056         /* more to come */
5057 }
5058
5059 void
5060 Editor::reset_region_gain_envelopes ()
5061 {
5062         RegionSelection rs = get_regions_from_selection_and_entered ();
5063
5064         if (!_session || rs.empty()) {
5065                 return;
5066         }
5067
5068         _session->begin_reversible_command (_("reset region gain"));
5069
5070         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5071                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5072                 if (arv) {
5073                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5074                         XMLNode& before (alist->get_state());
5075
5076                         arv->audio_region()->set_default_envelope ();
5077                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5078                 }
5079         }
5080
5081         _session->commit_reversible_command ();
5082 }
5083
5084 void
5085 Editor::set_region_gain_visibility (RegionView* rv)
5086 {
5087         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5088         if (arv) {
5089                 arv->update_envelope_visibility();
5090         }
5091 }
5092
5093 void
5094 Editor::set_gain_envelope_visibility ()
5095 {
5096         if (!_session) {
5097                 return;
5098         }
5099
5100         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5101                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5102                 if (v) {
5103                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5104                 }
5105         }
5106 }
5107
5108 void
5109 Editor::toggle_gain_envelope_active ()
5110 {
5111         if (_ignore_region_action) {
5112                 return;
5113         }
5114
5115         RegionSelection rs = get_regions_from_selection_and_entered ();
5116
5117         if (!_session || rs.empty()) {
5118                 return;
5119         }
5120
5121         _session->begin_reversible_command (_("region gain envelope active"));
5122
5123         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5124                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5125                 if (arv) {
5126                         arv->region()->clear_changes ();
5127                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5128                         _session->add_command (new StatefulDiffCommand (arv->region()));
5129                 }
5130         }
5131
5132         _session->commit_reversible_command ();
5133 }
5134
5135 void
5136 Editor::toggle_region_lock ()
5137 {
5138         if (_ignore_region_action) {
5139                 return;
5140         }
5141
5142         RegionSelection rs = get_regions_from_selection_and_entered ();
5143
5144         if (!_session || rs.empty()) {
5145                 return;
5146         }
5147
5148         _session->begin_reversible_command (_("toggle region lock"));
5149
5150         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5151                 (*i)->region()->clear_changes ();
5152                 (*i)->region()->set_locked (!(*i)->region()->locked());
5153                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5154         }
5155
5156         _session->commit_reversible_command ();
5157 }
5158
5159 void
5160 Editor::toggle_region_video_lock ()
5161 {
5162         if (_ignore_region_action) {
5163                 return;
5164         }
5165
5166         RegionSelection rs = get_regions_from_selection_and_entered ();
5167
5168         if (!_session || rs.empty()) {
5169                 return;
5170         }
5171
5172         _session->begin_reversible_command (_("Toggle Video Lock"));
5173
5174         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5175                 (*i)->region()->clear_changes ();
5176                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5177                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5178         }
5179
5180         _session->commit_reversible_command ();
5181 }
5182
5183 void
5184 Editor::toggle_region_lock_style ()
5185 {
5186         if (_ignore_region_action) {
5187                 return;
5188         }
5189
5190         RegionSelection rs = get_regions_from_selection_and_entered ();
5191
5192         if (!_session || rs.empty()) {
5193                 return;
5194         }
5195
5196         _session->begin_reversible_command (_("region lock style"));
5197
5198         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5199                 (*i)->region()->clear_changes ();
5200                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5201                 (*i)->region()->set_position_lock_style (ns);
5202                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5203         }
5204
5205         _session->commit_reversible_command ();
5206 }
5207
5208 void
5209 Editor::toggle_opaque_region ()
5210 {
5211         if (_ignore_region_action) {
5212                 return;
5213         }
5214
5215         RegionSelection rs = get_regions_from_selection_and_entered ();
5216
5217         if (!_session || rs.empty()) {
5218                 return;
5219         }
5220
5221         _session->begin_reversible_command (_("change region opacity"));
5222
5223         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5224                 (*i)->region()->clear_changes ();
5225                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5226                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5227         }
5228
5229         _session->commit_reversible_command ();
5230 }
5231
5232 void
5233 Editor::toggle_record_enable ()
5234 {
5235         bool new_state = false;
5236         bool first = true;
5237         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5238                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5239                 if (!rtav)
5240                         continue;
5241                 if (!rtav->is_track())
5242                         continue;
5243
5244                 if (first) {
5245                         new_state = !rtav->track()->record_enabled();
5246                         first = false;
5247                 }
5248
5249                 rtav->track()->set_record_enabled (new_state, this);
5250         }
5251 }
5252
5253 void
5254 Editor::toggle_solo ()
5255 {
5256         bool new_state = false;
5257         bool first = true;
5258         boost::shared_ptr<RouteList> rl (new RouteList);
5259
5260         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5261                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5262
5263                 if (!rtav) {
5264                         continue;
5265                 }
5266
5267                 if (first) {
5268                         new_state = !rtav->route()->soloed ();
5269                         first = false;
5270                 }
5271
5272                 rl->push_back (rtav->route());
5273         }
5274
5275         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5276 }
5277
5278 void
5279 Editor::toggle_mute ()
5280 {
5281         bool new_state = false;
5282         bool first = true;
5283         boost::shared_ptr<RouteList> rl (new RouteList);
5284
5285         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5286                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5287
5288                 if (!rtav) {
5289                         continue;
5290                 }
5291
5292                 if (first) {
5293                         new_state = !rtav->route()->muted();
5294                         first = false;
5295                 }
5296
5297                 rl->push_back (rtav->route());
5298         }
5299
5300         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5301 }
5302
5303 void
5304 Editor::toggle_solo_isolate ()
5305 {
5306 }
5307
5308 void
5309 Editor::set_fade_length (bool in)
5310 {
5311         RegionSelection rs = get_regions_from_selection_and_entered ();
5312
5313         if (rs.empty()) {
5314                 return;
5315         }
5316
5317         /* we need a region to measure the offset from the start */
5318
5319         RegionView* rv = rs.front ();
5320
5321         framepos_t pos = get_preferred_edit_position();
5322         framepos_t len;
5323         char const * cmd;
5324
5325         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5326                 /* edit point is outside the relevant region */
5327                 return;
5328         }
5329
5330         if (in) {
5331                 if (pos <= rv->region()->position()) {
5332                         /* can't do it */
5333                         return;
5334                 }
5335                 len = pos - rv->region()->position();
5336                 cmd = _("set fade in length");
5337         } else {
5338                 if (pos >= rv->region()->last_frame()) {
5339                         /* can't do it */
5340                         return;
5341                 }
5342                 len = rv->region()->last_frame() - pos;
5343                 cmd = _("set fade out length");
5344         }
5345
5346         begin_reversible_command (cmd);
5347
5348         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5349                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5350
5351                 if (!tmp) {
5352                         return;
5353                 }
5354
5355                 boost::shared_ptr<AutomationList> alist;
5356                 if (in) {
5357                         alist = tmp->audio_region()->fade_in();
5358                 } else {
5359                         alist = tmp->audio_region()->fade_out();
5360                 }
5361
5362                 XMLNode &before = alist->get_state();
5363
5364                 if (in) {
5365                         tmp->audio_region()->set_fade_in_length (len);
5366                         tmp->audio_region()->set_fade_in_active (true);
5367                 } else {
5368                         tmp->audio_region()->set_fade_out_length (len);
5369                         tmp->audio_region()->set_fade_out_active (true);
5370                 }
5371
5372                 XMLNode &after = alist->get_state();
5373                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5374         }
5375
5376         commit_reversible_command ();
5377 }
5378
5379 void
5380 Editor::set_fade_in_shape (FadeShape shape)
5381 {
5382         RegionSelection rs = get_regions_from_selection_and_entered ();
5383
5384         if (rs.empty()) {
5385                 return;
5386         }
5387
5388         begin_reversible_command (_("set fade in shape"));
5389
5390         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5391                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5392
5393                 if (!tmp) {
5394                         return;
5395                 }
5396
5397                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5398                 XMLNode &before = alist->get_state();
5399
5400                 tmp->audio_region()->set_fade_in_shape (shape);
5401
5402                 XMLNode &after = alist->get_state();
5403                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5404         }
5405
5406         commit_reversible_command ();
5407
5408 }
5409
5410 void
5411 Editor::set_fade_out_shape (FadeShape shape)
5412 {
5413         RegionSelection rs = get_regions_from_selection_and_entered ();
5414
5415         if (rs.empty()) {
5416                 return;
5417         }
5418
5419         begin_reversible_command (_("set fade out shape"));
5420
5421         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5422                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5423
5424                 if (!tmp) {
5425                         return;
5426                 }
5427
5428                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5429                 XMLNode &before = alist->get_state();
5430
5431                 tmp->audio_region()->set_fade_out_shape (shape);
5432
5433                 XMLNode &after = alist->get_state();
5434                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5435         }
5436
5437         commit_reversible_command ();
5438 }
5439
5440 void
5441 Editor::set_fade_in_active (bool yn)
5442 {
5443         RegionSelection rs = get_regions_from_selection_and_entered ();
5444
5445         if (rs.empty()) {
5446                 return;
5447         }
5448
5449         begin_reversible_command (_("set fade in active"));
5450
5451         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5452                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5453
5454                 if (!tmp) {
5455                         return;
5456                 }
5457
5458
5459                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5460
5461                 ar->clear_changes ();
5462                 ar->set_fade_in_active (yn);
5463                 _session->add_command (new StatefulDiffCommand (ar));
5464         }
5465
5466         commit_reversible_command ();
5467 }
5468
5469 void
5470 Editor::set_fade_out_active (bool yn)
5471 {
5472         RegionSelection rs = get_regions_from_selection_and_entered ();
5473
5474         if (rs.empty()) {
5475                 return;
5476         }
5477
5478         begin_reversible_command (_("set fade out active"));
5479
5480         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5481                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5482
5483                 if (!tmp) {
5484                         return;
5485                 }
5486
5487                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5488
5489                 ar->clear_changes ();
5490                 ar->set_fade_out_active (yn);
5491                 _session->add_command(new StatefulDiffCommand (ar));
5492         }
5493
5494         commit_reversible_command ();
5495 }
5496
5497 void
5498 Editor::toggle_region_fades (int dir)
5499 {
5500         if (_ignore_region_action) {
5501                 return;
5502         }
5503         
5504         boost::shared_ptr<AudioRegion> ar;
5505         bool yn = false;
5506
5507         RegionSelection rs = get_regions_from_selection_and_entered ();
5508
5509         if (rs.empty()) {
5510                 return;
5511         }
5512
5513         RegionSelection::iterator i;
5514         for (i = rs.begin(); i != rs.end(); ++i) {
5515                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5516                         if (dir == -1) {
5517                                 yn = ar->fade_out_active ();
5518                         } else {
5519                                 yn = ar->fade_in_active ();
5520                         }
5521                         break;
5522                 }
5523         }
5524
5525         if (i == rs.end()) {
5526                 return;
5527         }
5528
5529         /* XXX should this undo-able? */
5530
5531         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5532                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5533                         continue;
5534                 }
5535                 if (dir == 1 || dir == 0) {
5536                         ar->set_fade_in_active (!yn);
5537                 }
5538
5539                 if (dir == -1 || dir == 0) {
5540                         ar->set_fade_out_active (!yn);
5541                 }
5542         }
5543 }
5544
5545
5546 /** Update region fade visibility after its configuration has been changed */
5547 void
5548 Editor::update_region_fade_visibility ()
5549 {
5550         bool _fade_visibility = _session->config.get_show_region_fades ();
5551
5552         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5553                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5554                 if (v) {
5555                         if (_fade_visibility) {
5556                                 v->audio_view()->show_all_fades ();
5557                         } else {
5558                                 v->audio_view()->hide_all_fades ();
5559                         }
5560                 }
5561         }
5562 }
5563
5564 void
5565 Editor::set_edit_point ()
5566 {
5567         framepos_t where;
5568         bool ignored;
5569
5570         if (!mouse_frame (where, ignored)) {
5571                 return;
5572         }
5573
5574         snap_to (where);
5575
5576         if (selection->markers.empty()) {
5577
5578                 mouse_add_new_marker (where);
5579
5580         } else {
5581                 bool ignored;
5582
5583                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5584
5585                 if (loc) {
5586                         loc->move_to (where);
5587                 }
5588         }
5589 }
5590
5591 void
5592 Editor::set_playhead_cursor ()
5593 {
5594         if (entered_marker) {
5595                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5596         } else {
5597                 framepos_t where;
5598                 bool ignored;
5599
5600                 if (!mouse_frame (where, ignored)) {
5601                         return;
5602                 }
5603
5604                 snap_to (where);
5605
5606                 if (_session) {
5607                         _session->request_locate (where, _session->transport_rolling());
5608                 }
5609         }
5610
5611         if ( Config->get_always_play_range() )
5612                 cancel_time_selection();
5613 }
5614
5615 void
5616 Editor::split_region ()
5617 {
5618         if ( !selection->time.empty()) {
5619                 separate_regions_between (selection->time);
5620                 return;
5621         }
5622
5623         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5624
5625         framepos_t where = get_preferred_edit_position ();
5626
5627         if (rs.empty()) {
5628                 return;
5629         }
5630
5631         split_regions_at (where, rs);
5632 }
5633
5634 struct EditorOrderRouteSorter {
5635     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5636             return a->order_key () < b->order_key ();
5637     }
5638 };
5639
5640 void
5641 Editor::select_next_route()
5642 {
5643         if (selection->tracks.empty()) {
5644                 selection->set (track_views.front());
5645                 return;
5646         }
5647
5648         TimeAxisView* current = selection->tracks.front();
5649
5650         RouteUI *rui;
5651         do {
5652                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5653                         if (*i == current) {
5654                                 ++i;
5655                                 if (i != track_views.end()) {
5656                                         current = (*i);
5657                                 } else {
5658                                         current = (*(track_views.begin()));
5659                                         //selection->set (*(track_views.begin()));
5660                                 }
5661                                 break;
5662                         }
5663                 }
5664                 rui = dynamic_cast<RouteUI *>(current);
5665         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5666
5667         selection->set(current);
5668
5669         ensure_track_visible(current);
5670 }
5671
5672 void
5673 Editor::select_prev_route()
5674 {
5675         if (selection->tracks.empty()) {
5676                 selection->set (track_views.front());
5677                 return;
5678         }
5679
5680         TimeAxisView* current = selection->tracks.front();
5681
5682         RouteUI *rui;
5683         do {
5684                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5685                         if (*i == current) {
5686                                 ++i;
5687                                 if (i != track_views.rend()) {
5688                                         current = (*i);
5689                                 } else {
5690                                         current = *(track_views.rbegin());
5691                                 }
5692                                 break;
5693                         }
5694                 }
5695                 rui = dynamic_cast<RouteUI *>(current);
5696         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5697
5698         selection->set (current);
5699
5700         ensure_track_visible(current);
5701 }
5702
5703 void
5704 Editor::ensure_track_visible(TimeAxisView *track)
5705 {
5706         if (track->hidden())
5707                 return;
5708
5709         double const current_view_min_y = vertical_adjustment.get_value();
5710         double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
5711
5712         double const track_min_y = track->y_position ();
5713         double const track_max_y = track->y_position () + track->effective_height ();
5714
5715         if (track_min_y >= current_view_min_y &&
5716             track_max_y <= current_view_max_y) {
5717                 return;
5718         }
5719
5720         double new_value;
5721
5722         if (track_min_y < current_view_min_y) {
5723                 // Track is above the current view
5724                 new_value = track_min_y;
5725         } else {
5726                 // Track is below the current view
5727                 new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
5728         }
5729
5730         vertical_adjustment.set_value(new_value);
5731 }
5732
5733 void
5734 Editor::set_loop_from_selection (bool play)
5735 {
5736         if (_session == 0 || selection->time.empty()) {
5737                 return;
5738         }
5739
5740         framepos_t start = selection->time[clicked_selection].start;
5741         framepos_t end = selection->time[clicked_selection].end;
5742
5743         set_loop_range (start, end,  _("set loop range from selection"));
5744
5745         if (play) {
5746                 _session->request_play_loop (true);
5747                 _session->request_locate (start, true);
5748         }
5749 }
5750
5751 void
5752 Editor::set_loop_from_edit_range (bool play)
5753 {
5754         if (_session == 0) {
5755                 return;
5756         }
5757
5758         framepos_t start;
5759         framepos_t end;
5760
5761         if (!get_edit_op_range (start, end)) {
5762                 return;
5763         }
5764
5765         set_loop_range (start, end,  _("set loop range from edit range"));
5766
5767         if (play) {
5768                 _session->request_play_loop (true);
5769                 _session->request_locate (start, true);
5770         }
5771 }
5772
5773 void
5774 Editor::set_loop_from_region (bool play)
5775 {
5776         framepos_t start = max_framepos;
5777         framepos_t end = 0;
5778
5779         RegionSelection rs = get_regions_from_selection_and_entered ();
5780
5781         if (rs.empty()) {
5782                 return;
5783         }
5784
5785         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5786                 if ((*i)->region()->position() < start) {
5787                         start = (*i)->region()->position();
5788                 }
5789                 if ((*i)->region()->last_frame() + 1 > end) {
5790                         end = (*i)->region()->last_frame() + 1;
5791                 }
5792         }
5793
5794         set_loop_range (start, end, _("set loop range from region"));
5795
5796         if (play) {
5797                 _session->request_play_loop (true);
5798                 _session->request_locate (start, true);
5799         }
5800 }
5801
5802 void
5803 Editor::set_punch_from_selection ()
5804 {
5805         if (_session == 0 || selection->time.empty()) {
5806                 return;
5807         }
5808
5809         framepos_t start = selection->time[clicked_selection].start;
5810         framepos_t end = selection->time[clicked_selection].end;
5811
5812         set_punch_range (start, end,  _("set punch range from selection"));
5813 }
5814
5815 void
5816 Editor::set_punch_from_edit_range ()
5817 {
5818         if (_session == 0) {
5819                 return;
5820         }
5821
5822         framepos_t start;
5823         framepos_t end;
5824
5825         if (!get_edit_op_range (start, end)) {
5826                 return;
5827         }
5828
5829         set_punch_range (start, end,  _("set punch range from edit range"));
5830 }
5831
5832 void
5833 Editor::set_punch_from_region ()
5834 {
5835         framepos_t start = max_framepos;
5836         framepos_t end = 0;
5837
5838         RegionSelection rs = get_regions_from_selection_and_entered ();
5839
5840         if (rs.empty()) {
5841                 return;
5842         }
5843
5844         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5845                 if ((*i)->region()->position() < start) {
5846                         start = (*i)->region()->position();
5847                 }
5848                 if ((*i)->region()->last_frame() + 1 > end) {
5849                         end = (*i)->region()->last_frame() + 1;
5850                 }
5851         }
5852
5853         set_punch_range (start, end, _("set punch range from region"));
5854 }
5855
5856 void
5857 Editor::pitch_shift_region ()
5858 {
5859         RegionSelection rs = get_regions_from_selection_and_entered ();
5860
5861         RegionSelection audio_rs;
5862         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5863                 if (dynamic_cast<AudioRegionView*> (*i)) {
5864                         audio_rs.push_back (*i);
5865                 }
5866         }
5867
5868         if (audio_rs.empty()) {
5869                 return;
5870         }
5871
5872         pitch_shift (audio_rs, 1.2);
5873 }
5874
5875 void
5876 Editor::transpose_region ()
5877 {
5878         RegionSelection rs = get_regions_from_selection_and_entered ();
5879
5880         list<MidiRegionView*> midi_region_views;
5881         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5882                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5883                 if (mrv) {
5884                         midi_region_views.push_back (mrv);
5885                 }
5886         }
5887
5888         TransposeDialog d;
5889         int const r = d.run ();
5890         if (r != RESPONSE_ACCEPT) {
5891                 return;
5892         }
5893
5894         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5895                 (*i)->midi_region()->transpose (d.semitones ());
5896         }
5897 }
5898
5899 void
5900 Editor::set_tempo_from_region ()
5901 {
5902         RegionSelection rs = get_regions_from_selection_and_entered ();
5903
5904         if (!_session || rs.empty()) {
5905                 return;
5906         }
5907
5908         RegionView* rv = rs.front();
5909
5910         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5911 }
5912
5913 void
5914 Editor::use_range_as_bar ()
5915 {
5916         framepos_t start, end;
5917         if (get_edit_op_range (start, end)) {
5918                 define_one_bar (start, end);
5919         }
5920 }
5921
5922 void
5923 Editor::define_one_bar (framepos_t start, framepos_t end)
5924 {
5925         framepos_t length = end - start;
5926
5927         const Meter& m (_session->tempo_map().meter_at (start));
5928
5929         /* length = 1 bar */
5930
5931         /* now we want frames per beat.
5932            we have frames per bar, and beats per bar, so ...
5933         */
5934
5935         /* XXXX METER MATH */
5936
5937         double frames_per_beat = length / m.divisions_per_bar();
5938
5939         /* beats per minute = */
5940
5941         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5942
5943         /* now decide whether to:
5944
5945             (a) set global tempo
5946             (b) add a new tempo marker
5947
5948         */
5949
5950         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5951
5952         bool do_global = false;
5953
5954         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5955
5956                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5957                    at the start, or create a new marker
5958                 */
5959
5960                 vector<string> options;
5961                 options.push_back (_("Cancel"));
5962                 options.push_back (_("Add new marker"));
5963                 options.push_back (_("Set global tempo"));
5964
5965                 Choice c (
5966                         _("Define one bar"),
5967                         _("Do you want to set the global tempo or add a new tempo marker?"),
5968                         options
5969                         );
5970
5971                 c.set_default_response (2);
5972
5973                 switch (c.run()) {
5974                 case 0:
5975                         return;
5976
5977                 case 2:
5978                         do_global = true;
5979                         break;
5980
5981                 default:
5982                         do_global = false;
5983                 }
5984
5985         } else {
5986
5987                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5988                    if the marker is at the region starter, change it, otherwise add
5989                    a new tempo marker
5990                 */
5991         }
5992
5993         begin_reversible_command (_("set tempo from region"));
5994         XMLNode& before (_session->tempo_map().get_state());
5995
5996         if (do_global) {
5997                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5998         } else if (t.frame() == start) {
5999                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6000         } else {
6001                 Timecode::BBT_Time bbt;
6002                 _session->tempo_map().bbt_time (start, bbt);
6003                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
6004         }
6005
6006         XMLNode& after (_session->tempo_map().get_state());
6007
6008         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6009         commit_reversible_command ();
6010 }
6011
6012 void
6013 Editor::split_region_at_transients ()
6014 {
6015         AnalysisFeatureList positions;
6016
6017         RegionSelection rs = get_regions_from_selection_and_entered ();
6018
6019         if (!_session || rs.empty()) {
6020                 return;
6021         }
6022
6023         _session->begin_reversible_command (_("split regions"));
6024
6025         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6026
6027                 RegionSelection::iterator tmp;
6028
6029                 tmp = i;
6030                 ++tmp;
6031
6032                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6033
6034                 if (ar && (ar->get_transients (positions) == 0)) {
6035                         split_region_at_points ((*i)->region(), positions, true);
6036                         positions.clear ();
6037                 }
6038
6039                 i = tmp;
6040         }
6041
6042         _session->commit_reversible_command ();
6043
6044 }
6045
6046 void
6047 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6048 {
6049         bool use_rhythmic_rodent = false;
6050
6051         boost::shared_ptr<Playlist> pl = r->playlist();
6052
6053         list<boost::shared_ptr<Region> > new_regions;
6054
6055         if (!pl) {
6056                 return;
6057         }
6058
6059         if (positions.empty()) {
6060                 return;
6061         }
6062
6063
6064         if (positions.size() > 20 && can_ferret) {
6065                 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);
6066                 MessageDialog msg (msgstr,
6067                                    false,
6068                                    Gtk::MESSAGE_INFO,
6069                                    Gtk::BUTTONS_OK_CANCEL);
6070
6071                 if (can_ferret) {
6072                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6073                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6074                 } else {
6075                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6076                 }
6077
6078                 msg.set_title (_("Excessive split?"));
6079                 msg.present ();
6080
6081                 int response = msg.run();
6082                 msg.hide ();
6083
6084                 switch (response) {
6085                 case RESPONSE_OK:
6086                         break;
6087                 case RESPONSE_APPLY:
6088                         use_rhythmic_rodent = true;
6089                         break;
6090                 default:
6091                         return;
6092                 }
6093         }
6094
6095         if (use_rhythmic_rodent) {
6096                 show_rhythm_ferret ();
6097                 return;
6098         }
6099
6100         AnalysisFeatureList::const_iterator x;
6101
6102         pl->clear_changes ();
6103         pl->clear_owned_changes ();
6104
6105         x = positions.begin();
6106
6107         if (x == positions.end()) {
6108                 return;
6109         }
6110
6111         pl->freeze ();
6112         pl->remove_region (r);
6113
6114         framepos_t pos = 0;
6115
6116         while (x != positions.end()) {
6117
6118                 /* deal with positons that are out of scope of present region bounds */
6119                 if (*x <= 0 || *x > r->length()) {
6120                         ++x;
6121                         continue;
6122                 }
6123
6124                 /* file start = original start + how far we from the initial position ?
6125                  */
6126
6127                 framepos_t file_start = r->start() + pos;
6128
6129                 /* length = next position - current position
6130                  */
6131
6132                 framepos_t len = (*x) - pos;
6133
6134                 /* XXX we do we really want to allow even single-sample regions?
6135                    shouldn't we have some kind of lower limit on region size?
6136                 */
6137
6138                 if (len <= 0) {
6139                         break;
6140                 }
6141
6142                 string new_name;
6143
6144                 if (RegionFactory::region_name (new_name, r->name())) {
6145                         break;
6146                 }
6147
6148                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6149
6150                 PropertyList plist;
6151
6152                 plist.add (ARDOUR::Properties::start, file_start);
6153                 plist.add (ARDOUR::Properties::length, len);
6154                 plist.add (ARDOUR::Properties::name, new_name);
6155                 plist.add (ARDOUR::Properties::layer, 0);
6156
6157                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6158                 /* because we set annouce to false, manually add the new region to the
6159                    RegionFactory map
6160                 */
6161                 RegionFactory::map_add (nr);
6162
6163                 pl->add_region (nr, r->position() + pos);
6164
6165                 if (select_new) {
6166                         new_regions.push_front(nr);
6167                 }
6168
6169                 pos += len;
6170                 ++x;
6171         }
6172
6173         string new_name;
6174
6175         RegionFactory::region_name (new_name, r->name());
6176
6177         /* Add the final region */
6178         PropertyList plist;
6179
6180         plist.add (ARDOUR::Properties::start, r->start() + pos);
6181         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6182         plist.add (ARDOUR::Properties::name, new_name);
6183         plist.add (ARDOUR::Properties::layer, 0);
6184
6185         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6186         /* because we set annouce to false, manually add the new region to the
6187            RegionFactory map
6188         */
6189         RegionFactory::map_add (nr);
6190         pl->add_region (nr, r->position() + pos);
6191
6192         if (select_new) {
6193                 new_regions.push_front(nr);
6194         }
6195
6196         pl->thaw ();
6197
6198         /* We might have removed regions, which alters other regions' layering_index,
6199            so we need to do a recursive diff here.
6200         */
6201         vector<Command*> cmds;
6202         pl->rdiff (cmds);
6203         _session->add_commands (cmds);
6204         
6205         _session->add_command (new StatefulDiffCommand (pl));
6206
6207         if (select_new) {
6208
6209                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6210                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6211                 }
6212         }
6213 }
6214
6215 void
6216 Editor::place_transient()
6217 {
6218         if (!_session) {
6219                 return;
6220         }
6221
6222         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6223
6224         if (rs.empty()) {
6225                 return;
6226         }
6227
6228         framepos_t where = get_preferred_edit_position();
6229
6230         _session->begin_reversible_command (_("place transient"));
6231
6232         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6233                 framepos_t position = (*r)->region()->position();
6234                 (*r)->region()->add_transient(where - position);
6235         }
6236
6237         _session->commit_reversible_command ();
6238 }
6239
6240 void
6241 Editor::remove_transient(ArdourCanvas::Item* item)
6242 {
6243         if (!_session) {
6244                 return;
6245         }
6246
6247         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6248         assert (_line);
6249
6250         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6251         _arv->remove_transient (*(float*) _line->get_data ("position"));
6252 }
6253
6254 void
6255 Editor::snap_regions_to_grid ()
6256 {
6257         list <boost::shared_ptr<Playlist > > used_playlists;
6258
6259         RegionSelection rs = get_regions_from_selection_and_entered ();
6260
6261         if (!_session || rs.empty()) {
6262                 return;
6263         }
6264
6265         _session->begin_reversible_command (_("snap regions to grid"));
6266
6267         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6268
6269                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6270
6271                 if (!pl->frozen()) {
6272                         /* we haven't seen this playlist before */
6273
6274                         /* remember used playlists so we can thaw them later */
6275                         used_playlists.push_back(pl);
6276                         pl->freeze();
6277                 }
6278
6279                 framepos_t start_frame = (*r)->region()->first_frame ();
6280                 snap_to (start_frame);
6281                 (*r)->region()->set_position (start_frame);
6282         }
6283
6284         while (used_playlists.size() > 0) {
6285                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6286                 (*i)->thaw();
6287                 used_playlists.pop_front();
6288         }
6289
6290         _session->commit_reversible_command ();
6291 }
6292
6293 void
6294 Editor::close_region_gaps ()
6295 {
6296         list <boost::shared_ptr<Playlist > > used_playlists;
6297
6298         RegionSelection rs = get_regions_from_selection_and_entered ();
6299
6300         if (!_session || rs.empty()) {
6301                 return;
6302         }
6303
6304         Dialog dialog (_("Close Region Gaps"));
6305
6306         Table table (2, 3);
6307         table.set_spacings (12);
6308         table.set_border_width (12);
6309         Label* l = manage (left_aligned_label (_("Crossfade length")));
6310         table.attach (*l, 0, 1, 0, 1);
6311
6312         SpinButton spin_crossfade (1, 0);
6313         spin_crossfade.set_range (0, 15);
6314         spin_crossfade.set_increments (1, 1);
6315         spin_crossfade.set_value (5);
6316         table.attach (spin_crossfade, 1, 2, 0, 1);
6317
6318         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6319
6320         l = manage (left_aligned_label (_("Pull-back length")));
6321         table.attach (*l, 0, 1, 1, 2);
6322
6323         SpinButton spin_pullback (1, 0);
6324         spin_pullback.set_range (0, 100);
6325         spin_pullback.set_increments (1, 1);
6326         spin_pullback.set_value(30);
6327         table.attach (spin_pullback, 1, 2, 1, 2);
6328
6329         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6330
6331         dialog.get_vbox()->pack_start (table);
6332         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6333         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6334         dialog.show_all ();
6335
6336         if (dialog.run () == RESPONSE_CANCEL) {
6337                 return;
6338         }
6339
6340         framepos_t crossfade_len = spin_crossfade.get_value();
6341         framepos_t pull_back_frames = spin_pullback.get_value();
6342
6343         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6344         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6345
6346         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6347
6348         _session->begin_reversible_command (_("close region gaps"));
6349
6350         int idx = 0;
6351         boost::shared_ptr<Region> last_region;
6352
6353         rs.sort_by_position_and_track();
6354
6355         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6356
6357                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6358
6359                 if (!pl->frozen()) {
6360                         /* we haven't seen this playlist before */
6361
6362                         /* remember used playlists so we can thaw them later */
6363                         used_playlists.push_back(pl);
6364                         pl->freeze();
6365                 }
6366
6367                 framepos_t position = (*r)->region()->position();
6368
6369                 if (idx == 0 || position < last_region->position()){
6370                         last_region = (*r)->region();
6371                         idx++;
6372                         continue;
6373                 }
6374
6375                 (*r)->region()->trim_front( (position - pull_back_frames));
6376                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6377
6378                 last_region = (*r)->region();
6379
6380                 idx++;
6381         }
6382
6383         while (used_playlists.size() > 0) {
6384                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6385                 (*i)->thaw();
6386                 used_playlists.pop_front();
6387         }
6388
6389         _session->commit_reversible_command ();
6390 }
6391
6392 void
6393 Editor::tab_to_transient (bool forward)
6394 {
6395         AnalysisFeatureList positions;
6396
6397         RegionSelection rs = get_regions_from_selection_and_entered ();
6398
6399         if (!_session) {
6400                 return;
6401         }
6402
6403         framepos_t pos = _session->audible_frame ();
6404
6405         if (!selection->tracks.empty()) {
6406
6407                 /* don't waste time searching for transients in duplicate playlists.
6408                  */
6409
6410                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6411
6412                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6413
6414                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6415
6416                         if (rtv) {
6417                                 boost::shared_ptr<Track> tr = rtv->track();
6418                                 if (tr) {
6419                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6420                                         if (pl) {
6421                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6422
6423                                                 if (result >= 0) {
6424                                                         positions.push_back (result);
6425                                                 }
6426                                         }
6427                                 }
6428                         }
6429                 }
6430
6431         } else {
6432
6433                 if (rs.empty()) {
6434                         return;
6435                 }
6436
6437                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6438                         (*r)->region()->get_transients (positions);
6439                 }
6440         }
6441
6442         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6443
6444         if (forward) {
6445                 AnalysisFeatureList::iterator x;
6446
6447                 for (x = positions.begin(); x != positions.end(); ++x) {
6448                         if ((*x) > pos) {
6449                                 break;
6450                         }
6451                 }
6452
6453                 if (x != positions.end ()) {
6454                         _session->request_locate (*x);
6455                 }
6456
6457         } else {
6458                 AnalysisFeatureList::reverse_iterator x;
6459
6460                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6461                         if ((*x) < pos) {
6462                                 break;
6463                         }
6464                 }
6465
6466                 if (x != positions.rend ()) {
6467                         _session->request_locate (*x);
6468                 }
6469         }
6470 }
6471
6472 void
6473 Editor::playhead_forward_to_grid ()
6474 {
6475         if (!_session) {
6476                 return;
6477         }
6478         
6479         framepos_t pos = playhead_cursor->current_frame ();
6480         if (pos < max_framepos - 1) {
6481                 pos += 2;
6482                 snap_to_internal (pos, 1, false);
6483                 _session->request_locate (pos);
6484         }
6485 }
6486
6487
6488 void
6489 Editor::playhead_backward_to_grid ()
6490 {
6491         if (!_session) {
6492                 return;
6493         }
6494         
6495         framepos_t pos = playhead_cursor->current_frame ();
6496         if (pos > 2) {
6497                 pos -= 2;
6498                 snap_to_internal (pos, -1, false);
6499                 _session->request_locate (pos);
6500         }
6501 }
6502
6503 void
6504 Editor::set_track_height (Height h)
6505 {
6506         TrackSelection& ts (selection->tracks);
6507
6508         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6509                 (*x)->set_height_enum (h);
6510         }
6511 }
6512
6513 void
6514 Editor::toggle_tracks_active ()
6515 {
6516         TrackSelection& ts (selection->tracks);
6517         bool first = true;
6518         bool target = false;
6519
6520         if (ts.empty()) {
6521                 return;
6522         }
6523
6524         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6525                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6526
6527                 if (rtv) {
6528                         if (first) {
6529                                 target = !rtv->_route->active();
6530                                 first = false;
6531                         }
6532                         rtv->_route->set_active (target, this);
6533                 }
6534         }
6535 }
6536
6537 void
6538 Editor::remove_tracks ()
6539 {
6540         TrackSelection& ts (selection->tracks);
6541
6542         if (ts.empty()) {
6543                 return;
6544         }
6545
6546         vector<string> choices;
6547         string prompt;
6548         int ntracks = 0;
6549         int nbusses = 0;
6550         const char* trackstr;
6551         const char* busstr;
6552         vector<boost::shared_ptr<Route> > routes;
6553         bool special_bus = false;
6554
6555         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6556                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6557                 if (!rtv) {
6558                         continue;
6559                 }
6560                 if (rtv->is_track()) {
6561                         ntracks++;
6562                 } else {
6563                         nbusses++;
6564                 }
6565                 routes.push_back (rtv->_route);
6566
6567                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6568                         special_bus = true;
6569                 }
6570         }
6571
6572         if (special_bus && !Config->get_allow_special_bus_removal()) {
6573                 MessageDialog msg (_("That would be bad news ...."),
6574                                    false,
6575                                    Gtk::MESSAGE_INFO,
6576                                    Gtk::BUTTONS_OK);
6577                 msg.set_secondary_text (string_compose (_(
6578                                                                 "Removing the master or monitor bus is such a bad idea\n\
6579 that %1 is not going to allow it.\n\
6580 \n\
6581 If you really want to do this sort of thing\n\
6582 edit your ardour.rc file to set the\n\
6583 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6584
6585                 msg.present ();
6586                 msg.run ();
6587                 return;
6588         }
6589
6590         if (ntracks + nbusses == 0) {
6591                 return;
6592         }
6593
6594         if (ntracks > 1) {
6595                 trackstr = _("tracks");
6596         } else {
6597                 trackstr = _("track");
6598         }
6599
6600         if (nbusses > 1) {
6601                 busstr = _("busses");
6602         } else {
6603                 busstr = _("bus");
6604         }
6605
6606         if (ntracks) {
6607                 if (nbusses) {
6608                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6609                                                     "(You may also lose the playlists associated with the %2)\n\n"
6610                                                     "This action cannot be undone, and the session file will be overwritten!"),
6611                                                   ntracks, trackstr, nbusses, busstr);
6612                 } else {
6613                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
6614                                                     "(You may also lose the playlists associated with the %2)\n\n"
6615                                                     "This action cannot be undone, and the session file will be overwritten!"),
6616                                                   ntracks, trackstr);
6617                 }
6618         } else if (nbusses) {
6619                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
6620                                             "This action cannot be undon, and the session file will be overwritten"),
6621                                           nbusses, busstr);
6622         }
6623
6624         choices.push_back (_("No, do nothing."));
6625         if (ntracks + nbusses > 1) {
6626                 choices.push_back (_("Yes, remove them."));
6627         } else {
6628                 choices.push_back (_("Yes, remove it."));
6629         }
6630
6631         string title;
6632         if (ntracks) {
6633                 title = string_compose (_("Remove %1"), trackstr);
6634         } else {
6635                 title = string_compose (_("Remove %1"), busstr);
6636         }
6637
6638         Choice prompter (title, prompt, choices);
6639
6640         if (prompter.run () != 1) {
6641                 return;
6642         }
6643
6644         for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6645                 _session->remove_route (*x);
6646         }
6647 }
6648
6649 void
6650 Editor::do_insert_time ()
6651 {
6652         if (selection->tracks.empty()) {
6653                 return;
6654         }
6655
6656         InsertTimeDialog d (*this);
6657         int response = d.run ();
6658
6659         if (response != RESPONSE_OK) {
6660                 return;
6661         }
6662
6663         if (d.distance() == 0) {
6664                 return;
6665         }
6666
6667         InsertTimeOption opt = d.intersected_region_action ();
6668
6669         insert_time (
6670                 get_preferred_edit_position(),
6671                 d.distance(),
6672                 opt,
6673                 d.all_playlists(),
6674                 d.move_glued(),
6675                 d.move_markers(),
6676                 d.move_glued_markers(),
6677                 d.move_locked_markers(),
6678                 d.move_tempos()
6679                 );
6680 }
6681
6682 void
6683 Editor::insert_time (
6684         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6685         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6686         )
6687 {
6688         bool commit = false;
6689
6690         if (Config->get_edit_mode() == Lock) {
6691                 return;
6692         }
6693
6694         begin_reversible_command (_("insert time"));
6695
6696         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6697
6698         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6699
6700                 /* regions */
6701
6702                 /* don't operate on any playlist more than once, which could
6703                  * happen if "all playlists" is enabled, but there is more
6704                  * than 1 track using playlists "from" a given track.
6705                  */
6706
6707                 set<boost::shared_ptr<Playlist> > pl;
6708
6709                 if (all_playlists) {
6710                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6711                         if (rtav) {
6712                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6713                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6714                                         pl.insert (*p);
6715                                 }
6716                         }
6717                 } else {
6718                         if ((*x)->playlist ()) {
6719                                 pl.insert ((*x)->playlist ());
6720                         }
6721                 }
6722                 
6723                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6724
6725                         (*i)->clear_changes ();
6726                         (*i)->clear_owned_changes ();
6727
6728                         if (opt == SplitIntersected) {
6729                                 (*i)->split (pos);
6730                         }
6731
6732                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6733
6734                         vector<Command*> cmds;
6735                         (*i)->rdiff (cmds);
6736                         _session->add_commands (cmds);
6737
6738                         _session->add_command (new StatefulDiffCommand (*i));
6739                         commit = true;
6740                 }
6741
6742                 /* automation */
6743                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6744                 if (rtav) {
6745                         rtav->route ()->shift (pos, frames);
6746                         commit = true;
6747                 }
6748         }
6749
6750         /* markers */
6751         if (markers_too) {
6752                 bool moved = false;
6753                 XMLNode& before (_session->locations()->get_state());
6754                 Locations::LocationList copy (_session->locations()->list());
6755
6756                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6757
6758                         Locations::LocationList::const_iterator tmp;
6759
6760                         bool const was_locked = (*i)->locked ();
6761                         if (locked_markers_too) {
6762                                 (*i)->unlock ();
6763                         }
6764
6765                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6766
6767                                 if ((*i)->start() >= pos) {
6768                                         (*i)->set_start ((*i)->start() + frames);
6769                                         if (!(*i)->is_mark()) {
6770                                                 (*i)->set_end ((*i)->end() + frames);
6771                                         }
6772                                         moved = true;
6773                                 }
6774
6775                         }
6776
6777                         if (was_locked) {
6778                                 (*i)->lock ();
6779                         }
6780                 }
6781
6782                 if (moved) {
6783                         XMLNode& after (_session->locations()->get_state());
6784                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6785                 }
6786         }
6787
6788         if (tempo_too) {
6789                 _session->tempo_map().insert_time (pos, frames);
6790         }
6791
6792         if (commit) {
6793                 commit_reversible_command ();
6794         }
6795 }
6796
6797 void
6798 Editor::fit_selected_tracks ()
6799 {
6800         if (!selection->tracks.empty()) {
6801                 fit_tracks (selection->tracks);
6802         } else {
6803                 TrackViewList tvl;
6804
6805                 /* no selected tracks - use tracks with selected regions */
6806
6807                 if (!selection->regions.empty()) {
6808                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6809                                 tvl.push_back (&(*r)->get_time_axis_view ());
6810                         }
6811
6812                         if (!tvl.empty()) {
6813                                 fit_tracks (tvl);
6814                         }
6815                 } else if (internal_editing()) {
6816                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6817                            the entered track
6818                         */
6819                         if (entered_track) {
6820                                 tvl.push_back (entered_track);
6821                                 fit_tracks (tvl);
6822                         }
6823                 }
6824         }
6825 }
6826
6827 void
6828 Editor::fit_tracks (TrackViewList & tracks)
6829 {
6830         if (tracks.empty()) {
6831                 return;
6832         }
6833
6834         uint32_t child_heights = 0;
6835         int visible_tracks = 0;
6836
6837         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6838
6839                 if (!(*t)->marked_for_display()) {
6840                         continue;
6841                 }
6842
6843                 child_heights += (*t)->effective_height() - (*t)->current_height();
6844                 ++visible_tracks;
6845         }
6846
6847         uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
6848         double first_y_pos = DBL_MAX;
6849
6850         if (h < TimeAxisView::preset_height (HeightSmall)) {
6851                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6852                 /* too small to be displayed */
6853                 return;
6854         }
6855
6856         undo_visual_stack.push_back (current_visual_state (true));
6857         no_save_visual = true;
6858
6859         /* build a list of all tracks, including children */
6860
6861         TrackViewList all;
6862         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6863                 all.push_back (*i);
6864                 TimeAxisView::Children c = (*i)->get_child_list ();
6865                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6866                         all.push_back (j->get());
6867                 }
6868         }
6869
6870         /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6871
6872         bool prev_was_selected = false;
6873         bool is_selected = tracks.contains (all.front());
6874         bool next_is_selected;
6875
6876         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6877
6878                 TrackViewList::iterator next;
6879
6880                 next = t;
6881                 ++next;
6882
6883                 if (next != all.end()) {
6884                         next_is_selected = tracks.contains (*next);
6885                 } else {
6886                         next_is_selected = false;
6887                 }
6888
6889                 if ((*t)->marked_for_display ()) {
6890                         if (is_selected) {
6891                                 (*t)->set_height (h);
6892                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6893                         } else {
6894                                 if (prev_was_selected && next_is_selected) {
6895                                         hide_track_in_display (*t);
6896                                 }
6897                         }
6898                 }
6899
6900                 prev_was_selected = is_selected;
6901                 is_selected = next_is_selected;
6902         }
6903
6904         /*
6905            set the controls_layout height now, because waiting for its size
6906            request signal handler will cause the vertical adjustment setting to fail
6907         */
6908
6909         controls_layout.property_height () = _full_canvas_height;
6910         vertical_adjustment.set_value (first_y_pos);
6911
6912         redo_visual_stack.push_back (current_visual_state (true));
6913 }
6914
6915 void
6916 Editor::save_visual_state (uint32_t n)
6917 {
6918         while (visual_states.size() <= n) {
6919                 visual_states.push_back (0);
6920         }
6921
6922         if (visual_states[n] != 0) {
6923                 delete visual_states[n];
6924         }
6925
6926         visual_states[n] = current_visual_state (true);
6927         gdk_beep ();
6928 }
6929
6930 void
6931 Editor::goto_visual_state (uint32_t n)
6932 {
6933         if (visual_states.size() <= n) {
6934                 return;
6935         }
6936
6937         if (visual_states[n] == 0) {
6938                 return;
6939         }
6940
6941         use_visual_state (*visual_states[n]);
6942 }
6943
6944 void
6945 Editor::start_visual_state_op (uint32_t n)
6946 {
6947         save_visual_state (n);
6948         
6949         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6950         char buf[32];
6951         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6952         pup->set_text (buf);
6953         pup->touch();
6954 }
6955
6956 void
6957 Editor::cancel_visual_state_op (uint32_t n)
6958 {
6959         goto_visual_state (n);
6960 }
6961
6962 void
6963 Editor::toggle_region_mute ()
6964 {
6965         if (_ignore_region_action) {
6966                 return;
6967         }
6968
6969         RegionSelection rs = get_regions_from_selection_and_entered ();
6970
6971         if (rs.empty ()) {
6972                 return;
6973         }
6974
6975         if (rs.size() > 1) {
6976                 begin_reversible_command (_("mute regions"));
6977         } else {
6978                 begin_reversible_command (_("mute region"));
6979         }
6980
6981         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6982
6983                 (*i)->region()->playlist()->clear_changes ();
6984                 (*i)->region()->set_muted (!(*i)->region()->muted ());
6985                 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6986
6987         }
6988
6989         commit_reversible_command ();
6990 }
6991
6992 void
6993 Editor::combine_regions ()
6994 {
6995         /* foreach track with selected regions, take all selected regions
6996            and join them into a new region containing the subregions (as a
6997            playlist)
6998         */
6999
7000         typedef set<RouteTimeAxisView*> RTVS;
7001         RTVS tracks;
7002
7003         if (selection->regions.empty()) {
7004                 return;
7005         }
7006
7007         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7008                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7009
7010                 if (rtv) {
7011                         tracks.insert (rtv);
7012                 }
7013         }
7014
7015         begin_reversible_command (_("combine regions"));
7016
7017         vector<RegionView*> new_selection;
7018
7019         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7020                 RegionView* rv;
7021
7022                 if ((rv = (*i)->combine_regions ()) != 0) {
7023                         new_selection.push_back (rv);
7024                 }
7025         }
7026
7027         selection->clear_regions ();
7028         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7029                 selection->add (*i);
7030         }
7031
7032         commit_reversible_command ();
7033 }
7034
7035 void
7036 Editor::uncombine_regions ()
7037 {
7038         typedef set<RouteTimeAxisView*> RTVS;
7039         RTVS tracks;
7040
7041         if (selection->regions.empty()) {
7042                 return;
7043         }
7044
7045         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7046                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7047
7048                 if (rtv) {
7049                         tracks.insert (rtv);
7050                 }
7051         }
7052
7053         begin_reversible_command (_("uncombine regions"));
7054
7055         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7056                 (*i)->uncombine_regions ();
7057         }
7058
7059         commit_reversible_command ();
7060 }
7061
7062 void
7063 Editor::toggle_midi_input_active (bool flip_others)
7064 {
7065         bool onoff = false;
7066         boost::shared_ptr<RouteList> rl (new RouteList);
7067
7068         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7069                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7070
7071                 if (!rtav) {
7072                         continue;
7073                 }
7074
7075                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7076
7077                 if (mt) {
7078                         rl->push_back (rtav->route());
7079                         onoff = !mt->input_active();
7080                 }
7081         }
7082         
7083         _session->set_exclusive_input_active (rl, onoff, flip_others);
7084 }