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