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