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