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