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