NOOP, just re-indent diagram
[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         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3579         current_interthread_info->done = true;
3580         return 0;
3581 }
3582
3583 void
3584 Editor::freeze_route ()
3585 {
3586         if (!_session) {
3587                 return;
3588         }
3589
3590         /* stop transport before we start. this is important */
3591
3592         _session->request_transport_speed (0.0);
3593         
3594         /* wait for just a little while, because the above call is asynchronous */
3595
3596         Glib::usleep (250000);
3597
3598         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3599                 return;
3600         }
3601
3602         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3603                 MessageDialog d (
3604                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3605                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3606                         );
3607                 d.set_title (_("Cannot freeze"));
3608                 d.run ();
3609                 return;
3610         }
3611
3612         if (clicked_routeview->track()->has_external_redirects()) {
3613                 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"
3614                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3615                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3616
3617                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3618                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3619                 d.set_title (_("Freeze Limits"));
3620
3621                 int response = d.run ();
3622
3623                 switch (response) {
3624                 case Gtk::RESPONSE_CANCEL:
3625                         return;
3626                 default:
3627                         break;
3628                 }
3629         }
3630
3631         InterThreadInfo itt;
3632         current_interthread_info = &itt;
3633
3634         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3635
3636         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3637
3638         set_canvas_cursor (_cursors->wait);
3639
3640         while (!itt.done && !itt.cancel) {
3641                 gtk_main_iteration ();
3642         }
3643
3644         current_interthread_info = 0;
3645         set_canvas_cursor (current_canvas_cursor);
3646 }
3647
3648 void
3649 Editor::bounce_range_selection (bool replace, bool enable_processing)
3650 {
3651         if (selection->time.empty()) {
3652                 return;
3653         }
3654
3655         TrackSelection views = selection->tracks;
3656
3657         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3658
3659                 if (enable_processing) {
3660
3661                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3662
3663                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3664                                 MessageDialog d (
3665                                         _("You can't perform this operation because the processing of the signal "
3666                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3667                                           "You can do this without processing, which is a different operation.")
3668                                         );
3669                                 d.set_title (_("Cannot bounce"));
3670                                 d.run ();
3671                                 return;
3672                         }
3673                 }
3674         }
3675
3676         framepos_t start = selection->time[clicked_selection].start;
3677         framepos_t end = selection->time[clicked_selection].end;
3678         framepos_t cnt = end - start + 1;
3679
3680         begin_reversible_command (_("bounce range"));
3681
3682         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3683
3684                 RouteTimeAxisView* rtv;
3685
3686                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3687                         continue;
3688                 }
3689
3690                 boost::shared_ptr<Playlist> playlist;
3691
3692                 if ((playlist = rtv->playlist()) == 0) {
3693                         return;
3694                 }
3695
3696                 InterThreadInfo itt;
3697
3698                 playlist->clear_changes ();
3699                 playlist->clear_owned_changes ();
3700
3701                 boost::shared_ptr<Region> r;
3702
3703                 if (enable_processing) {
3704                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3705                 } else {
3706                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3707                 }
3708
3709                 if (!r) {
3710                         continue;
3711                 }
3712
3713                 if (replace) {
3714                         list<AudioRange> ranges;
3715                         ranges.push_back (AudioRange (start, start+cnt, 0));
3716                         playlist->cut (ranges); // discard result
3717                         playlist->add_region (r, start);
3718                 }
3719
3720                 vector<Command*> cmds;
3721                 playlist->rdiff (cmds);
3722                 _session->add_commands (cmds);
3723
3724                 _session->add_command (new StatefulDiffCommand (playlist));
3725         }
3726
3727         commit_reversible_command ();
3728 }
3729
3730 /** Delete selected regions, automation points or a time range */
3731 void
3732 Editor::delete_ ()
3733 {
3734         cut_copy (Delete);
3735 }
3736
3737 /** Cut selected regions, automation points or a time range */
3738 void
3739 Editor::cut ()
3740 {
3741         cut_copy (Cut);
3742 }
3743
3744 /** Copy selected regions, automation points or a time range */
3745 void
3746 Editor::copy ()
3747 {
3748         cut_copy (Copy);
3749 }
3750
3751
3752 /** @return true if a Cut, Copy or Clear is possible */
3753 bool
3754 Editor::can_cut_copy () const
3755 {
3756         switch (effective_mouse_mode()) {
3757
3758         case MouseObject:
3759                 if (!selection->regions.empty() || !selection->points.empty()) {
3760                         return true;
3761                 }
3762                 break;
3763
3764         case MouseRange:
3765                 if (!selection->time.empty()) {
3766                         return true;
3767                 }
3768                 break;
3769
3770         default:
3771                 break;
3772         }
3773
3774         return false;
3775 }
3776
3777
3778 /** Cut, copy or clear selected regions, automation points or a time range.
3779  * @param op Operation (Cut, Copy or Clear)
3780  */
3781 void
3782 Editor::cut_copy (CutCopyOp op)
3783 {
3784         /* only cancel selection if cut/copy is successful.*/
3785
3786         string opname;
3787
3788         switch (op) {
3789         case Delete:
3790                 opname = _("delete");
3791                 break;
3792         case Cut:
3793                 opname = _("cut");
3794                 break;
3795         case Copy:
3796                 opname = _("copy");
3797                 break;
3798         case Clear:
3799                 opname = _("clear");
3800                 break;
3801         }
3802
3803         /* if we're deleting something, and the mouse is still pressed,
3804            the thing we started a drag for will be gone when we release
3805            the mouse button(s). avoid this. see part 2 at the end of
3806            this function.
3807         */
3808
3809         if (op == Delete || op == Cut || op == Clear) {
3810                 if (_drags->active ()) {
3811                         _drags->abort ();
3812                 }
3813         }
3814
3815         if ( op != Clear )  //"Delete" doesn't change copy/paste buf
3816                 cut_buffer->clear ();
3817
3818         if (entered_marker) {
3819
3820                 /* cut/delete op while pointing at a marker */
3821
3822                 bool ignored;
3823                 Location* loc = find_location_from_marker (entered_marker, ignored);
3824
3825                 if (_session && loc) {
3826                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3827                 }
3828
3829                 _drags->abort ();
3830                 return;
3831         }
3832
3833         if (internal_editing()) {
3834
3835                 switch (effective_mouse_mode()) {
3836                 case MouseObject:
3837                 case MouseRange:
3838                         begin_reversible_command (opname + ' ' + X_("MIDI"));
3839                         cut_copy_midi (op);
3840                         commit_reversible_command ();
3841                         break;
3842                 default:
3843                         break;
3844                 }
3845
3846                 return;
3847         }
3848
3849         bool did_edit = false;
3850
3851         switch (effective_mouse_mode()) {
3852         case MouseGain:
3853                 if (!selection->points.empty()) {
3854                         begin_reversible_command (opname + _(" points"));
3855                         did_edit = true;
3856                         cut_copy_points (op);
3857                         if (op == Cut || op == Delete) {
3858                                 selection->clear_points ();
3859                         }
3860                 }
3861                 break;
3862                 
3863         case MouseObject: 
3864
3865                 if (!selection->regions.empty() || !selection->points.empty()) {
3866
3867                         string thing_name;
3868
3869                         if (selection->regions.empty()) {
3870                                 thing_name = _("points");
3871                         } else if (selection->points.empty()) {
3872                                 thing_name = _("regions");
3873                         } else {
3874                                 thing_name = _("objects");
3875                         }
3876                 
3877                         begin_reversible_command (opname + ' ' + thing_name);
3878                         did_edit = true;
3879
3880                         if (!selection->regions.empty()) {
3881                                 cut_copy_regions (op, selection->regions);
3882                                 
3883                                 if (op == Cut || op == Delete) {
3884                                         selection->clear_regions ();
3885                                 }
3886                         }
3887                         
3888                         if (!selection->points.empty()) {
3889                                 cut_copy_points (op);
3890                                 
3891                                 if (op == Cut || op == Delete) {
3892                                         selection->clear_points ();
3893                                 }
3894                         }
3895                 } 
3896                 break;
3897                         
3898         case MouseRange:
3899                 if (selection->time.empty()) {
3900                         framepos_t start, end;
3901                         /* no time selection, see if we can get an edit range
3902                            and use that.
3903                         */
3904                         if (get_edit_op_range (start, end)) {
3905                                 selection->set (start, end);
3906                         }
3907                 }
3908                 if (!selection->time.empty()) {
3909                         begin_reversible_command (opname + _(" range"));
3910
3911                         did_edit = true;
3912                         cut_copy_ranges (op);
3913                         
3914                         if (op == Cut || op == Delete) {
3915                                 selection->clear_time ();
3916                         }
3917                 }
3918                 break;
3919                 
3920         default:
3921                 break;
3922         }
3923         
3924         if (did_edit) {
3925                 commit_reversible_command ();   
3926         }
3927         
3928         if (op == Delete || op == Cut || op == Clear) {
3929                 _drags->abort ();
3930         }
3931 }
3932
3933 struct AutomationRecord {
3934         AutomationRecord () : state (0) {}
3935         AutomationRecord (XMLNode* s) : state (s) {}
3936         
3937         XMLNode* state; ///< state before any operation
3938         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3939 };
3940
3941 /** Cut, copy or clear selected automation points.
3942  *  @param op Operation (Cut, Copy or Clear)
3943  */
3944 void
3945 Editor::cut_copy_points (CutCopyOp op)
3946 {
3947         if (selection->points.empty ()) {
3948                 return;
3949         }
3950
3951         /* XXX: not ideal, as there may be more than one track involved in the point selection */
3952         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3953
3954         /* Keep a record of the AutomationLists that we end up using in this operation */
3955         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3956         Lists lists;
3957
3958         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3959         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3960                 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3961                 if (lists.find (al) == lists.end ()) {
3962                         /* We haven't seen this list yet, so make a record for it.  This includes
3963                            taking a copy of its current state, in case this is needed for undo later.
3964                         */
3965                         lists[al] = AutomationRecord (&al->get_state ());
3966                 }
3967         }
3968
3969         if (op == Cut || op == Copy) {
3970                 /* This operation will involve putting things in the cut buffer, so create an empty
3971                    ControlList for each of our source lists to put the cut buffer data in.
3972                 */
3973                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3974                         i->second.copy = i->first->create (i->first->parameter ());
3975                 }
3976
3977                 /* Add all selected points to the relevant copy ControlLists */
3978                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3979                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3980                         AutomationList::const_iterator j = (*i)->model ();
3981                         lists[al].copy->add ((*j)->when, (*j)->value);
3982                 }
3983
3984                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3985                         /* Correct this copy list so that it starts at time 0 */
3986                         double const start = i->second.copy->front()->when;
3987                         for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3988                                 (*j)->when -= start;
3989                         }
3990
3991                         /* And add it to the cut buffer */
3992                         cut_buffer->add (i->second.copy);
3993                 }
3994         }
3995                 
3996         if (op == Delete || op == Cut) {
3997                 /* This operation needs to remove things from the main AutomationList, so do that now */
3998                 
3999                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4000                         i->first->freeze ();
4001                 }
4002
4003                 /* Remove each selected point from its AutomationList */
4004                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4005                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
4006                         al->erase ((*i)->model ());
4007                 }
4008
4009                 /* Thaw the lists and add undo records for them */
4010                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4011                         boost::shared_ptr<AutomationList> al = i->first;
4012                         al->thaw ();
4013                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4014                 }
4015         }
4016 }
4017
4018 /** Cut, copy or clear selected automation points.
4019  * @param op Operation (Cut, Copy or Clear)
4020  */
4021 void
4022 Editor::cut_copy_midi (CutCopyOp op)
4023 {
4024         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4025                 MidiRegionView* mrv = *i;
4026                 mrv->cut_copy_clear (op);
4027         }
4028 }
4029
4030
4031
4032 struct lt_playlist {
4033     bool operator () (const PlaylistState& a, const PlaylistState& b) {
4034             return a.playlist < b.playlist;
4035     }
4036 };
4037
4038 struct PlaylistMapping {
4039     TimeAxisView* tv;
4040     boost::shared_ptr<Playlist> pl;
4041
4042     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4043 };
4044
4045 /** Remove `clicked_regionview' */
4046 void
4047 Editor::remove_clicked_region ()
4048 {
4049         if (clicked_routeview == 0 || clicked_regionview == 0) {
4050                 return;
4051         }
4052
4053         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4054
4055         begin_reversible_command (_("remove region"));
4056         playlist->clear_changes ();
4057         playlist->clear_owned_changes ();
4058         playlist->remove_region (clicked_regionview->region());
4059
4060         /* We might have removed regions, which alters other regions' layering_index,
4061            so we need to do a recursive diff here.
4062         */
4063         vector<Command*> cmds;
4064         playlist->rdiff (cmds);
4065         _session->add_commands (cmds);
4066         
4067         _session->add_command(new StatefulDiffCommand (playlist));
4068         commit_reversible_command ();
4069 }
4070
4071
4072 /** Remove the selected regions */
4073 void
4074 Editor::remove_selected_regions ()
4075 {
4076         RegionSelection rs = get_regions_from_selection_and_entered ();
4077
4078         if (!_session || rs.empty()) {
4079                 return;
4080         }
4081
4082         begin_reversible_command (_("remove region"));
4083
4084         list<boost::shared_ptr<Region> > regions_to_remove;
4085
4086         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4087                 // we can't just remove the region(s) in this loop because
4088                 // this removes them from the RegionSelection, and they thus
4089                 // disappear from underneath the iterator, and the ++i above
4090                 // SEGVs in a puzzling fashion.
4091
4092                 // so, first iterate over the regions to be removed from rs and
4093                 // add them to the regions_to_remove list, and then
4094                 // iterate over the list to actually remove them.
4095
4096                 regions_to_remove.push_back ((*i)->region());
4097         }
4098
4099         vector<boost::shared_ptr<Playlist> > playlists;
4100
4101         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4102
4103                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4104
4105                 if (!playlist) {
4106                         // is this check necessary?
4107                         continue;
4108                 }
4109
4110                 /* get_regions_from_selection_and_entered() guarantees that
4111                    the playlists involved are unique, so there is no need
4112                    to check here.
4113                 */
4114
4115                 playlists.push_back (playlist);
4116
4117                 playlist->clear_changes ();
4118                 playlist->clear_owned_changes ();
4119                 playlist->freeze ();
4120                 playlist->remove_region (*rl);
4121         }
4122
4123         vector<boost::shared_ptr<Playlist> >::iterator pl;
4124
4125         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4126                 (*pl)->thaw ();
4127
4128                 /* We might have removed regions, which alters other regions' layering_index,
4129                    so we need to do a recursive diff here.
4130                 */
4131                 vector<Command*> cmds;
4132                 (*pl)->rdiff (cmds);
4133                 _session->add_commands (cmds);
4134                 
4135                 _session->add_command(new StatefulDiffCommand (*pl));
4136         }
4137
4138         commit_reversible_command ();
4139 }
4140
4141 /** Cut, copy or clear selected regions.
4142  * @param op Operation (Cut, Copy or Clear)
4143  */
4144 void
4145 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4146 {
4147         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4148            a map when we want ordered access to both elements. i think.
4149         */
4150
4151         vector<PlaylistMapping> pmap;
4152
4153         framepos_t first_position = max_framepos;
4154
4155         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4156         FreezeList freezelist;
4157
4158         /* get ordering correct before we cut/copy */
4159
4160         rs.sort_by_position_and_track ();
4161
4162         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4163
4164                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4165
4166                 if (op == Cut || op == Clear || op == Delete) {
4167                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4168
4169                         if (pl) {
4170                                 FreezeList::iterator fl;
4171
4172                                 // only take state if this is a new playlist.
4173                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4174                                         if ((*fl) == pl) {
4175                                                 break;
4176                                         }
4177                                 }
4178
4179                                 if (fl == freezelist.end()) {
4180                                         pl->clear_changes();
4181                                         pl->clear_owned_changes ();
4182                                         pl->freeze ();
4183                                         freezelist.insert (pl);
4184                                 }
4185                         }
4186                 }
4187
4188                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4189                 vector<PlaylistMapping>::iterator z;
4190
4191                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4192                         if ((*z).tv == tv) {
4193                                 break;
4194                         }
4195                 }
4196
4197                 if (z == pmap.end()) {
4198                         pmap.push_back (PlaylistMapping (tv));
4199                 }
4200         }
4201
4202         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4203
4204                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4205
4206                 if (!pl) {
4207                         /* region not yet associated with a playlist (e.g. unfinished
4208                            capture pass.
4209                         */
4210                         ++x;
4211                         continue;
4212                 }
4213
4214                 TimeAxisView& tv = (*x)->get_time_axis_view();
4215                 boost::shared_ptr<Playlist> npl;
4216                 RegionSelection::iterator tmp;
4217
4218                 tmp = x;
4219                 ++tmp;
4220
4221                 if (op != Delete) {
4222
4223                         vector<PlaylistMapping>::iterator z;
4224                         
4225                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4226                                 if ((*z).tv == &tv) {
4227                                         break;
4228                                 }
4229                         }
4230                         
4231                         assert (z != pmap.end());
4232                         
4233                         if (!(*z).pl) {
4234                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4235                                 npl->freeze();
4236                                 (*z).pl = npl;
4237                         } else {
4238                                 npl = (*z).pl;
4239                         }
4240                 }
4241
4242                 boost::shared_ptr<Region> r = (*x)->region();
4243                 boost::shared_ptr<Region> _xx;
4244
4245                 assert (r != 0);
4246
4247                 switch (op) {
4248                 case Delete:
4249                         pl->remove_region (r);
4250                         break;
4251                         
4252                 case Cut:
4253                         _xx = RegionFactory::create (r);
4254                         npl->add_region (_xx, r->position() - first_position);
4255                         pl->remove_region (r);
4256                         break;
4257
4258                 case Copy:
4259                         /* copy region before adding, so we're not putting same object into two different playlists */
4260                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4261                         break;
4262
4263                 case Clear:
4264                         pl->remove_region (r);  
4265                         break;
4266                 }
4267
4268                 x = tmp;
4269         }
4270
4271         if (op != Delete) {
4272
4273                 list<boost::shared_ptr<Playlist> > foo;
4274                 
4275                 /* the pmap is in the same order as the tracks in which selected regions occured */
4276                 
4277                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4278                         if ((*i).pl) {
4279                                 (*i).pl->thaw();
4280                                 foo.push_back ((*i).pl);
4281                         }
4282                 }
4283                 
4284                 if (!foo.empty()) {
4285                         cut_buffer->set (foo);
4286                 }
4287                 
4288                 if (pmap.empty()) {
4289                         _last_cut_copy_source_track = 0;
4290                 } else {
4291                         _last_cut_copy_source_track = pmap.front().tv;
4292                 }
4293         }
4294
4295         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4296                 (*pl)->thaw ();
4297
4298                 /* We might have removed regions, which alters other regions' layering_index,
4299                    so we need to do a recursive diff here.
4300                 */
4301                 vector<Command*> cmds;
4302                 (*pl)->rdiff (cmds);
4303                 _session->add_commands (cmds);
4304                 
4305                 _session->add_command (new StatefulDiffCommand (*pl));
4306         }
4307 }
4308
4309 void
4310 Editor::cut_copy_ranges (CutCopyOp op)
4311 {
4312         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4313
4314         /* Sort the track selection now, so that it if is used, the playlists
4315            selected by the calls below to cut_copy_clear are in the order that
4316            their tracks appear in the editor.  This makes things like paste
4317            of ranges work properly.
4318         */
4319
4320         sort_track_selection (ts);
4321
4322         if (ts.empty()) {
4323                 if (!entered_track) {
4324                         return;
4325                 }
4326                 ts.push_back (entered_track);
4327         } 
4328
4329         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4330                 (*i)->cut_copy_clear (*selection, op);
4331         }
4332 }
4333
4334 void
4335 Editor::paste (float times, bool from_context)
4336 {
4337         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4338
4339         paste_internal (get_preferred_edit_position (false, from_context), times);
4340 }
4341
4342 void
4343 Editor::mouse_paste ()
4344 {
4345         framepos_t where;
4346         bool ignored;
4347
4348         if (!mouse_frame (where, ignored)) {
4349                 return;
4350         }
4351
4352         snap_to (where);
4353         paste_internal (where, 1);
4354 }
4355
4356 void
4357 Editor::paste_internal (framepos_t position, float times)
4358 {
4359         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4360
4361         if (internal_editing()) {
4362                 if (cut_buffer->midi_notes.empty()) {
4363                         return;
4364                 }
4365         } else {
4366                 if (cut_buffer->empty()) {
4367                         return;
4368                 }
4369         }
4370
4371         if (position == max_framepos) {
4372                 position = get_preferred_edit_position();
4373                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4374         }
4375
4376         TrackViewList ts;
4377         TrackViewList::iterator i;
4378         size_t nth;
4379
4380         /* get everything in the correct order */
4381
4382         if (_edit_point == Editing::EditAtMouse && entered_track) {
4383                 /* With the mouse edit point, paste onto the track under the mouse */
4384                 ts.push_back (entered_track);
4385         } else if (!selection->tracks.empty()) {
4386                 /* Otherwise, if there are some selected tracks, paste to them */
4387                 ts = selection->tracks.filter_to_unique_playlists ();
4388                 sort_track_selection (ts);
4389         } else if (_last_cut_copy_source_track) {
4390                 /* Otherwise paste to the track that the cut/copy came from;
4391                    see discussion in mantis #3333.
4392                 */
4393                 ts.push_back (_last_cut_copy_source_track);
4394         }
4395
4396         if (internal_editing ()) {
4397
4398                 /* undo/redo is handled by individual tracks/regions */
4399
4400                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4401
4402                         RegionSelection rs;
4403                         RegionSelection::iterator r;
4404                         MidiNoteSelection::iterator cb;
4405
4406                         get_regions_at (rs, position, ts);
4407
4408                         for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4409                              cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4410                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4411                                 if (mrv) {
4412                                         mrv->paste (position, times, **cb);
4413                                         ++cb;
4414                                 }
4415                         }
4416                 }
4417
4418         } else {
4419
4420                 /* we do redo (do you do voodoo?) */
4421
4422                 begin_reversible_command (Operations::paste);
4423
4424                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4425                         (*i)->paste (position, times, *cut_buffer, nth);
4426                 }
4427
4428                 commit_reversible_command ();
4429         }
4430 }
4431
4432 void
4433 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4434 {
4435         boost::shared_ptr<Playlist> playlist;
4436         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4437         RegionSelection foo;
4438
4439         framepos_t const start_frame = regions.start ();
4440         framepos_t const end_frame = regions.end_frame ();
4441
4442         begin_reversible_command (Operations::duplicate_region);
4443
4444         selection->clear_regions ();
4445
4446         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4447
4448                 boost::shared_ptr<Region> r ((*i)->region());
4449
4450                 TimeAxisView& tv = (*i)->get_time_axis_view();
4451                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4452                 latest_regionviews.clear ();
4453                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4454
4455                 playlist = (*i)->region()->playlist();
4456                 playlist->clear_changes ();
4457                 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4458                 _session->add_command(new StatefulDiffCommand (playlist));
4459
4460                 c.disconnect ();
4461
4462                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4463         }
4464
4465         commit_reversible_command ();
4466
4467         if (!foo.empty()) {
4468                 selection->set (foo);
4469         }
4470 }
4471
4472 void
4473 Editor::duplicate_selection (float times)
4474 {
4475         if (selection->time.empty() || selection->tracks.empty()) {
4476                 return;
4477         }
4478
4479         boost::shared_ptr<Playlist> playlist;
4480         vector<boost::shared_ptr<Region> > new_regions;
4481         vector<boost::shared_ptr<Region> >::iterator ri;
4482
4483         create_region_from_selection (new_regions);
4484
4485         if (new_regions.empty()) {
4486                 return;
4487         }
4488
4489         begin_reversible_command (_("duplicate selection"));
4490
4491         ri = new_regions.begin();
4492
4493         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4494
4495         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4496                 if ((playlist = (*i)->playlist()) == 0) {
4497                         continue;
4498                 }
4499                 playlist->clear_changes ();
4500                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4501                 _session->add_command (new StatefulDiffCommand (playlist));
4502
4503                 ++ri;
4504                 if (ri == new_regions.end()) {
4505                         --ri;
4506                 }
4507         }
4508
4509         commit_reversible_command ();
4510 }
4511
4512 /** Reset all selected points to the relevant default value */
4513 void
4514 Editor::reset_point_selection ()
4515 {
4516         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4517                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4518                 (*j)->value = (*i)->line().the_list()->default_value ();
4519         }
4520 }
4521
4522 void
4523 Editor::center_playhead ()
4524 {
4525         float const page = _visible_canvas_width * samples_per_pixel;
4526         center_screen_internal (playhead_cursor->current_frame (), page);
4527 }
4528
4529 void
4530 Editor::center_edit_point ()
4531 {
4532         float const page = _visible_canvas_width * samples_per_pixel;
4533         center_screen_internal (get_preferred_edit_position(), page);
4534 }
4535
4536 /** Caller must begin and commit a reversible command */
4537 void
4538 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4539 {
4540         playlist->clear_changes ();
4541         playlist->clear ();
4542         _session->add_command (new StatefulDiffCommand (playlist));
4543 }
4544
4545 void
4546 Editor::nudge_track (bool use_edit, bool forwards)
4547 {
4548         boost::shared_ptr<Playlist> playlist;
4549         framepos_t distance;
4550         framepos_t next_distance;
4551         framepos_t start;
4552
4553         if (use_edit) {
4554                 start = get_preferred_edit_position();
4555         } else {
4556                 start = 0;
4557         }
4558
4559         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4560                 return;
4561         }
4562
4563         if (selection->tracks.empty()) {
4564                 return;
4565         }
4566
4567         begin_reversible_command (_("nudge track"));
4568
4569         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4570
4571         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4572
4573                 if ((playlist = (*i)->playlist()) == 0) {
4574                         continue;
4575                 }
4576
4577                 playlist->clear_changes ();
4578                 playlist->clear_owned_changes ();
4579
4580                 playlist->nudge_after (start, distance, forwards);
4581
4582                 vector<Command*> cmds;
4583
4584                 playlist->rdiff (cmds);
4585                 _session->add_commands (cmds);
4586
4587                 _session->add_command (new StatefulDiffCommand (playlist));
4588         }
4589
4590         commit_reversible_command ();
4591 }
4592
4593 void
4594 Editor::remove_last_capture ()
4595 {
4596         vector<string> choices;
4597         string prompt;
4598
4599         if (!_session) {
4600                 return;
4601         }
4602
4603         if (Config->get_verify_remove_last_capture()) {
4604                 prompt  = _("Do you really want to destroy the last capture?"
4605                             "\n(This is destructive and cannot be undone)");
4606
4607                 choices.push_back (_("No, do nothing."));
4608                 choices.push_back (_("Yes, destroy it."));
4609
4610                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4611
4612                 if (prompter.run () == 1) {
4613                         _session->remove_last_capture ();
4614                         _regions->redisplay ();
4615                 }
4616
4617         } else {
4618                 _session->remove_last_capture();
4619                 _regions->redisplay ();
4620         }
4621 }
4622
4623 void
4624 Editor::normalize_region ()
4625 {
4626         if (!_session) {
4627                 return;
4628         }
4629
4630         RegionSelection rs = get_regions_from_selection_and_entered ();
4631
4632         if (rs.empty()) {
4633                 return;
4634         }
4635
4636         NormalizeDialog dialog (rs.size() > 1);
4637
4638         if (dialog.run () == RESPONSE_CANCEL) {
4639                 return;
4640         }
4641
4642         set_canvas_cursor (_cursors->wait);
4643         gdk_flush ();
4644
4645         /* XXX: should really only count audio regions here */
4646         int const regions = rs.size ();
4647
4648         /* Make a list of the selected audio regions' maximum amplitudes, and also
4649            obtain the maximum amplitude of them all.
4650         */
4651         list<double> max_amps;
4652         double max_amp = 0;
4653         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4654                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4655                 if (arv) {
4656                         dialog.descend (1.0 / regions);
4657                         double const a = arv->audio_region()->maximum_amplitude (&dialog);
4658
4659                         if (a == -1) {
4660                                 /* the user cancelled the operation */
4661                                 set_canvas_cursor (current_canvas_cursor);
4662                                 return;
4663                         }
4664
4665                         max_amps.push_back (a);
4666                         max_amp = max (max_amp, a);
4667                         dialog.ascend ();
4668                 }
4669         }
4670
4671         begin_reversible_command (_("normalize"));
4672
4673         list<double>::const_iterator a = max_amps.begin ();
4674
4675         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4676                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4677                 if (!arv) {
4678                         continue;
4679                 }
4680
4681                 arv->region()->clear_changes ();
4682
4683                 double const amp = dialog.normalize_individually() ? *a : max_amp;
4684
4685                 arv->audio_region()->normalize (amp, dialog.target ());
4686                 _session->add_command (new StatefulDiffCommand (arv->region()));
4687
4688                 ++a;
4689         }
4690
4691         commit_reversible_command ();
4692         set_canvas_cursor (current_canvas_cursor);
4693 }
4694
4695
4696 void
4697 Editor::reset_region_scale_amplitude ()
4698 {
4699         if (!_session) {
4700                 return;
4701         }
4702
4703         RegionSelection rs = get_regions_from_selection_and_entered ();
4704
4705         if (rs.empty()) {
4706                 return;
4707         }
4708
4709         begin_reversible_command ("reset gain");
4710
4711         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4712                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4713                 if (!arv)
4714                         continue;
4715                 arv->region()->clear_changes ();
4716                 arv->audio_region()->set_scale_amplitude (1.0f);
4717                 _session->add_command (new StatefulDiffCommand (arv->region()));
4718         }
4719
4720         commit_reversible_command ();
4721 }
4722
4723 void
4724 Editor::adjust_region_gain (bool up)
4725 {
4726         RegionSelection rs = get_regions_from_selection_and_entered ();
4727
4728         if (!_session || rs.empty()) {
4729                 return;
4730         }
4731
4732         begin_reversible_command ("adjust region gain");
4733
4734         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4735                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4736                 if (!arv) {
4737                         continue;
4738                 }
4739
4740                 arv->region()->clear_changes ();
4741
4742                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4743
4744                 if (up) {
4745                         dB += 1;
4746                 } else {
4747                         dB -= 1;
4748                 }
4749
4750                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4751                 _session->add_command (new StatefulDiffCommand (arv->region()));
4752         }
4753
4754         commit_reversible_command ();
4755 }
4756
4757
4758 void
4759 Editor::reverse_region ()
4760 {
4761         if (!_session) {
4762                 return;
4763         }
4764
4765         Reverse rev (*_session);
4766         apply_filter (rev, _("reverse regions"));
4767 }
4768
4769 void
4770 Editor::strip_region_silence ()
4771 {
4772         if (!_session) {
4773                 return;
4774         }
4775
4776         RegionSelection rs = get_regions_from_selection_and_entered ();
4777
4778         if (rs.empty()) {
4779                 return;
4780         }
4781
4782         std::list<RegionView*> audio_only;
4783
4784         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4785                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4786                 if (arv) {
4787                         audio_only.push_back (arv);
4788                 }
4789         }
4790
4791         StripSilenceDialog d (_session, audio_only);
4792         int const r = d.run ();
4793
4794         d.drop_rects ();
4795
4796         if (r == Gtk::RESPONSE_OK) {
4797                 ARDOUR::AudioIntervalMap silences;
4798                 d.silences (silences);
4799                 StripSilence s (*_session, silences, d.fade_length());
4800                 apply_filter (s, _("strip silence"), &d);
4801         }
4802 }
4803
4804 Command*
4805 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4806 {
4807         Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4808         mrv.selection_as_notelist (selected, true);
4809
4810         vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4811         v.push_back (selected);
4812
4813         framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
4814         double     pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4815
4816         return op (mrv.midi_region()->model(), pos_beats, v);
4817 }
4818
4819 void
4820 Editor::apply_midi_note_edit_op (MidiOperator& op)
4821 {
4822         Command* cmd;
4823
4824         RegionSelection rs = get_regions_from_selection_and_entered ();
4825
4826         if (rs.empty()) {
4827                 return;
4828         }
4829
4830         begin_reversible_command (op.name ());
4831
4832         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4833                 RegionSelection::iterator tmp = r;
4834                 ++tmp;
4835
4836                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4837
4838                 if (mrv) {
4839                         cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4840                         if (cmd) {
4841                                 (*cmd)();
4842                                 _session->add_command (cmd);
4843                         }
4844                 }
4845
4846                 r = tmp;
4847         }
4848
4849         commit_reversible_command ();
4850 }
4851
4852 void
4853 Editor::fork_region ()
4854 {
4855         RegionSelection rs = get_regions_from_selection_and_entered ();
4856
4857         if (rs.empty()) {
4858                 return;
4859         }
4860
4861         begin_reversible_command (_("Fork Region(s)"));
4862
4863         set_canvas_cursor (_cursors->wait);
4864         gdk_flush ();
4865
4866         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4867                 RegionSelection::iterator tmp = r;
4868                 ++tmp;
4869
4870                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4871
4872                 if (mrv) {
4873                         try {
4874                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4875                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
4876                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
4877                                 
4878                                 playlist->clear_changes ();
4879                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4880                                 _session->add_command(new StatefulDiffCommand (playlist));
4881                         } catch (...) {
4882                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
4883                         }
4884                 }
4885
4886                 r = tmp;
4887         }
4888
4889         commit_reversible_command ();
4890
4891         set_canvas_cursor (current_canvas_cursor);
4892 }
4893
4894 void
4895 Editor::quantize_region ()
4896 {
4897         int selected_midi_region_cnt = 0;
4898
4899         if (!_session) {
4900                 return;
4901         }
4902
4903         RegionSelection rs = get_regions_from_selection_and_entered ();
4904
4905         if (rs.empty()) {
4906                 return;
4907         }
4908
4909         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4910                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4911                 if (mrv) {
4912                         selected_midi_region_cnt++;
4913                 }
4914         }
4915
4916         if (selected_midi_region_cnt == 0) {
4917                 return;
4918         }
4919
4920         QuantizeDialog* qd = new QuantizeDialog (*this);
4921
4922         qd->present ();
4923         const int r = qd->run ();
4924         qd->hide ();
4925
4926         if (r == Gtk::RESPONSE_OK) {
4927                 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4928                                 qd->start_grid_size(), qd->end_grid_size(),
4929                                 qd->strength(), qd->swing(), qd->threshold());
4930
4931                 apply_midi_note_edit_op (quant);
4932         }
4933 }
4934
4935 void
4936 Editor::insert_patch_change (bool from_context)
4937 {
4938         RegionSelection rs = get_regions_from_selection_and_entered ();
4939
4940         if (rs.empty ()) {
4941                 return;
4942         }
4943
4944         const framepos_t p = get_preferred_edit_position (false, from_context);
4945
4946         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4947            there may be more than one, but the PatchChangeDialog can only offer
4948            one set of patch menus.
4949         */
4950         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4951
4952         Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4953         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4954
4955         if (d.run() == RESPONSE_CANCEL) {
4956                 return;
4957         }
4958
4959         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4960                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4961                 if (mrv) {
4962                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4963                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4964                         }
4965                 }
4966         }
4967 }
4968
4969 void
4970 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4971 {
4972         RegionSelection rs = get_regions_from_selection_and_entered ();
4973
4974         if (rs.empty()) {
4975                 return;
4976         }
4977
4978         begin_reversible_command (command);
4979
4980         set_canvas_cursor (_cursors->wait);
4981         gdk_flush ();
4982
4983         int n = 0;
4984         int const N = rs.size ();
4985
4986         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4987                 RegionSelection::iterator tmp = r;
4988                 ++tmp;
4989
4990                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4991                 if (arv) {
4992                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4993
4994                         if (progress) {
4995                                 progress->descend (1.0 / N);
4996                         }
4997
4998                         if (arv->audio_region()->apply (filter, progress) == 0) {
4999
5000                                 playlist->clear_changes ();
5001                                 playlist->clear_owned_changes ();
5002
5003                                 if (filter.results.empty ()) {
5004
5005                                         /* no regions returned; remove the old one */
5006                                         playlist->remove_region (arv->region ());
5007
5008                                 } else {
5009
5010                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5011
5012                                         /* first region replaces the old one */
5013                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5014                                         ++res;
5015
5016                                         /* add the rest */
5017                                         while (res != filter.results.end()) {
5018                                                 playlist->add_region (*res, (*res)->position());
5019                                                 ++res;
5020                                         }
5021
5022                                 }
5023
5024                                 /* We might have removed regions, which alters other regions' layering_index,
5025                                    so we need to do a recursive diff here.
5026                                 */
5027                                 vector<Command*> cmds;
5028                                 playlist->rdiff (cmds);
5029                                 _session->add_commands (cmds);
5030                                 
5031                                 _session->add_command(new StatefulDiffCommand (playlist));
5032                         } else {
5033                                 goto out;
5034                         }
5035
5036                         if (progress) {
5037                                 progress->ascend ();
5038                         }
5039                 }
5040
5041                 r = tmp;
5042                 ++n;
5043         }
5044
5045         commit_reversible_command ();
5046
5047   out:
5048         set_canvas_cursor (current_canvas_cursor);
5049 }
5050
5051 void
5052 Editor::external_edit_region ()
5053 {
5054         /* more to come */
5055 }
5056
5057 void
5058 Editor::reset_region_gain_envelopes ()
5059 {
5060         RegionSelection rs = get_regions_from_selection_and_entered ();
5061
5062         if (!_session || rs.empty()) {
5063                 return;
5064         }
5065
5066         _session->begin_reversible_command (_("reset region gain"));
5067
5068         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5069                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5070                 if (arv) {
5071                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5072                         XMLNode& before (alist->get_state());
5073
5074                         arv->audio_region()->set_default_envelope ();
5075                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5076                 }
5077         }
5078
5079         _session->commit_reversible_command ();
5080 }
5081
5082 void
5083 Editor::set_region_gain_visibility (RegionView* rv)
5084 {
5085         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5086         if (arv) {
5087                 arv->update_envelope_visibility();
5088         }
5089 }
5090
5091 void
5092 Editor::set_gain_envelope_visibility ()
5093 {
5094         if (!_session) {
5095                 return;
5096         }
5097
5098         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5099                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5100                 if (v) {
5101                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5102                 }
5103         }
5104 }
5105
5106 void
5107 Editor::toggle_gain_envelope_active ()
5108 {
5109         if (_ignore_region_action) {
5110                 return;
5111         }
5112
5113         RegionSelection rs = get_regions_from_selection_and_entered ();
5114
5115         if (!_session || rs.empty()) {
5116                 return;
5117         }
5118
5119         _session->begin_reversible_command (_("region gain envelope active"));
5120
5121         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5122                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5123                 if (arv) {
5124                         arv->region()->clear_changes ();
5125                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5126                         _session->add_command (new StatefulDiffCommand (arv->region()));
5127                 }
5128         }
5129
5130         _session->commit_reversible_command ();
5131 }
5132
5133 void
5134 Editor::toggle_region_lock ()
5135 {
5136         if (_ignore_region_action) {
5137                 return;
5138         }
5139
5140         RegionSelection rs = get_regions_from_selection_and_entered ();
5141
5142         if (!_session || rs.empty()) {
5143                 return;
5144         }
5145
5146         _session->begin_reversible_command (_("toggle region lock"));
5147
5148         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5149                 (*i)->region()->clear_changes ();
5150                 (*i)->region()->set_locked (!(*i)->region()->locked());
5151                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5152         }
5153
5154         _session->commit_reversible_command ();
5155 }
5156
5157 void
5158 Editor::toggle_region_video_lock ()
5159 {
5160         if (_ignore_region_action) {
5161                 return;
5162         }
5163
5164         RegionSelection rs = get_regions_from_selection_and_entered ();
5165
5166         if (!_session || rs.empty()) {
5167                 return;
5168         }
5169
5170         _session->begin_reversible_command (_("Toggle Video Lock"));
5171
5172         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5173                 (*i)->region()->clear_changes ();
5174                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5175                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5176         }
5177
5178         _session->commit_reversible_command ();
5179 }
5180
5181 void
5182 Editor::toggle_region_lock_style ()
5183 {
5184         if (_ignore_region_action) {
5185                 return;
5186         }
5187
5188         RegionSelection rs = get_regions_from_selection_and_entered ();
5189
5190         if (!_session || rs.empty()) {
5191                 return;
5192         }
5193
5194         _session->begin_reversible_command (_("region lock style"));
5195
5196         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5197                 (*i)->region()->clear_changes ();
5198                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5199                 (*i)->region()->set_position_lock_style (ns);
5200                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5201         }
5202
5203         _session->commit_reversible_command ();
5204 }
5205
5206 void
5207 Editor::toggle_opaque_region ()
5208 {
5209         if (_ignore_region_action) {
5210                 return;
5211         }
5212
5213         RegionSelection rs = get_regions_from_selection_and_entered ();
5214
5215         if (!_session || rs.empty()) {
5216                 return;
5217         }
5218
5219         _session->begin_reversible_command (_("change region opacity"));
5220
5221         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5222                 (*i)->region()->clear_changes ();
5223                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5224                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5225         }
5226
5227         _session->commit_reversible_command ();
5228 }
5229
5230 void
5231 Editor::toggle_record_enable ()
5232 {
5233         bool new_state = false;
5234         bool first = true;
5235         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5236                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5237                 if (!rtav)
5238                         continue;
5239                 if (!rtav->is_track())
5240                         continue;
5241
5242                 if (first) {
5243                         new_state = !rtav->track()->record_enabled();
5244                         first = false;
5245                 }
5246
5247                 rtav->track()->set_record_enabled (new_state, this);
5248         }
5249 }
5250
5251 void
5252 Editor::toggle_solo ()
5253 {
5254         bool new_state = false;
5255         bool first = true;
5256         boost::shared_ptr<RouteList> rl (new RouteList);
5257
5258         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5259                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5260
5261                 if (!rtav) {
5262                         continue;
5263                 }
5264
5265                 if (first) {
5266                         new_state = !rtav->route()->soloed ();
5267                         first = false;
5268                 }
5269
5270                 rl->push_back (rtav->route());
5271         }
5272
5273         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5274 }
5275
5276 void
5277 Editor::toggle_mute ()
5278 {
5279         bool new_state = false;
5280         bool first = true;
5281         boost::shared_ptr<RouteList> rl (new RouteList);
5282
5283         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5284                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5285
5286                 if (!rtav) {
5287                         continue;
5288                 }
5289
5290                 if (first) {
5291                         new_state = !rtav->route()->muted();
5292                         first = false;
5293                 }
5294
5295                 rl->push_back (rtav->route());
5296         }
5297
5298         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5299 }
5300
5301 void
5302 Editor::toggle_solo_isolate ()
5303 {
5304 }
5305
5306 void
5307 Editor::set_fade_length (bool in)
5308 {
5309         RegionSelection rs = get_regions_from_selection_and_entered ();
5310
5311         if (rs.empty()) {
5312                 return;
5313         }
5314
5315         /* we need a region to measure the offset from the start */
5316
5317         RegionView* rv = rs.front ();
5318
5319         framepos_t pos = get_preferred_edit_position();
5320         framepos_t len;
5321         char const * cmd;
5322
5323         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5324                 /* edit point is outside the relevant region */
5325                 return;
5326         }
5327
5328         if (in) {
5329                 if (pos <= rv->region()->position()) {
5330                         /* can't do it */
5331                         return;
5332                 }
5333                 len = pos - rv->region()->position();
5334                 cmd = _("set fade in length");
5335         } else {
5336                 if (pos >= rv->region()->last_frame()) {
5337                         /* can't do it */
5338                         return;
5339                 }
5340                 len = rv->region()->last_frame() - pos;
5341                 cmd = _("set fade out length");
5342         }
5343
5344         begin_reversible_command (cmd);
5345
5346         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5347                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5348
5349                 if (!tmp) {
5350                         return;
5351                 }
5352
5353                 boost::shared_ptr<AutomationList> alist;
5354                 if (in) {
5355                         alist = tmp->audio_region()->fade_in();
5356                 } else {
5357                         alist = tmp->audio_region()->fade_out();
5358                 }
5359
5360                 XMLNode &before = alist->get_state();
5361
5362                 if (in) {
5363                         tmp->audio_region()->set_fade_in_length (len);
5364                         tmp->audio_region()->set_fade_in_active (true);
5365                 } else {
5366                         tmp->audio_region()->set_fade_out_length (len);
5367                         tmp->audio_region()->set_fade_out_active (true);
5368                 }
5369
5370                 XMLNode &after = alist->get_state();
5371                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5372         }
5373
5374         commit_reversible_command ();
5375 }
5376
5377 void
5378 Editor::set_fade_in_shape (FadeShape shape)
5379 {
5380         RegionSelection rs = get_regions_from_selection_and_entered ();
5381
5382         if (rs.empty()) {
5383                 return;
5384         }
5385
5386         begin_reversible_command (_("set fade in shape"));
5387
5388         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5389                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5390
5391                 if (!tmp) {
5392                         return;
5393                 }
5394
5395                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5396                 XMLNode &before = alist->get_state();
5397
5398                 tmp->audio_region()->set_fade_in_shape (shape);
5399
5400                 XMLNode &after = alist->get_state();
5401                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5402         }
5403
5404         commit_reversible_command ();
5405
5406 }
5407
5408 void
5409 Editor::set_fade_out_shape (FadeShape shape)
5410 {
5411         RegionSelection rs = get_regions_from_selection_and_entered ();
5412
5413         if (rs.empty()) {
5414                 return;
5415         }
5416
5417         begin_reversible_command (_("set fade out shape"));
5418
5419         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5420                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5421
5422                 if (!tmp) {
5423                         return;
5424                 }
5425
5426                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5427                 XMLNode &before = alist->get_state();
5428
5429                 tmp->audio_region()->set_fade_out_shape (shape);
5430
5431                 XMLNode &after = alist->get_state();
5432                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5433         }
5434
5435         commit_reversible_command ();
5436 }
5437
5438 void
5439 Editor::set_fade_in_active (bool yn)
5440 {
5441         RegionSelection rs = get_regions_from_selection_and_entered ();
5442
5443         if (rs.empty()) {
5444                 return;
5445         }
5446
5447         begin_reversible_command (_("set fade in active"));
5448
5449         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5450                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5451
5452                 if (!tmp) {
5453                         return;
5454                 }
5455
5456
5457                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5458
5459                 ar->clear_changes ();
5460                 ar->set_fade_in_active (yn);
5461                 _session->add_command (new StatefulDiffCommand (ar));
5462         }
5463
5464         commit_reversible_command ();
5465 }
5466
5467 void
5468 Editor::set_fade_out_active (bool yn)
5469 {
5470         RegionSelection rs = get_regions_from_selection_and_entered ();
5471
5472         if (rs.empty()) {
5473                 return;
5474         }
5475
5476         begin_reversible_command (_("set fade out active"));
5477
5478         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5479                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5480
5481                 if (!tmp) {
5482                         return;
5483                 }
5484
5485                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5486
5487                 ar->clear_changes ();
5488                 ar->set_fade_out_active (yn);
5489                 _session->add_command(new StatefulDiffCommand (ar));
5490         }
5491
5492         commit_reversible_command ();
5493 }
5494
5495 void
5496 Editor::toggle_region_fades (int dir)
5497 {
5498         if (_ignore_region_action) {
5499                 return;
5500         }
5501         
5502         boost::shared_ptr<AudioRegion> ar;
5503         bool yn = false;
5504
5505         RegionSelection rs = get_regions_from_selection_and_entered ();
5506
5507         if (rs.empty()) {
5508                 return;
5509         }
5510
5511         RegionSelection::iterator i;
5512         for (i = rs.begin(); i != rs.end(); ++i) {
5513                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5514                         if (dir == -1) {
5515                                 yn = ar->fade_out_active ();
5516                         } else {
5517                                 yn = ar->fade_in_active ();
5518                         }
5519                         break;
5520                 }
5521         }
5522
5523         if (i == rs.end()) {
5524                 return;
5525         }
5526
5527         /* XXX should this undo-able? */
5528
5529         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5530                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5531                         continue;
5532                 }
5533                 if (dir == 1 || dir == 0) {
5534                         ar->set_fade_in_active (!yn);
5535                 }
5536
5537                 if (dir == -1 || dir == 0) {
5538                         ar->set_fade_out_active (!yn);
5539                 }
5540         }
5541 }
5542
5543
5544 /** Update region fade visibility after its configuration has been changed */
5545 void
5546 Editor::update_region_fade_visibility ()
5547 {
5548         bool _fade_visibility = _session->config.get_show_region_fades ();
5549
5550         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5551                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5552                 if (v) {
5553                         if (_fade_visibility) {
5554                                 v->audio_view()->show_all_fades ();
5555                         } else {
5556                                 v->audio_view()->hide_all_fades ();
5557                         }
5558                 }
5559         }
5560 }
5561
5562 void
5563 Editor::set_edit_point ()
5564 {
5565         framepos_t where;
5566         bool ignored;
5567
5568         if (!mouse_frame (where, ignored)) {
5569                 return;
5570         }
5571
5572         snap_to (where);
5573
5574         if (selection->markers.empty()) {
5575
5576                 mouse_add_new_marker (where);
5577
5578         } else {
5579                 bool ignored;
5580
5581                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5582
5583                 if (loc) {
5584                         loc->move_to (where);
5585                 }
5586         }
5587 }
5588
5589 void
5590 Editor::set_playhead_cursor ()
5591 {
5592         if (entered_marker) {
5593                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5594         } else {
5595                 framepos_t where;
5596                 bool ignored;
5597
5598                 if (!mouse_frame (where, ignored)) {
5599                         return;
5600                 }
5601
5602                 snap_to (where);
5603
5604                 if (_session) {
5605                         _session->request_locate (where, _session->transport_rolling());
5606                 }
5607         }
5608
5609         if ( Config->get_always_play_range() )
5610                 cancel_time_selection();
5611 }
5612
5613 void
5614 Editor::split_region ()
5615 {
5616         if ( !selection->time.empty()) {
5617                 separate_regions_between (selection->time);
5618                 return;
5619         }
5620
5621         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5622
5623         framepos_t where = get_preferred_edit_position ();
5624
5625         if (rs.empty()) {
5626                 return;
5627         }
5628
5629         split_regions_at (where, rs);
5630 }
5631
5632 struct EditorOrderRouteSorter {
5633     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5634             return a->order_key () < b->order_key ();
5635     }
5636 };
5637
5638 void
5639 Editor::select_next_route()
5640 {
5641         if (selection->tracks.empty()) {
5642                 selection->set (track_views.front());
5643                 return;
5644         }
5645
5646         TimeAxisView* current = selection->tracks.front();
5647
5648         RouteUI *rui;
5649         do {
5650                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5651                         if (*i == current) {
5652                                 ++i;
5653                                 if (i != track_views.end()) {
5654                                         current = (*i);
5655                                 } else {
5656                                         current = (*(track_views.begin()));
5657                                         //selection->set (*(track_views.begin()));
5658                                 }
5659                                 break;
5660                         }
5661                 }
5662                 rui = dynamic_cast<RouteUI *>(current);
5663         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5664
5665         selection->set(current);
5666
5667         ensure_track_visible(current);
5668 }
5669
5670 void
5671 Editor::select_prev_route()
5672 {
5673         if (selection->tracks.empty()) {
5674                 selection->set (track_views.front());
5675                 return;
5676         }
5677
5678         TimeAxisView* current = selection->tracks.front();
5679
5680         RouteUI *rui;
5681         do {
5682                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5683                         if (*i == current) {
5684                                 ++i;
5685                                 if (i != track_views.rend()) {
5686                                         current = (*i);
5687                                 } else {
5688                                         current = *(track_views.rbegin());
5689                                 }
5690                                 break;
5691                         }
5692                 }
5693                 rui = dynamic_cast<RouteUI *>(current);
5694         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5695
5696         selection->set (current);
5697
5698         ensure_track_visible(current);
5699 }
5700
5701 void
5702 Editor::ensure_track_visible(TimeAxisView *track)
5703 {
5704         if (track->hidden())
5705                 return;
5706
5707         double const current_view_min_y = vertical_adjustment.get_value();
5708         double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
5709
5710         double const track_min_y = track->y_position ();
5711         double const track_max_y = track->y_position () + track->effective_height ();
5712
5713         if (track_min_y >= current_view_min_y &&
5714             track_max_y <= current_view_max_y) {
5715                 return;
5716         }
5717
5718         double new_value;
5719
5720         if (track_min_y < current_view_min_y) {
5721                 // Track is above the current view
5722                 new_value = track_min_y;
5723         } else {
5724                 // Track is below the current view
5725                 new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
5726         }
5727
5728         vertical_adjustment.set_value(new_value);
5729 }
5730
5731 void
5732 Editor::set_loop_from_selection (bool play)
5733 {
5734         if (_session == 0 || selection->time.empty()) {
5735                 return;
5736         }
5737
5738         framepos_t start = selection->time[clicked_selection].start;
5739         framepos_t end = selection->time[clicked_selection].end;
5740
5741         set_loop_range (start, end,  _("set loop range from selection"));
5742
5743         if (play) {
5744                 _session->request_play_loop (true);
5745                 _session->request_locate (start, true);
5746         }
5747 }
5748
5749 void
5750 Editor::set_loop_from_edit_range (bool play)
5751 {
5752         if (_session == 0) {
5753                 return;
5754         }
5755
5756         framepos_t start;
5757         framepos_t end;
5758
5759         if (!get_edit_op_range (start, end)) {
5760                 return;
5761         }
5762
5763         set_loop_range (start, end,  _("set loop range from edit range"));
5764
5765         if (play) {
5766                 _session->request_play_loop (true);
5767                 _session->request_locate (start, true);
5768         }
5769 }
5770
5771 void
5772 Editor::set_loop_from_region (bool play)
5773 {
5774         framepos_t start = max_framepos;
5775         framepos_t end = 0;
5776
5777         RegionSelection rs = get_regions_from_selection_and_entered ();
5778
5779         if (rs.empty()) {
5780                 return;
5781         }
5782
5783         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5784                 if ((*i)->region()->position() < start) {
5785                         start = (*i)->region()->position();
5786                 }
5787                 if ((*i)->region()->last_frame() + 1 > end) {
5788                         end = (*i)->region()->last_frame() + 1;
5789                 }
5790         }
5791
5792         set_loop_range (start, end, _("set loop range from region"));
5793
5794         if (play) {
5795                 _session->request_play_loop (true);
5796                 _session->request_locate (start, true);
5797         }
5798 }
5799
5800 void
5801 Editor::set_punch_from_selection ()
5802 {
5803         if (_session == 0 || selection->time.empty()) {
5804                 return;
5805         }
5806
5807         framepos_t start = selection->time[clicked_selection].start;
5808         framepos_t end = selection->time[clicked_selection].end;
5809
5810         set_punch_range (start, end,  _("set punch range from selection"));
5811 }
5812
5813 void
5814 Editor::set_punch_from_edit_range ()
5815 {
5816         if (_session == 0) {
5817                 return;
5818         }
5819
5820         framepos_t start;
5821         framepos_t end;
5822
5823         if (!get_edit_op_range (start, end)) {
5824                 return;
5825         }
5826
5827         set_punch_range (start, end,  _("set punch range from edit range"));
5828 }
5829
5830 void
5831 Editor::set_punch_from_region ()
5832 {
5833         framepos_t start = max_framepos;
5834         framepos_t end = 0;
5835
5836         RegionSelection rs = get_regions_from_selection_and_entered ();
5837
5838         if (rs.empty()) {
5839                 return;
5840         }
5841
5842         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5843                 if ((*i)->region()->position() < start) {
5844                         start = (*i)->region()->position();
5845                 }
5846                 if ((*i)->region()->last_frame() + 1 > end) {
5847                         end = (*i)->region()->last_frame() + 1;
5848                 }
5849         }
5850
5851         set_punch_range (start, end, _("set punch range from region"));
5852 }
5853
5854 void
5855 Editor::pitch_shift_region ()
5856 {
5857         RegionSelection rs = get_regions_from_selection_and_entered ();
5858
5859         RegionSelection audio_rs;
5860         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5861                 if (dynamic_cast<AudioRegionView*> (*i)) {
5862                         audio_rs.push_back (*i);
5863                 }
5864         }
5865
5866         if (audio_rs.empty()) {
5867                 return;
5868         }
5869
5870         pitch_shift (audio_rs, 1.2);
5871 }
5872
5873 void
5874 Editor::transpose_region ()
5875 {
5876         RegionSelection rs = get_regions_from_selection_and_entered ();
5877
5878         list<MidiRegionView*> midi_region_views;
5879         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5880                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5881                 if (mrv) {
5882                         midi_region_views.push_back (mrv);
5883                 }
5884         }
5885
5886         TransposeDialog d;
5887         int const r = d.run ();
5888         if (r != RESPONSE_ACCEPT) {
5889                 return;
5890         }
5891
5892         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5893                 (*i)->midi_region()->transpose (d.semitones ());
5894         }
5895 }
5896
5897 void
5898 Editor::set_tempo_from_region ()
5899 {
5900         RegionSelection rs = get_regions_from_selection_and_entered ();
5901
5902         if (!_session || rs.empty()) {
5903                 return;
5904         }
5905
5906         RegionView* rv = rs.front();
5907
5908         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5909 }
5910
5911 void
5912 Editor::use_range_as_bar ()
5913 {
5914         framepos_t start, end;
5915         if (get_edit_op_range (start, end)) {
5916                 define_one_bar (start, end);
5917         }
5918 }
5919
5920 void
5921 Editor::define_one_bar (framepos_t start, framepos_t end)
5922 {
5923         framepos_t length = end - start;
5924
5925         const Meter& m (_session->tempo_map().meter_at (start));
5926
5927         /* length = 1 bar */
5928
5929         /* now we want frames per beat.
5930            we have frames per bar, and beats per bar, so ...
5931         */
5932
5933         /* XXXX METER MATH */
5934
5935         double frames_per_beat = length / m.divisions_per_bar();
5936
5937         /* beats per minute = */
5938
5939         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5940
5941         /* now decide whether to:
5942
5943             (a) set global tempo
5944             (b) add a new tempo marker
5945
5946         */
5947
5948         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5949
5950         bool do_global = false;
5951
5952         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5953
5954                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5955                    at the start, or create a new marker
5956                 */
5957
5958                 vector<string> options;
5959                 options.push_back (_("Cancel"));
5960                 options.push_back (_("Add new marker"));
5961                 options.push_back (_("Set global tempo"));
5962
5963                 Choice c (
5964                         _("Define one bar"),
5965                         _("Do you want to set the global tempo or add a new tempo marker?"),
5966                         options
5967                         );
5968
5969                 c.set_default_response (2);
5970
5971                 switch (c.run()) {
5972                 case 0:
5973                         return;
5974
5975                 case 2:
5976                         do_global = true;
5977                         break;
5978
5979                 default:
5980                         do_global = false;
5981                 }
5982
5983         } else {
5984
5985                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5986                    if the marker is at the region starter, change it, otherwise add
5987                    a new tempo marker
5988                 */
5989         }
5990
5991         begin_reversible_command (_("set tempo from region"));
5992         XMLNode& before (_session->tempo_map().get_state());
5993
5994         if (do_global) {
5995                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5996         } else if (t.frame() == start) {
5997                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5998         } else {
5999                 Timecode::BBT_Time bbt;
6000                 _session->tempo_map().bbt_time (start, bbt);
6001                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
6002         }
6003
6004         XMLNode& after (_session->tempo_map().get_state());
6005
6006         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6007         commit_reversible_command ();
6008 }
6009
6010 void
6011 Editor::split_region_at_transients ()
6012 {
6013         AnalysisFeatureList positions;
6014
6015         RegionSelection rs = get_regions_from_selection_and_entered ();
6016
6017         if (!_session || rs.empty()) {
6018                 return;
6019         }
6020
6021         _session->begin_reversible_command (_("split regions"));
6022
6023         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6024
6025                 RegionSelection::iterator tmp;
6026
6027                 tmp = i;
6028                 ++tmp;
6029
6030                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6031
6032                 if (ar && (ar->get_transients (positions) == 0)) {
6033                         split_region_at_points ((*i)->region(), positions, true);
6034                         positions.clear ();
6035                 }
6036
6037                 i = tmp;
6038         }
6039
6040         _session->commit_reversible_command ();
6041
6042 }
6043
6044 void
6045 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6046 {
6047         bool use_rhythmic_rodent = false;
6048
6049         boost::shared_ptr<Playlist> pl = r->playlist();
6050
6051         list<boost::shared_ptr<Region> > new_regions;
6052
6053         if (!pl) {
6054                 return;
6055         }
6056
6057         if (positions.empty()) {
6058                 return;
6059         }
6060
6061
6062         if (positions.size() > 20 && can_ferret) {
6063                 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);
6064                 MessageDialog msg (msgstr,
6065                                    false,
6066                                    Gtk::MESSAGE_INFO,
6067                                    Gtk::BUTTONS_OK_CANCEL);
6068
6069                 if (can_ferret) {
6070                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6071                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6072                 } else {
6073                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6074                 }
6075
6076                 msg.set_title (_("Excessive split?"));
6077                 msg.present ();
6078
6079                 int response = msg.run();
6080                 msg.hide ();
6081
6082                 switch (response) {
6083                 case RESPONSE_OK:
6084                         break;
6085                 case RESPONSE_APPLY:
6086                         use_rhythmic_rodent = true;
6087                         break;
6088                 default:
6089                         return;
6090                 }
6091         }
6092
6093         if (use_rhythmic_rodent) {
6094                 show_rhythm_ferret ();
6095                 return;
6096         }
6097
6098         AnalysisFeatureList::const_iterator x;
6099
6100         pl->clear_changes ();
6101         pl->clear_owned_changes ();
6102
6103         x = positions.begin();
6104
6105         if (x == positions.end()) {
6106                 return;
6107         }
6108
6109         pl->freeze ();
6110         pl->remove_region (r);
6111
6112         framepos_t pos = 0;
6113
6114         while (x != positions.end()) {
6115
6116                 /* deal with positons that are out of scope of present region bounds */
6117                 if (*x <= 0 || *x > r->length()) {
6118                         ++x;
6119                         continue;
6120                 }
6121
6122                 /* file start = original start + how far we from the initial position ?
6123                  */
6124
6125                 framepos_t file_start = r->start() + pos;
6126
6127                 /* length = next position - current position
6128                  */
6129
6130                 framepos_t len = (*x) - pos;
6131
6132                 /* XXX we do we really want to allow even single-sample regions?
6133                    shouldn't we have some kind of lower limit on region size?
6134                 */
6135
6136                 if (len <= 0) {
6137                         break;
6138                 }
6139
6140                 string new_name;
6141
6142                 if (RegionFactory::region_name (new_name, r->name())) {
6143                         break;
6144                 }
6145
6146                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6147
6148                 PropertyList plist;
6149
6150                 plist.add (ARDOUR::Properties::start, file_start);
6151                 plist.add (ARDOUR::Properties::length, len);
6152                 plist.add (ARDOUR::Properties::name, new_name);
6153                 plist.add (ARDOUR::Properties::layer, 0);
6154
6155                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6156                 /* because we set annouce to false, manually add the new region to the
6157                    RegionFactory map
6158                 */
6159                 RegionFactory::map_add (nr);
6160
6161                 pl->add_region (nr, r->position() + pos);
6162
6163                 if (select_new) {
6164                         new_regions.push_front(nr);
6165                 }
6166
6167                 pos += len;
6168                 ++x;
6169         }
6170
6171         string new_name;
6172
6173         RegionFactory::region_name (new_name, r->name());
6174
6175         /* Add the final region */
6176         PropertyList plist;
6177
6178         plist.add (ARDOUR::Properties::start, r->start() + pos);
6179         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6180         plist.add (ARDOUR::Properties::name, new_name);
6181         plist.add (ARDOUR::Properties::layer, 0);
6182
6183         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6184         /* because we set annouce to false, manually add the new region to the
6185            RegionFactory map
6186         */
6187         RegionFactory::map_add (nr);
6188         pl->add_region (nr, r->position() + pos);
6189
6190         if (select_new) {
6191                 new_regions.push_front(nr);
6192         }
6193
6194         pl->thaw ();
6195
6196         /* We might have removed regions, which alters other regions' layering_index,
6197            so we need to do a recursive diff here.
6198         */
6199         vector<Command*> cmds;
6200         pl->rdiff (cmds);
6201         _session->add_commands (cmds);
6202         
6203         _session->add_command (new StatefulDiffCommand (pl));
6204
6205         if (select_new) {
6206
6207                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6208                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6209                 }
6210         }
6211 }
6212
6213 void
6214 Editor::place_transient()
6215 {
6216         if (!_session) {
6217                 return;
6218         }
6219
6220         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6221
6222         if (rs.empty()) {
6223                 return;
6224         }
6225
6226         framepos_t where = get_preferred_edit_position();
6227
6228         _session->begin_reversible_command (_("place transient"));
6229
6230         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6231                 framepos_t position = (*r)->region()->position();
6232                 (*r)->region()->add_transient(where - position);
6233         }
6234
6235         _session->commit_reversible_command ();
6236 }
6237
6238 void
6239 Editor::remove_transient(ArdourCanvas::Item* item)
6240 {
6241         if (!_session) {
6242                 return;
6243         }
6244
6245         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6246         assert (_line);
6247
6248         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6249         _arv->remove_transient (*(float*) _line->get_data ("position"));
6250 }
6251
6252 void
6253 Editor::snap_regions_to_grid ()
6254 {
6255         list <boost::shared_ptr<Playlist > > used_playlists;
6256
6257         RegionSelection rs = get_regions_from_selection_and_entered ();
6258
6259         if (!_session || rs.empty()) {
6260                 return;
6261         }
6262
6263         _session->begin_reversible_command (_("snap regions to grid"));
6264
6265         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6266
6267                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6268
6269                 if (!pl->frozen()) {
6270                         /* we haven't seen this playlist before */
6271
6272                         /* remember used playlists so we can thaw them later */
6273                         used_playlists.push_back(pl);
6274                         pl->freeze();
6275                 }
6276
6277                 framepos_t start_frame = (*r)->region()->first_frame ();
6278                 snap_to (start_frame);
6279                 (*r)->region()->set_position (start_frame);
6280         }
6281
6282         while (used_playlists.size() > 0) {
6283                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6284                 (*i)->thaw();
6285                 used_playlists.pop_front();
6286         }
6287
6288         _session->commit_reversible_command ();
6289 }
6290
6291 void
6292 Editor::close_region_gaps ()
6293 {
6294         list <boost::shared_ptr<Playlist > > used_playlists;
6295
6296         RegionSelection rs = get_regions_from_selection_and_entered ();
6297
6298         if (!_session || rs.empty()) {
6299                 return;
6300         }
6301
6302         Dialog dialog (_("Close Region Gaps"));
6303
6304         Table table (2, 3);
6305         table.set_spacings (12);
6306         table.set_border_width (12);
6307         Label* l = manage (left_aligned_label (_("Crossfade length")));
6308         table.attach (*l, 0, 1, 0, 1);
6309
6310         SpinButton spin_crossfade (1, 0);
6311         spin_crossfade.set_range (0, 15);
6312         spin_crossfade.set_increments (1, 1);
6313         spin_crossfade.set_value (5);
6314         table.attach (spin_crossfade, 1, 2, 0, 1);
6315
6316         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6317
6318         l = manage (left_aligned_label (_("Pull-back length")));
6319         table.attach (*l, 0, 1, 1, 2);
6320
6321         SpinButton spin_pullback (1, 0);
6322         spin_pullback.set_range (0, 100);
6323         spin_pullback.set_increments (1, 1);
6324         spin_pullback.set_value(30);
6325         table.attach (spin_pullback, 1, 2, 1, 2);
6326
6327         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6328
6329         dialog.get_vbox()->pack_start (table);
6330         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6331         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6332         dialog.show_all ();
6333
6334         if (dialog.run () == RESPONSE_CANCEL) {
6335                 return;
6336         }
6337
6338         framepos_t crossfade_len = spin_crossfade.get_value();
6339         framepos_t pull_back_frames = spin_pullback.get_value();
6340
6341         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6342         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6343
6344         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6345
6346         _session->begin_reversible_command (_("close region gaps"));
6347
6348         int idx = 0;
6349         boost::shared_ptr<Region> last_region;
6350
6351         rs.sort_by_position_and_track();
6352
6353         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6354
6355                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6356
6357                 if (!pl->frozen()) {
6358                         /* we haven't seen this playlist before */
6359
6360                         /* remember used playlists so we can thaw them later */
6361                         used_playlists.push_back(pl);
6362                         pl->freeze();
6363                 }
6364
6365                 framepos_t position = (*r)->region()->position();
6366
6367                 if (idx == 0 || position < last_region->position()){
6368                         last_region = (*r)->region();
6369                         idx++;
6370                         continue;
6371                 }
6372
6373                 (*r)->region()->trim_front( (position - pull_back_frames));
6374                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6375
6376                 last_region = (*r)->region();
6377
6378                 idx++;
6379         }
6380
6381         while (used_playlists.size() > 0) {
6382                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6383                 (*i)->thaw();
6384                 used_playlists.pop_front();
6385         }
6386
6387         _session->commit_reversible_command ();
6388 }
6389
6390 void
6391 Editor::tab_to_transient (bool forward)
6392 {
6393         AnalysisFeatureList positions;
6394
6395         RegionSelection rs = get_regions_from_selection_and_entered ();
6396
6397         if (!_session) {
6398                 return;
6399         }
6400
6401         framepos_t pos = _session->audible_frame ();
6402
6403         if (!selection->tracks.empty()) {
6404
6405                 /* don't waste time searching for transients in duplicate playlists.
6406                  */
6407
6408                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6409
6410                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6411
6412                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6413
6414                         if (rtv) {
6415                                 boost::shared_ptr<Track> tr = rtv->track();
6416                                 if (tr) {
6417                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6418                                         if (pl) {
6419                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6420
6421                                                 if (result >= 0) {
6422                                                         positions.push_back (result);
6423                                                 }
6424                                         }
6425                                 }
6426                         }
6427                 }
6428
6429         } else {
6430
6431                 if (rs.empty()) {
6432                         return;
6433                 }
6434
6435                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6436                         (*r)->region()->get_transients (positions);
6437                 }
6438         }
6439
6440         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6441
6442         if (forward) {
6443                 AnalysisFeatureList::iterator x;
6444
6445                 for (x = positions.begin(); x != positions.end(); ++x) {
6446                         if ((*x) > pos) {
6447                                 break;
6448                         }
6449                 }
6450
6451                 if (x != positions.end ()) {
6452                         _session->request_locate (*x);
6453                 }
6454
6455         } else {
6456                 AnalysisFeatureList::reverse_iterator x;
6457
6458                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6459                         if ((*x) < pos) {
6460                                 break;
6461                         }
6462                 }
6463
6464                 if (x != positions.rend ()) {
6465                         _session->request_locate (*x);
6466                 }
6467         }
6468 }
6469
6470 void
6471 Editor::playhead_forward_to_grid ()
6472 {
6473         if (!_session) {
6474                 return;
6475         }
6476         
6477         framepos_t pos = playhead_cursor->current_frame ();
6478         if (pos < max_framepos - 1) {
6479                 pos += 2;
6480                 snap_to_internal (pos, 1, false);
6481                 _session->request_locate (pos);
6482         }
6483 }
6484
6485
6486 void
6487 Editor::playhead_backward_to_grid ()
6488 {
6489         if (!_session) {
6490                 return;
6491         }
6492         
6493         framepos_t pos = playhead_cursor->current_frame ();
6494         if (pos > 2) {
6495                 pos -= 2;
6496                 snap_to_internal (pos, -1, false);
6497                 _session->request_locate (pos);
6498         }
6499 }
6500
6501 void
6502 Editor::set_track_height (Height h)
6503 {
6504         TrackSelection& ts (selection->tracks);
6505
6506         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6507                 (*x)->set_height_enum (h);
6508         }
6509 }
6510
6511 void
6512 Editor::toggle_tracks_active ()
6513 {
6514         TrackSelection& ts (selection->tracks);
6515         bool first = true;
6516         bool target = false;
6517
6518         if (ts.empty()) {
6519                 return;
6520         }
6521
6522         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6523                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6524
6525                 if (rtv) {
6526                         if (first) {
6527                                 target = !rtv->_route->active();
6528                                 first = false;
6529                         }
6530                         rtv->_route->set_active (target, this);
6531                 }
6532         }
6533 }
6534
6535 void
6536 Editor::remove_tracks ()
6537 {
6538         TrackSelection& ts (selection->tracks);
6539
6540         if (ts.empty()) {
6541                 return;
6542         }
6543
6544         vector<string> choices;
6545         string prompt;
6546         int ntracks = 0;
6547         int nbusses = 0;
6548         const char* trackstr;
6549         const char* busstr;
6550         vector<boost::shared_ptr<Route> > routes;
6551         bool special_bus = false;
6552
6553         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6554                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6555                 if (!rtv) {
6556                         continue;
6557                 }
6558                 if (rtv->is_track()) {
6559                         ntracks++;
6560                 } else {
6561                         nbusses++;
6562                 }
6563                 routes.push_back (rtv->_route);
6564
6565                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6566                         special_bus = true;
6567                 }
6568         }
6569
6570         if (special_bus && !Config->get_allow_special_bus_removal()) {
6571                 MessageDialog msg (_("That would be bad news ...."),
6572                                    false,
6573                                    Gtk::MESSAGE_INFO,
6574                                    Gtk::BUTTONS_OK);
6575                 msg.set_secondary_text (string_compose (_(
6576                                                                 "Removing the master or monitor bus is such a bad idea\n\
6577 that %1 is not going to allow it.\n\
6578 \n\
6579 If you really want to do this sort of thing\n\
6580 edit your ardour.rc file to set the\n\
6581 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6582
6583                 msg.present ();
6584                 msg.run ();
6585                 return;
6586         }
6587
6588         if (ntracks + nbusses == 0) {
6589                 return;
6590         }
6591
6592         if (ntracks > 1) {
6593                 trackstr = _("tracks");
6594         } else {
6595                 trackstr = _("track");
6596         }
6597
6598         if (nbusses > 1) {
6599                 busstr = _("busses");
6600         } else {
6601                 busstr = _("bus");
6602         }
6603
6604         if (ntracks) {
6605                 if (nbusses) {
6606                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6607                                                     "(You may also lose the playlists associated with the %2)\n\n"
6608                                                     "This action cannot be undone, and the session file will be overwritten!"),
6609                                                   ntracks, trackstr, nbusses, busstr);
6610                 } else {
6611                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
6612                                                     "(You may also lose the playlists associated with the %2)\n\n"
6613                                                     "This action cannot be undone, and the session file will be overwritten!"),
6614                                                   ntracks, trackstr);
6615                 }
6616         } else if (nbusses) {
6617                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
6618                                             "This action cannot be undon, and the session file will be overwritten"),
6619                                           nbusses, busstr);
6620         }
6621
6622         choices.push_back (_("No, do nothing."));
6623         if (ntracks + nbusses > 1) {
6624                 choices.push_back (_("Yes, remove them."));
6625         } else {
6626                 choices.push_back (_("Yes, remove it."));
6627         }
6628
6629         string title;
6630         if (ntracks) {
6631                 title = string_compose (_("Remove %1"), trackstr);
6632         } else {
6633                 title = string_compose (_("Remove %1"), busstr);
6634         }
6635
6636         Choice prompter (title, prompt, choices);
6637
6638         if (prompter.run () != 1) {
6639                 return;
6640         }
6641
6642         for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6643                 _session->remove_route (*x);
6644         }
6645 }
6646
6647 void
6648 Editor::do_insert_time ()
6649 {
6650         if (selection->tracks.empty()) {
6651                 return;
6652         }
6653
6654         InsertTimeDialog d (*this);
6655         int response = d.run ();
6656
6657         if (response != RESPONSE_OK) {
6658                 return;
6659         }
6660
6661         if (d.distance() == 0) {
6662                 return;
6663         }
6664
6665         InsertTimeOption opt = d.intersected_region_action ();
6666
6667         insert_time (
6668                 get_preferred_edit_position(),
6669                 d.distance(),
6670                 opt,
6671                 d.all_playlists(),
6672                 d.move_glued(),
6673                 d.move_markers(),
6674                 d.move_glued_markers(),
6675                 d.move_locked_markers(),
6676                 d.move_tempos()
6677                 );
6678 }
6679
6680 void
6681 Editor::insert_time (
6682         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6683         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6684         )
6685 {
6686         bool commit = false;
6687
6688         if (Config->get_edit_mode() == Lock) {
6689                 return;
6690         }
6691
6692         begin_reversible_command (_("insert time"));
6693
6694         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6695
6696         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6697
6698                 /* regions */
6699
6700                 /* don't operate on any playlist more than once, which could
6701                  * happen if "all playlists" is enabled, but there is more
6702                  * than 1 track using playlists "from" a given track.
6703                  */
6704
6705                 set<boost::shared_ptr<Playlist> > pl;
6706
6707                 if (all_playlists) {
6708                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6709                         if (rtav) {
6710                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6711                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6712                                         pl.insert (*p);
6713                                 }
6714                         }
6715                 } else {
6716                         if ((*x)->playlist ()) {
6717                                 pl.insert ((*x)->playlist ());
6718                         }
6719                 }
6720                 
6721                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6722
6723                         (*i)->clear_changes ();
6724                         (*i)->clear_owned_changes ();
6725
6726                         if (opt == SplitIntersected) {
6727                                 (*i)->split (pos);
6728                         }
6729
6730                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6731
6732                         vector<Command*> cmds;
6733                         (*i)->rdiff (cmds);
6734                         _session->add_commands (cmds);
6735
6736                         _session->add_command (new StatefulDiffCommand (*i));
6737                         commit = true;
6738                 }
6739
6740                 /* automation */
6741                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6742                 if (rtav) {
6743                         rtav->route ()->shift (pos, frames);
6744                         commit = true;
6745                 }
6746         }
6747
6748         /* markers */
6749         if (markers_too) {
6750                 bool moved = false;
6751                 XMLNode& before (_session->locations()->get_state());
6752                 Locations::LocationList copy (_session->locations()->list());
6753
6754                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6755
6756                         Locations::LocationList::const_iterator tmp;
6757
6758                         bool const was_locked = (*i)->locked ();
6759                         if (locked_markers_too) {
6760                                 (*i)->unlock ();
6761                         }
6762
6763                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6764
6765                                 if ((*i)->start() >= pos) {
6766                                         (*i)->set_start ((*i)->start() + frames);
6767                                         if (!(*i)->is_mark()) {
6768                                                 (*i)->set_end ((*i)->end() + frames);
6769                                         }
6770                                         moved = true;
6771                                 }
6772
6773                         }
6774
6775                         if (was_locked) {
6776                                 (*i)->lock ();
6777                         }
6778                 }
6779
6780                 if (moved) {
6781                         XMLNode& after (_session->locations()->get_state());
6782                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6783                 }
6784         }
6785
6786         if (tempo_too) {
6787                 _session->tempo_map().insert_time (pos, frames);
6788         }
6789
6790         if (commit) {
6791                 commit_reversible_command ();
6792         }
6793 }
6794
6795 void
6796 Editor::fit_selected_tracks ()
6797 {
6798         if (!selection->tracks.empty()) {
6799                 fit_tracks (selection->tracks);
6800         } else {
6801                 TrackViewList tvl;
6802
6803                 /* no selected tracks - use tracks with selected regions */
6804
6805                 if (!selection->regions.empty()) {
6806                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6807                                 tvl.push_back (&(*r)->get_time_axis_view ());
6808                         }
6809
6810                         if (!tvl.empty()) {
6811                                 fit_tracks (tvl);
6812                         }
6813                 } else if (internal_editing()) {
6814                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6815                            the entered track
6816                         */
6817                         if (entered_track) {
6818                                 tvl.push_back (entered_track);
6819                                 fit_tracks (tvl);
6820                         }
6821                 }
6822         }
6823 }
6824
6825 void
6826 Editor::fit_tracks (TrackViewList & tracks)
6827 {
6828         if (tracks.empty()) {
6829                 return;
6830         }
6831
6832         uint32_t child_heights = 0;
6833         int visible_tracks = 0;
6834
6835         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6836
6837                 if (!(*t)->marked_for_display()) {
6838                         continue;
6839                 }
6840
6841                 child_heights += (*t)->effective_height() - (*t)->current_height();
6842                 ++visible_tracks;
6843         }
6844
6845         uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
6846         double first_y_pos = DBL_MAX;
6847
6848         if (h < TimeAxisView::preset_height (HeightSmall)) {
6849                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6850                 /* too small to be displayed */
6851                 return;
6852         }
6853
6854         undo_visual_stack.push_back (current_visual_state (true));
6855         no_save_visual = true;
6856
6857         /* build a list of all tracks, including children */
6858
6859         TrackViewList all;
6860         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6861                 all.push_back (*i);
6862                 TimeAxisView::Children c = (*i)->get_child_list ();
6863                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6864                         all.push_back (j->get());
6865                 }
6866         }
6867
6868         /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6869
6870         bool prev_was_selected = false;
6871         bool is_selected = tracks.contains (all.front());
6872         bool next_is_selected;
6873
6874         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6875
6876                 TrackViewList::iterator next;
6877
6878                 next = t;
6879                 ++next;
6880
6881                 if (next != all.end()) {
6882                         next_is_selected = tracks.contains (*next);
6883                 } else {
6884                         next_is_selected = false;
6885                 }
6886
6887                 if ((*t)->marked_for_display ()) {
6888                         if (is_selected) {
6889                                 (*t)->set_height (h);
6890                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6891                         } else {
6892                                 if (prev_was_selected && next_is_selected) {
6893                                         hide_track_in_display (*t);
6894                                 }
6895                         }
6896                 }
6897
6898                 prev_was_selected = is_selected;
6899                 is_selected = next_is_selected;
6900         }
6901
6902         /*
6903            set the controls_layout height now, because waiting for its size
6904            request signal handler will cause the vertical adjustment setting to fail
6905         */
6906
6907         controls_layout.property_height () = _full_canvas_height;
6908         vertical_adjustment.set_value (first_y_pos);
6909
6910         redo_visual_stack.push_back (current_visual_state (true));
6911 }
6912
6913 void
6914 Editor::save_visual_state (uint32_t n)
6915 {
6916         while (visual_states.size() <= n) {
6917                 visual_states.push_back (0);
6918         }
6919
6920         if (visual_states[n] != 0) {
6921                 delete visual_states[n];
6922         }
6923
6924         visual_states[n] = current_visual_state (true);
6925         gdk_beep ();
6926 }
6927
6928 void
6929 Editor::goto_visual_state (uint32_t n)
6930 {
6931         if (visual_states.size() <= n) {
6932                 return;
6933         }
6934
6935         if (visual_states[n] == 0) {
6936                 return;
6937         }
6938
6939         use_visual_state (*visual_states[n]);
6940 }
6941
6942 void
6943 Editor::start_visual_state_op (uint32_t n)
6944 {
6945         save_visual_state (n);
6946         
6947         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6948         char buf[32];
6949         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6950         pup->set_text (buf);
6951         pup->touch();
6952 }
6953
6954 void
6955 Editor::cancel_visual_state_op (uint32_t n)
6956 {
6957         goto_visual_state (n);
6958 }
6959
6960 void
6961 Editor::toggle_region_mute ()
6962 {
6963         if (_ignore_region_action) {
6964                 return;
6965         }
6966
6967         RegionSelection rs = get_regions_from_selection_and_entered ();
6968
6969         if (rs.empty ()) {
6970                 return;
6971         }
6972
6973         if (rs.size() > 1) {
6974                 begin_reversible_command (_("mute regions"));
6975         } else {
6976                 begin_reversible_command (_("mute region"));
6977         }
6978
6979         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6980
6981                 (*i)->region()->playlist()->clear_changes ();
6982                 (*i)->region()->set_muted (!(*i)->region()->muted ());
6983                 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6984
6985         }
6986
6987         commit_reversible_command ();
6988 }
6989
6990 void
6991 Editor::combine_regions ()
6992 {
6993         /* foreach track with selected regions, take all selected regions
6994            and join them into a new region containing the subregions (as a
6995            playlist)
6996         */
6997
6998         typedef set<RouteTimeAxisView*> RTVS;
6999         RTVS tracks;
7000
7001         if (selection->regions.empty()) {
7002                 return;
7003         }
7004
7005         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7006                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7007
7008                 if (rtv) {
7009                         tracks.insert (rtv);
7010                 }
7011         }
7012
7013         begin_reversible_command (_("combine regions"));
7014
7015         vector<RegionView*> new_selection;
7016
7017         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7018                 RegionView* rv;
7019
7020                 if ((rv = (*i)->combine_regions ()) != 0) {
7021                         new_selection.push_back (rv);
7022                 }
7023         }
7024
7025         selection->clear_regions ();
7026         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7027                 selection->add (*i);
7028         }
7029
7030         commit_reversible_command ();
7031 }
7032
7033 void
7034 Editor::uncombine_regions ()
7035 {
7036         typedef set<RouteTimeAxisView*> RTVS;
7037         RTVS tracks;
7038
7039         if (selection->regions.empty()) {
7040                 return;
7041         }
7042
7043         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7044                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7045
7046                 if (rtv) {
7047                         tracks.insert (rtv);
7048                 }
7049         }
7050
7051         begin_reversible_command (_("uncombine regions"));
7052
7053         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7054                 (*i)->uncombine_regions ();
7055         }
7056
7057         commit_reversible_command ();
7058 }
7059
7060 void
7061 Editor::toggle_midi_input_active (bool flip_others)
7062 {
7063         bool onoff = false;
7064         boost::shared_ptr<RouteList> rl (new RouteList);
7065
7066         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7067                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7068
7069                 if (!rtav) {
7070                         continue;
7071                 }
7072
7073                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7074
7075                 if (mt) {
7076                         rl->push_back (rtav->route());
7077                         onoff = !mt->input_active();
7078                 }
7079         }
7080         
7081         _session->set_exclusive_input_active (rl, onoff, flip_others);
7082 }