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