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