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