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