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