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