Fix various cursor problems.
[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 void
1764 Editor::zoom_to_region (bool both_axes)
1765 {
1766         temporal_zoom_region (both_axes);
1767 }
1768
1769 void
1770 Editor::temporal_zoom_selection (bool both_axes)
1771 {
1772         if (!selection) return;
1773
1774         //if a range is selected, zoom to that
1775         if (!selection->time.empty()) {
1776
1777                 framepos_t start = selection->time.start();
1778                 framepos_t end = selection->time.end_frame();
1779
1780                 calc_extra_zoom_edges(start, end);
1781
1782                 temporal_zoom_by_frame (start, end);
1783
1784                 if (both_axes)
1785                         fit_selected_tracks();
1786
1787         } else {
1788                 temporal_zoom_region (both_axes);
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 (!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() || !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 = selection->time[clicked_selection].start;
2694         framepos_t end = selection->time[clicked_selection].end;
2695
2696         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2697         sort_track_selection (ts);
2698
2699         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2700                 boost::shared_ptr<Region> current;
2701                 boost::shared_ptr<Playlist> playlist;
2702                 framepos_t internal_start;
2703                 string new_name;
2704
2705                 if ((playlist = (*i)->playlist()) == 0) {
2706                         continue;
2707                 }
2708
2709                 if ((current = playlist->top_region_at(start)) == 0) {
2710                         continue;
2711                 }
2712
2713                 internal_start = start - current->position();
2714                 RegionFactory::region_name (new_name, current->name(), true);
2715
2716                 PropertyList plist;
2717
2718                 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2719                 plist.add (ARDOUR::Properties::length, end - start + 1);
2720                 plist.add (ARDOUR::Properties::name, new_name);
2721
2722                 new_regions.push_back (RegionFactory::create (current, plist));
2723         }
2724 }
2725
2726 void
2727 Editor::split_multichannel_region ()
2728 {
2729         RegionSelection rs = get_regions_from_selection_and_entered ();
2730
2731         if (rs.empty()) {
2732                 return;
2733         }
2734
2735         vector< boost::shared_ptr<Region> > v;
2736
2737         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2738                 (*x)->region()->separate_by_channel (*_session, v);
2739         }
2740 }
2741
2742 void
2743 Editor::new_region_from_selection ()
2744 {
2745         region_from_selection ();
2746         cancel_selection ();
2747 }
2748
2749 static void
2750 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2751 {
2752         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2753         // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
2754         case Evoral::OverlapNone:
2755                 break;
2756         default:
2757                 rs->push_back (rv);
2758         }
2759 }
2760
2761 /** Return either:
2762  *    - selected tracks, or if there are none...
2763  *    - tracks containing selected regions, or if there are none...
2764  *    - all tracks
2765  * @return tracks.
2766  */
2767 TrackViewList
2768 Editor::get_tracks_for_range_action () const
2769 {
2770         TrackViewList t;
2771
2772         if (selection->tracks.empty()) {
2773
2774                 /* use tracks with selected regions */
2775
2776                 RegionSelection rs = selection->regions;
2777
2778                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2779                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2780
2781                         if (!t.contains (tv)) {
2782                                 t.push_back (tv);
2783                         }
2784                 }
2785
2786                 if (t.empty()) {
2787                         /* no regions and no tracks: use all tracks */
2788                         t = track_views;
2789                 }
2790
2791         } else {
2792
2793                 t = selection->tracks;
2794         }
2795
2796         return t.filter_to_unique_playlists();
2797 }
2798
2799 void
2800 Editor::separate_regions_between (const TimeSelection& ts)
2801 {
2802         bool in_command = false;
2803         boost::shared_ptr<Playlist> playlist;
2804         RegionSelection new_selection;
2805
2806         TrackViewList tmptracks = get_tracks_for_range_action ();
2807         sort_track_selection (tmptracks);
2808
2809         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2810
2811                 RouteTimeAxisView* rtv;
2812
2813                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2814
2815                         if (rtv->is_track()) {
2816
2817                                 /* no edits to destructive tracks */
2818
2819                                 if (rtv->track()->destructive()) {
2820                                         continue;
2821                                 }
2822
2823                                 if ((playlist = rtv->playlist()) != 0) {
2824
2825                                         playlist->clear_changes ();
2826
2827                                         /* XXX need to consider musical time selections here at some point */
2828
2829                                         double speed = rtv->track()->speed();
2830
2831
2832                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2833
2834                                                 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2835                                                                 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2836
2837                                                 latest_regionviews.clear ();
2838
2839                                                 playlist->partition ((framepos_t)((*t).start * speed),
2840                                                                 (framepos_t)((*t).end * speed), false);
2841
2842                                                 c.disconnect ();
2843
2844                                                 if (!latest_regionviews.empty()) {
2845
2846                                                         rtv->view()->foreach_regionview (sigc::bind (
2847                                                                                 sigc::ptr_fun (add_if_covered),
2848                                                                                 &(*t), &new_selection));
2849
2850                                                         if (!in_command) {
2851                                                                 begin_reversible_command (_("separate"));
2852                                                                 in_command = true;
2853                                                         }
2854
2855                                                         /* pick up changes to existing regions */
2856
2857                                                         vector<Command*> cmds;
2858                                                         playlist->rdiff (cmds);
2859                                                         _session->add_commands (cmds);
2860
2861                                                         /* pick up changes to the playlist itself (adds/removes)
2862                                                          */
2863
2864                                                         _session->add_command(new StatefulDiffCommand (playlist));
2865                                                 }
2866                                         }
2867                                 }
2868                         }
2869                 }
2870         }
2871
2872         if (in_command) {
2873 //              selection->set (new_selection);
2874
2875                 commit_reversible_command ();
2876         }
2877 }
2878
2879 struct PlaylistState {
2880     boost::shared_ptr<Playlist> playlist;
2881     XMLNode*  before;
2882 };
2883
2884 /** Take tracks from get_tracks_for_range_action and cut any regions
2885  *  on those tracks so that the tracks are empty over the time
2886  *  selection.
2887  */
2888 void
2889 Editor::separate_region_from_selection ()
2890 {
2891         /* preferentially use *all* ranges in the time selection if we're in range mode
2892            to allow discontiguous operation, since get_edit_op_range() currently
2893            returns a single range.
2894         */
2895
2896         if (!selection->time.empty()) {
2897
2898                 separate_regions_between (selection->time);
2899
2900         } else {
2901
2902                 framepos_t start;
2903                 framepos_t end;
2904
2905                 if (get_edit_op_range (start, end)) {
2906
2907                         AudioRange ar (start, end, 1);
2908                         TimeSelection ts;
2909                         ts.push_back (ar);
2910
2911                         separate_regions_between (ts);
2912                 }
2913         }
2914 }
2915
2916 void
2917 Editor::separate_region_from_punch ()
2918 {
2919         Location* loc  = _session->locations()->auto_punch_location();
2920         if (loc) {
2921                 separate_regions_using_location (*loc);
2922         }
2923 }
2924
2925 void
2926 Editor::separate_region_from_loop ()
2927 {
2928         Location* loc  = _session->locations()->auto_loop_location();
2929         if (loc) {
2930                 separate_regions_using_location (*loc);
2931         }
2932 }
2933
2934 void
2935 Editor::separate_regions_using_location (Location& loc)
2936 {
2937         if (loc.is_mark()) {
2938                 return;
2939         }
2940
2941         AudioRange ar (loc.start(), loc.end(), 1);
2942         TimeSelection ts;
2943
2944         ts.push_back (ar);
2945
2946         separate_regions_between (ts);
2947 }
2948
2949 /** Separate regions under the selected region */
2950 void
2951 Editor::separate_under_selected_regions ()
2952 {
2953         vector<PlaylistState> playlists;
2954
2955         RegionSelection rs;
2956
2957         rs = get_regions_from_selection_and_entered();
2958
2959         if (!_session || rs.empty()) {
2960                 return;
2961         }
2962
2963         begin_reversible_command (_("separate region under"));
2964
2965         list<boost::shared_ptr<Region> > regions_to_remove;
2966
2967         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2968                 // we can't just remove the region(s) in this loop because
2969                 // this removes them from the RegionSelection, and they thus
2970                 // disappear from underneath the iterator, and the ++i above
2971                 // SEGVs in a puzzling fashion.
2972
2973                 // so, first iterate over the regions to be removed from rs and
2974                 // add them to the regions_to_remove list, and then
2975                 // iterate over the list to actually remove them.
2976
2977                 regions_to_remove.push_back ((*i)->region());
2978         }
2979
2980         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2981
2982                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2983
2984                 if (!playlist) {
2985                         // is this check necessary?
2986                         continue;
2987                 }
2988
2989                 vector<PlaylistState>::iterator i;
2990
2991                 //only take state if this is a new playlist.
2992                 for (i = playlists.begin(); i != playlists.end(); ++i) {
2993                         if ((*i).playlist == playlist) {
2994                                 break;
2995                         }
2996                 }
2997
2998                 if (i == playlists.end()) {
2999
3000                         PlaylistState before;
3001                         before.playlist = playlist;
3002                         before.before = &playlist->get_state();
3003
3004                         playlist->freeze ();
3005                         playlists.push_back(before);
3006                 }
3007
3008                 //Partition on the region bounds
3009                 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3010
3011                 //Re-add region that was just removed due to the partition operation
3012                 playlist->add_region( (*rl), (*rl)->first_frame() );
3013         }
3014
3015         vector<PlaylistState>::iterator pl;
3016
3017         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3018                 (*pl).playlist->thaw ();
3019                 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3020         }
3021
3022         commit_reversible_command ();
3023 }
3024
3025 void
3026 Editor::crop_region_to_selection ()
3027 {
3028         if (!selection->time.empty()) {
3029
3030                 crop_region_to (selection->time.start(), selection->time.end_frame());
3031
3032         } else {
3033
3034                 framepos_t start;
3035                 framepos_t end;
3036
3037                 if (get_edit_op_range (start, end)) {
3038                         crop_region_to (start, end);
3039                 }
3040         }
3041
3042 }
3043
3044 void
3045 Editor::crop_region_to (framepos_t start, framepos_t end)
3046 {
3047         vector<boost::shared_ptr<Playlist> > playlists;
3048         boost::shared_ptr<Playlist> playlist;
3049         TrackViewList ts;
3050
3051         if (selection->tracks.empty()) {
3052                 ts = track_views.filter_to_unique_playlists();
3053         } else {
3054                 ts = selection->tracks.filter_to_unique_playlists ();
3055         }
3056
3057         sort_track_selection (ts);
3058
3059         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3060
3061                 RouteTimeAxisView* rtv;
3062
3063                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
3064
3065                         boost::shared_ptr<Track> t = rtv->track();
3066
3067                         if (t != 0 && ! t->destructive()) {
3068
3069                                 if ((playlist = rtv->playlist()) != 0) {
3070                                         playlists.push_back (playlist);
3071                                 }
3072                         }
3073                 }
3074         }
3075
3076         if (playlists.empty()) {
3077                 return;
3078         }
3079
3080         framepos_t the_start;
3081         framepos_t the_end;
3082         framepos_t cnt;
3083
3084         begin_reversible_command (_("trim to selection"));
3085
3086         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3087
3088                 boost::shared_ptr<Region> region;
3089
3090                 the_start = start;
3091
3092                 if ((region = (*i)->top_region_at(the_start)) == 0) {
3093                         continue;
3094                 }
3095
3096                 /* now adjust lengths to that we do the right thing
3097                    if the selection extends beyond the region
3098                 */
3099
3100                 the_start = max (the_start, (framepos_t) region->position());
3101                 if (max_framepos - the_start < region->length()) {
3102                         the_end = the_start + region->length() - 1;
3103                 } else {
3104                         the_end = max_framepos;
3105                 }
3106                 the_end = min (end, the_end);
3107                 cnt = the_end - the_start + 1;
3108
3109                 region->clear_changes ();
3110                 region->trim_to (the_start, cnt);
3111                 _session->add_command (new StatefulDiffCommand (region));
3112         }
3113
3114         commit_reversible_command ();
3115 }
3116
3117 void
3118 Editor::region_fill_track ()
3119 {
3120         RegionSelection rs = get_regions_from_selection_and_entered ();
3121
3122         if (!_session || rs.empty()) {
3123                 return;
3124         }
3125
3126         framepos_t const end = _session->current_end_frame ();
3127
3128         begin_reversible_command (Operations::region_fill);
3129
3130         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3131
3132                 boost::shared_ptr<Region> region ((*i)->region());
3133
3134                 boost::shared_ptr<Playlist> pl = region->playlist();
3135
3136                 if (end <= region->last_frame()) {
3137                         return;
3138                 }
3139
3140                 double times = (double) (end - region->last_frame()) / (double) region->length();
3141
3142                 if (times == 0) {
3143                         return;
3144                 }
3145
3146                 pl->clear_changes ();
3147                 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
3148                 _session->add_command (new StatefulDiffCommand (pl));
3149         }
3150
3151         commit_reversible_command ();
3152 }
3153
3154 void
3155 Editor::region_fill_selection ()
3156 {
3157         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3158                 return;
3159         }
3160
3161         if (selection->time.empty()) {
3162                 return;
3163         }
3164
3165         boost::shared_ptr<Region> region = _regions->get_single_selection ();
3166         if (region == 0) {
3167                 return;
3168         }
3169
3170         framepos_t start = selection->time[clicked_selection].start;
3171         framepos_t end = selection->time[clicked_selection].end;
3172
3173         boost::shared_ptr<Playlist> playlist;
3174
3175         if (selection->tracks.empty()) {
3176                 return;
3177         }
3178
3179         framepos_t selection_length = end - start;
3180         float times = (float)selection_length / region->length();
3181
3182         begin_reversible_command (Operations::fill_selection);
3183
3184         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3185
3186         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3187
3188                 if ((playlist = (*i)->playlist()) == 0) {
3189                         continue;
3190                 }
3191
3192                 playlist->clear_changes ();
3193                 playlist->add_region (RegionFactory::create (region, true), start, times);
3194                 _session->add_command (new StatefulDiffCommand (playlist));
3195         }
3196
3197         commit_reversible_command ();
3198 }
3199
3200 void
3201 Editor::set_region_sync_position ()
3202 {
3203         set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3204 }
3205
3206 void
3207 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3208 {
3209         bool in_command = false;
3210
3211         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3212
3213                 if (!(*r)->region()->covers (where)) {
3214                         continue;
3215                 }
3216
3217                 boost::shared_ptr<Region> region ((*r)->region());
3218
3219                 if (!in_command) {
3220                         begin_reversible_command (_("set sync point"));
3221                         in_command = true;
3222                 }
3223
3224                 region->clear_changes ();
3225                 region->set_sync_position (where);
3226                 _session->add_command(new StatefulDiffCommand (region));
3227         }
3228
3229         if (in_command) {
3230                 commit_reversible_command ();
3231         }
3232 }
3233
3234 /** Remove the sync positions of the selection */
3235 void
3236 Editor::remove_region_sync ()
3237 {
3238         RegionSelection rs = get_regions_from_selection_and_entered ();
3239
3240         if (rs.empty()) {
3241                 return;
3242         }
3243
3244         begin_reversible_command (_("remove region sync"));
3245
3246         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3247
3248                 (*i)->region()->clear_changes ();
3249                 (*i)->region()->clear_sync_position ();
3250                 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3251         }
3252
3253         commit_reversible_command ();
3254 }
3255
3256 void
3257 Editor::naturalize_region ()
3258 {
3259         RegionSelection rs = get_regions_from_selection_and_entered ();
3260
3261         if (rs.empty()) {
3262                 return;
3263         }
3264
3265         if (rs.size() > 1) {
3266                 begin_reversible_command (_("move regions to original position"));
3267         } else {
3268                 begin_reversible_command (_("move region to original position"));
3269         }
3270
3271         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3272                 (*i)->region()->clear_changes ();
3273                 (*i)->region()->move_to_natural_position ();
3274                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3275         }
3276
3277         commit_reversible_command ();
3278 }
3279
3280 void
3281 Editor::align_regions (RegionPoint what)
3282 {
3283         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3284
3285         if (rs.empty()) {
3286                 return;
3287         }
3288
3289         begin_reversible_command (_("align selection"));
3290
3291         framepos_t const position = get_preferred_edit_position ();
3292
3293         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3294                 align_region_internal ((*i)->region(), what, position);
3295         }
3296
3297         commit_reversible_command ();
3298 }
3299
3300 struct RegionSortByTime {
3301     bool operator() (const RegionView* a, const RegionView* b) {
3302             return a->region()->position() < b->region()->position();
3303     }
3304 };
3305
3306 void
3307 Editor::align_regions_relative (RegionPoint point)
3308 {
3309         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3310
3311         if (rs.empty()) {
3312                 return;
3313         }
3314
3315         framepos_t const position = get_preferred_edit_position ();
3316
3317         framepos_t distance = 0;
3318         framepos_t pos = 0;
3319         int dir = 1;
3320
3321         list<RegionView*> sorted;
3322         rs.by_position (sorted);
3323
3324         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3325
3326         switch (point) {
3327         case Start:
3328                 pos = position;
3329                 if (position > r->position()) {
3330                         distance = position - r->position();
3331                 } else {
3332                         distance = r->position() - position;
3333                         dir = -1;
3334                 }
3335                 break;
3336
3337         case End:
3338                 if (position > r->last_frame()) {
3339                         distance = position - r->last_frame();
3340                         pos = r->position() + distance;
3341                 } else {
3342                         distance = r->last_frame() - position;
3343                         pos = r->position() - distance;
3344                         dir = -1;
3345                 }
3346                 break;
3347
3348         case SyncPoint:
3349                 pos = r->adjust_to_sync (position);
3350                 if (pos > r->position()) {
3351                         distance = pos - r->position();
3352                 } else {
3353                         distance = r->position() - pos;
3354                         dir = -1;
3355                 }
3356                 break;
3357         }
3358
3359         if (pos == r->position()) {
3360                 return;
3361         }
3362
3363         begin_reversible_command (_("align selection (relative)"));
3364
3365         /* move first one specially */
3366
3367         r->clear_changes ();
3368         r->set_position (pos);
3369         _session->add_command(new StatefulDiffCommand (r));
3370
3371         /* move rest by the same amount */
3372
3373         sorted.pop_front();
3374
3375         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3376
3377                 boost::shared_ptr<Region> region ((*i)->region());
3378
3379                 region->clear_changes ();
3380
3381                 if (dir > 0) {
3382                         region->set_position (region->position() + distance);
3383                 } else {
3384                         region->set_position (region->position() - distance);
3385                 }
3386
3387                 _session->add_command(new StatefulDiffCommand (region));
3388
3389         }
3390
3391         commit_reversible_command ();
3392 }
3393
3394 void
3395 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3396 {
3397         begin_reversible_command (_("align region"));
3398         align_region_internal (region, point, position);
3399         commit_reversible_command ();
3400 }
3401
3402 void
3403 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3404 {
3405         region->clear_changes ();
3406
3407         switch (point) {
3408         case SyncPoint:
3409                 region->set_position (region->adjust_to_sync (position));
3410                 break;
3411
3412         case End:
3413                 if (position > region->length()) {
3414                         region->set_position (position - region->length());
3415                 }
3416                 break;
3417
3418         case Start:
3419                 region->set_position (position);
3420                 break;
3421         }
3422
3423         _session->add_command(new StatefulDiffCommand (region));
3424 }
3425
3426 void
3427 Editor::trim_region_front ()
3428 {
3429         trim_region (true);
3430 }
3431
3432 void
3433 Editor::trim_region_back ()
3434 {
3435         trim_region (false);
3436 }
3437
3438 void
3439 Editor::trim_region (bool front)
3440 {
3441         framepos_t where = get_preferred_edit_position();
3442         RegionSelection rs = get_regions_from_selection_and_edit_point ();
3443
3444         if (rs.empty()) {
3445                 return;
3446         }
3447
3448         begin_reversible_command (front ? _("trim front") : _("trim back"));
3449
3450         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3451                 if (!(*i)->region()->locked()) {
3452
3453                         (*i)->region()->clear_changes ();
3454
3455                         if (front) {
3456                                 (*i)->region()->trim_front (where);
3457                                 maybe_locate_with_edit_preroll ( where );
3458                         } else {
3459                                 (*i)->region()->trim_end (where);
3460                                 maybe_locate_with_edit_preroll ( where );
3461                         }
3462
3463                         _session->add_command (new StatefulDiffCommand ((*i)->region()));
3464                 }
3465         }
3466
3467         commit_reversible_command ();
3468 }
3469
3470 /** Trim the end of the selected regions to the position of the edit cursor */
3471 void
3472 Editor::trim_region_to_loop ()
3473 {
3474         Location* loc = _session->locations()->auto_loop_location();
3475         if (!loc) {
3476                 return;
3477         }
3478         trim_region_to_location (*loc, _("trim to loop"));
3479 }
3480
3481 void
3482 Editor::trim_region_to_punch ()
3483 {
3484         Location* loc = _session->locations()->auto_punch_location();
3485         if (!loc) {
3486                 return;
3487         }
3488         trim_region_to_location (*loc, _("trim to punch"));
3489 }
3490
3491 void
3492 Editor::trim_region_to_location (const Location& loc, const char* str)
3493 {
3494         RegionSelection rs = get_regions_from_selection_and_entered ();
3495
3496         begin_reversible_command (str);
3497
3498         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3499                 RegionView* rv = (*x);
3500
3501                 /* require region to span proposed trim */
3502                 switch (rv->region()->coverage (loc.start(), loc.end())) {
3503                 case Evoral::OverlapInternal:
3504                         break;
3505                 default:
3506                         continue;
3507                 }
3508
3509                 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3510                 if (!tav) {
3511                         return;
3512                 }
3513
3514                 float speed = 1.0;
3515                 framepos_t start;
3516                 framepos_t end;
3517
3518                 if (tav->track() != 0) {
3519                         speed = tav->track()->speed();
3520                 }
3521
3522                 start = session_frame_to_track_frame (loc.start(), speed);
3523                 end = session_frame_to_track_frame (loc.end(), speed);
3524
3525                 rv->region()->clear_changes ();
3526                 rv->region()->trim_to (start, (end - start));
3527                 _session->add_command(new StatefulDiffCommand (rv->region()));
3528         }
3529
3530         commit_reversible_command ();
3531 }
3532
3533 void
3534 Editor::trim_region_to_previous_region_end ()
3535 {
3536         return trim_to_region(false);
3537 }
3538
3539 void
3540 Editor::trim_region_to_next_region_start ()
3541 {
3542         return trim_to_region(true);
3543 }
3544
3545 void
3546 Editor::trim_to_region(bool forward)
3547 {
3548         RegionSelection rs = get_regions_from_selection_and_entered ();
3549
3550         begin_reversible_command (_("trim to region"));
3551
3552         boost::shared_ptr<Region> next_region;
3553
3554         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3555
3556                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3557
3558                 if (!arv) {
3559                         continue;
3560                 }
3561
3562                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3563
3564                 if (!atav) {
3565                         return;
3566                 }
3567
3568                 float speed = 1.0;
3569
3570                 if (atav->track() != 0) {
3571                         speed = atav->track()->speed();
3572                 }
3573
3574
3575                 boost::shared_ptr<Region> region = arv->region();
3576                 boost::shared_ptr<Playlist> playlist (region->playlist());
3577
3578                 region->clear_changes ();
3579
3580                 if (forward) {
3581
3582                     next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3583
3584                     if (!next_region) {
3585                         continue;
3586                     }
3587
3588                     region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3589                     arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3590                 }
3591                 else {
3592
3593                     next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3594
3595                     if(!next_region){
3596                         continue;
3597                     }
3598
3599                     region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3600
3601                     arv->region_changed (ARDOUR::bounds_change);
3602                 }
3603
3604                 _session->add_command(new StatefulDiffCommand (region));
3605         }
3606
3607         commit_reversible_command ();
3608 }
3609
3610 void
3611 Editor::unfreeze_route ()
3612 {
3613         if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3614                 return;
3615         }
3616
3617         clicked_routeview->track()->unfreeze ();
3618 }
3619
3620 void*
3621 Editor::_freeze_thread (void* arg)
3622 {
3623         return static_cast<Editor*>(arg)->freeze_thread ();
3624 }
3625
3626 void*
3627 Editor::freeze_thread ()
3628 {
3629         /* create event pool because we may need to talk to the session */
3630         SessionEvent::create_per_thread_pool ("freeze events", 64);
3631         /* create per-thread buffers for process() tree to use */
3632         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3633         current_interthread_info->done = true;
3634         return 0;
3635 }
3636
3637 void
3638 Editor::freeze_route ()
3639 {
3640         if (!_session) {
3641                 return;
3642         }
3643
3644         /* stop transport before we start. this is important */
3645
3646         _session->request_transport_speed (0.0);
3647         
3648         /* wait for just a little while, because the above call is asynchronous */
3649
3650         Glib::usleep (250000);
3651
3652         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3653                 return;
3654         }
3655
3656         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3657                 MessageDialog d (
3658                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3659                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3660                         );
3661                 d.set_title (_("Cannot freeze"));
3662                 d.run ();
3663                 return;
3664         }
3665
3666         if (clicked_routeview->track()->has_external_redirects()) {
3667                 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"
3668                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3669                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3670
3671                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3672                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3673                 d.set_title (_("Freeze Limits"));
3674
3675                 int response = d.run ();
3676
3677                 switch (response) {
3678                 case Gtk::RESPONSE_CANCEL:
3679                         return;
3680                 default:
3681                         break;
3682                 }
3683         }
3684
3685         InterThreadInfo itt;
3686         current_interthread_info = &itt;
3687
3688         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3689
3690         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3691
3692         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3693
3694         while (!itt.done && !itt.cancel) {
3695                 gtk_main_iteration ();
3696         }
3697
3698         current_interthread_info = 0;
3699 }
3700
3701 void
3702 Editor::bounce_range_selection (bool replace, bool enable_processing)
3703 {
3704         if (selection->time.empty()) {
3705                 return;
3706         }
3707
3708         TrackSelection views = selection->tracks;
3709
3710         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3711
3712                 if (enable_processing) {
3713
3714                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3715
3716                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3717                                 MessageDialog d (
3718                                         _("You can't perform this operation because the processing of the signal "
3719                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3720                                           "You can do this without processing, which is a different operation.")
3721                                         );
3722                                 d.set_title (_("Cannot bounce"));
3723                                 d.run ();
3724                                 return;
3725                         }
3726                 }
3727         }
3728
3729         framepos_t start = selection->time[clicked_selection].start;
3730         framepos_t end = selection->time[clicked_selection].end;
3731         framepos_t cnt = end - start + 1;
3732
3733         begin_reversible_command (_("bounce range"));
3734
3735         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3736
3737                 RouteTimeAxisView* rtv;
3738
3739                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3740                         continue;
3741                 }
3742
3743                 boost::shared_ptr<Playlist> playlist;
3744
3745                 if ((playlist = rtv->playlist()) == 0) {
3746                         return;
3747                 }
3748
3749                 InterThreadInfo itt;
3750
3751                 playlist->clear_changes ();
3752                 playlist->clear_owned_changes ();
3753
3754                 boost::shared_ptr<Region> r;
3755
3756                 if (enable_processing) {
3757                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3758                 } else {
3759                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3760                 }
3761
3762                 if (!r) {
3763                         continue;
3764                 }
3765
3766                 if (replace) {
3767                         list<AudioRange> ranges;
3768                         ranges.push_back (AudioRange (start, start+cnt, 0));
3769                         playlist->cut (ranges); // discard result
3770                         playlist->add_region (r, start);
3771                 }
3772
3773                 vector<Command*> cmds;
3774                 playlist->rdiff (cmds);
3775                 _session->add_commands (cmds);
3776
3777                 _session->add_command (new StatefulDiffCommand (playlist));
3778         }
3779
3780         commit_reversible_command ();
3781 }
3782
3783 /** Delete selected regions, automation points or a time range */
3784 void
3785 Editor::delete_ ()
3786 {
3787         //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
3788         //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
3789         bool deleted = false;
3790         if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
3791                 deleted = current_mixer_strip->delete_processors ();
3792
3793         if (!deleted)
3794                 cut_copy (Delete);
3795 }
3796
3797 /** Cut selected regions, automation points or a time range */
3798 void
3799 Editor::cut ()
3800 {
3801         cut_copy (Cut);
3802 }
3803
3804 /** Copy selected regions, automation points or a time range */
3805 void
3806 Editor::copy ()
3807 {
3808         cut_copy (Copy);
3809 }
3810
3811
3812 /** @return true if a Cut, Copy or Clear is possible */
3813 bool
3814 Editor::can_cut_copy () const
3815 {
3816         if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
3817                 return true;
3818
3819         return false;
3820 }
3821
3822
3823 /** Cut, copy or clear selected regions, automation points or a time range.
3824  * @param op Operation (Delete, Cut, Copy or Clear)
3825  */
3826 void
3827 Editor::cut_copy (CutCopyOp op)
3828 {
3829         /* only cancel selection if cut/copy is successful.*/
3830
3831         string opname;
3832
3833         switch (op) {
3834         case Delete:
3835                 opname = _("delete");
3836                 break;
3837         case Cut:
3838                 opname = _("cut");
3839                 break;
3840         case Copy:
3841                 opname = _("copy");
3842                 break;
3843         case Clear:
3844                 opname = _("clear");
3845                 break;
3846         }
3847
3848         /* if we're deleting something, and the mouse is still pressed,
3849            the thing we started a drag for will be gone when we release
3850            the mouse button(s). avoid this. see part 2 at the end of
3851            this function.
3852         */
3853
3854         if (op == Delete || op == Cut || op == Clear) {
3855                 if (_drags->active ()) {
3856                         _drags->abort ();
3857                 }
3858         }
3859
3860         if ( op != Delete )  //"Delete" doesn't change copy/paste buf
3861                 cut_buffer->clear ();
3862
3863         if (entered_marker) {
3864
3865                 /* cut/delete op while pointing at a marker */
3866
3867                 bool ignored;
3868                 Location* loc = find_location_from_marker (entered_marker, ignored);
3869
3870                 if (_session && loc) {
3871                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3872                 }
3873
3874                 _drags->abort ();
3875                 return;
3876         }
3877
3878         switch (mouse_mode) {
3879         case MouseDraw:
3880         case MouseContent:
3881                 begin_reversible_command (opname + ' ' + X_("MIDI"));
3882                 cut_copy_midi (op);
3883                 commit_reversible_command ();
3884                 return;
3885         default:
3886                 break;
3887         }
3888
3889         bool did_edit = false;
3890
3891         if (!selection->regions.empty() || !selection->points.empty()) {
3892                 begin_reversible_command (opname + ' ' + _("objects"));
3893                 did_edit = true;
3894
3895                 if (!selection->regions.empty()) {
3896                         cut_copy_regions (op, selection->regions);
3897                         
3898                         if (op == Cut || op == Delete) {
3899                                 selection->clear_regions ();
3900                         }
3901                 }
3902                 
3903                 if (!selection->points.empty()) {
3904                         cut_copy_points (op);
3905                         
3906                         if (op == Cut || op == Delete) {
3907                                 selection->clear_points ();
3908                         }
3909                 }
3910         } else if (selection->time.empty()) {
3911                 framepos_t start, end;
3912                 /* no time selection, see if we can get an edit range
3913                    and use that.
3914                 */
3915                 if (get_edit_op_range (start, end)) {
3916                         selection->set (start, end);
3917                 }
3918         } else if (!selection->time.empty()) {
3919                 begin_reversible_command (opname + ' ' + _("range"));
3920
3921                 did_edit = true;
3922                 cut_copy_ranges (op);
3923                 
3924                 if (op == Cut || op == Delete) {
3925                         selection->clear_time ();
3926                 }
3927         }
3928         
3929         if (did_edit) {
3930                 /* reset repeated paste state */
3931                 paste_count    = 0;
3932                 last_paste_pos = 0;
3933                 commit_reversible_command ();   
3934         }
3935         
3936         if (op == Delete || op == Cut || op == Clear) {
3937                 _drags->abort ();
3938         }
3939 }
3940
3941 struct AutomationRecord {
3942         AutomationRecord () : state (0) , line(NULL) {}
3943         AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
3944         
3945         XMLNode* state; ///< state before any operation
3946         const AutomationLine* line; ///< line this came from
3947         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3948 };
3949
3950 /** Cut, copy or clear selected automation points.
3951  *  @param op Operation (Cut, Copy or Clear)
3952  */
3953 void
3954 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::MusicalTime earliest, bool midi)
3955 {
3956         if (selection->points.empty ()) {
3957                 return;
3958         }
3959
3960         /* XXX: not ideal, as there may be more than one track involved in the point selection */
3961         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3962
3963         /* Keep a record of the AutomationLists that we end up using in this operation */
3964         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3965         Lists lists;
3966
3967         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3968         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3969                 const AutomationLine&                   line = (*i)->line();
3970                 const boost::shared_ptr<AutomationList> al   = line.the_list();
3971                 if (lists.find (al) == lists.end ()) {
3972                         /* We haven't seen this list yet, so make a record for it.  This includes
3973                            taking a copy of its current state, in case this is needed for undo later.
3974                         */
3975                         lists[al] = AutomationRecord (&al->get_state (), &line);
3976                 }
3977         }
3978
3979         if (op == Cut || op == Copy) {
3980                 /* This operation will involve putting things in the cut buffer, so create an empty
3981                    ControlList for each of our source lists to put the cut buffer data in.
3982                 */
3983                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3984                         i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
3985                 }
3986
3987                 /* Add all selected points to the relevant copy ControlLists */
3988                 framepos_t start = std::numeric_limits<framepos_t>::max();
3989                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3990                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3991                         AutomationList::const_iterator    j  = (*i)->model();
3992
3993                         lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
3994                         if (midi) {
3995                                 /* Update earliest MIDI start time in beats */
3996                                 earliest = std::min(earliest,  Evoral::MusicalTime((*j)->when));
3997                         } else {
3998                                 /* Update earliest session start time in frames */
3999                                 start = std::min(start, (*i)->line().session_position(j));
4000                         }
4001                 }
4002
4003                 /* Snap start time backwards, so copy/paste is snap aligned. */
4004                 if (midi) {
4005                         if (earliest == Evoral::MusicalTime::max()) {
4006                                 earliest = Evoral::MusicalTime();  // Weird... don't offset
4007                         }
4008                         earliest.round_down_to_beat();
4009                 } else {
4010                         if (start == std::numeric_limits<double>::max()) {
4011                                 start = 0;  // Weird... don't offset
4012                         }
4013                         snap_to(start, RoundDownMaybe);
4014                 }
4015
4016                 const double line_offset = midi ? earliest.to_double() : start;
4017                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4018                         /* Correct this copy list so that it is relative to the earliest
4019                            start time, so relative ordering between points is preserved
4020                            when copying from several lists and the paste starts at the
4021                            earliest copied piece of data. */
4022                         for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
4023                                 (*j)->when -= line_offset;
4024                         }
4025
4026                         /* And add it to the cut buffer */
4027                         cut_buffer->add (i->second.copy);
4028                 }
4029         }
4030                 
4031         if (op == Delete || op == Cut) {
4032                 /* This operation needs to remove things from the main AutomationList, so do that now */
4033                 
4034                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4035                         i->first->freeze ();
4036                 }
4037
4038                 /* Remove each selected point from its AutomationList */
4039                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4040                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
4041                         al->erase ((*i)->model ());
4042                 }
4043
4044                 /* Thaw the lists and add undo records for them */
4045                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4046                         boost::shared_ptr<AutomationList> al = i->first;
4047                         al->thaw ();
4048                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4049                 }
4050         }
4051 }
4052
4053 /** Cut, copy or clear selected automation points.
4054  * @param op Operation (Cut, Copy or Clear)
4055  */
4056 void
4057 Editor::cut_copy_midi (CutCopyOp op)
4058 {
4059         Evoral::MusicalTime earliest = Evoral::MusicalTime::max();
4060         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4061                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4062                 if (mrv) {
4063                         if (!mrv->selection().empty()) {
4064                                 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4065                         }
4066                         mrv->cut_copy_clear (op);
4067
4068                         /* XXX: not ideal, as there may be more than one track involved in the selection */
4069                         _last_cut_copy_source_track = &mrv->get_time_axis_view();
4070                 }
4071         }
4072
4073         if (!selection->points.empty()) {
4074                 cut_copy_points (op, earliest, true);
4075                 if (op == Cut || op == Delete) {
4076                         selection->clear_points ();
4077                 }
4078         }
4079 }
4080
4081 struct lt_playlist {
4082     bool operator () (const PlaylistState& a, const PlaylistState& b) {
4083             return a.playlist < b.playlist;
4084     }
4085 };
4086
4087 struct PlaylistMapping {
4088     TimeAxisView* tv;
4089     boost::shared_ptr<Playlist> pl;
4090
4091     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4092 };
4093
4094 /** Remove `clicked_regionview' */
4095 void
4096 Editor::remove_clicked_region ()
4097 {
4098         if (clicked_routeview == 0 || clicked_regionview == 0) {
4099                 return;
4100         }
4101
4102         begin_reversible_command (_("remove region"));
4103
4104         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4105
4106         playlist->clear_changes ();
4107         playlist->clear_owned_changes ();
4108         playlist->remove_region (clicked_regionview->region());
4109         if (Config->get_edit_mode() == Ripple)
4110                 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4111
4112         /* We might have removed regions, which alters other regions' layering_index,
4113            so we need to do a recursive diff here.
4114         */
4115         vector<Command*> cmds;
4116         playlist->rdiff (cmds);
4117         _session->add_commands (cmds);
4118         
4119         _session->add_command(new StatefulDiffCommand (playlist));
4120         commit_reversible_command ();
4121 }
4122
4123
4124 /** Remove the selected regions */
4125 void
4126 Editor::remove_selected_regions ()
4127 {
4128         RegionSelection rs = get_regions_from_selection_and_entered ();
4129
4130         if (!_session || rs.empty()) {
4131                 return;
4132         }
4133
4134         begin_reversible_command (_("remove region"));
4135
4136         list<boost::shared_ptr<Region> > regions_to_remove;
4137
4138         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4139                 // we can't just remove the region(s) in this loop because
4140                 // this removes them from the RegionSelection, and they thus
4141                 // disappear from underneath the iterator, and the ++i above
4142                 // SEGVs in a puzzling fashion.
4143
4144                 // so, first iterate over the regions to be removed from rs and
4145                 // add them to the regions_to_remove list, and then
4146                 // iterate over the list to actually remove them.
4147
4148                 regions_to_remove.push_back ((*i)->region());
4149         }
4150
4151         vector<boost::shared_ptr<Playlist> > playlists;
4152
4153         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4154
4155                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4156
4157                 if (!playlist) {
4158                         // is this check necessary?
4159                         continue;
4160                 }
4161
4162                 /* get_regions_from_selection_and_entered() guarantees that
4163                    the playlists involved are unique, so there is no need
4164                    to check here.
4165                 */
4166
4167                 playlists.push_back (playlist);
4168
4169                 playlist->clear_changes ();
4170                 playlist->clear_owned_changes ();
4171                 playlist->freeze ();
4172                 playlist->remove_region (*rl);
4173                 if (Config->get_edit_mode() == Ripple)
4174                         playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4175
4176         }
4177
4178         vector<boost::shared_ptr<Playlist> >::iterator pl;
4179
4180         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4181                 (*pl)->thaw ();
4182
4183                 /* We might have removed regions, which alters other regions' layering_index,
4184                    so we need to do a recursive diff here.
4185                 */
4186                 vector<Command*> cmds;
4187                 (*pl)->rdiff (cmds);
4188                 _session->add_commands (cmds);
4189                 
4190                 _session->add_command(new StatefulDiffCommand (*pl));
4191         }
4192
4193         commit_reversible_command ();
4194 }
4195
4196 /** Cut, copy or clear selected regions.
4197  * @param op Operation (Cut, Copy or Clear)
4198  */
4199 void
4200 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4201 {
4202         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4203            a map when we want ordered access to both elements. i think.
4204         */
4205
4206         vector<PlaylistMapping> pmap;
4207
4208         framepos_t first_position = max_framepos;
4209
4210         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4211         FreezeList freezelist;
4212
4213         /* get ordering correct before we cut/copy */
4214
4215         rs.sort_by_position_and_track ();
4216
4217         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4218
4219                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4220
4221                 if (op == Cut || op == Clear || op == Delete) {
4222                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4223
4224                         if (pl) {
4225                                 FreezeList::iterator fl;
4226
4227                                 // only take state if this is a new playlist.
4228                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4229                                         if ((*fl) == pl) {
4230                                                 break;
4231                                         }
4232                                 }
4233
4234                                 if (fl == freezelist.end()) {
4235                                         pl->clear_changes();
4236                                         pl->clear_owned_changes ();
4237                                         pl->freeze ();
4238                                         freezelist.insert (pl);
4239                                 }
4240                         }
4241                 }
4242
4243                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4244                 vector<PlaylistMapping>::iterator z;
4245
4246                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4247                         if ((*z).tv == tv) {
4248                                 break;
4249                         }
4250                 }
4251
4252                 if (z == pmap.end()) {
4253                         pmap.push_back (PlaylistMapping (tv));
4254                 }
4255         }
4256
4257         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4258
4259                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4260
4261                 if (!pl) {
4262                         /* region not yet associated with a playlist (e.g. unfinished
4263                            capture pass.
4264                         */
4265                         ++x;
4266                         continue;
4267                 }
4268
4269                 TimeAxisView& tv = (*x)->get_time_axis_view();
4270                 boost::shared_ptr<Playlist> npl;
4271                 RegionSelection::iterator tmp;
4272
4273                 tmp = x;
4274                 ++tmp;
4275
4276                 if (op != Delete) {
4277
4278                         vector<PlaylistMapping>::iterator z;
4279                         
4280                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4281                                 if ((*z).tv == &tv) {
4282                                         break;
4283                                 }
4284                         }
4285                         
4286                         assert (z != pmap.end());
4287                         
4288                         if (!(*z).pl) {
4289                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4290                                 npl->freeze();
4291                                 (*z).pl = npl;
4292                         } else {
4293                                 npl = (*z).pl;
4294                         }
4295                 }
4296
4297                 boost::shared_ptr<Region> r = (*x)->region();
4298                 boost::shared_ptr<Region> _xx;
4299
4300                 assert (r != 0);
4301
4302                 switch (op) {
4303                 case Delete:
4304                         pl->remove_region (r);
4305                         if (Config->get_edit_mode() == Ripple)
4306                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4307                         break;
4308                         
4309                 case Cut:
4310                         _xx = RegionFactory::create (r);
4311                         npl->add_region (_xx, r->position() - first_position);
4312                         pl->remove_region (r);
4313                         if (Config->get_edit_mode() == Ripple)
4314                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4315                         break;
4316
4317                 case Copy:
4318                         /* copy region before adding, so we're not putting same object into two different playlists */
4319                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4320                         break;
4321
4322                 case Clear:
4323                         pl->remove_region (r);
4324                         if (Config->get_edit_mode() == Ripple)
4325                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4326                         break;
4327                 }
4328
4329                 x = tmp;
4330         }
4331
4332         if (op != Delete) {
4333
4334                 list<boost::shared_ptr<Playlist> > foo;
4335                 
4336                 /* the pmap is in the same order as the tracks in which selected regions occured */
4337                 
4338                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4339                         if ((*i).pl) {
4340                                 (*i).pl->thaw();
4341                                 foo.push_back ((*i).pl);
4342                         }
4343                 }
4344                 
4345                 if (!foo.empty()) {
4346                         cut_buffer->set (foo);
4347                 }
4348                 
4349                 if (pmap.empty()) {
4350                         _last_cut_copy_source_track = 0;
4351                 } else {
4352                         _last_cut_copy_source_track = pmap.front().tv;
4353                 }
4354         }
4355
4356         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4357                 (*pl)->thaw ();
4358
4359                 /* We might have removed regions, which alters other regions' layering_index,
4360                    so we need to do a recursive diff here.
4361                 */
4362                 vector<Command*> cmds;
4363                 (*pl)->rdiff (cmds);
4364                 _session->add_commands (cmds);
4365                 
4366                 _session->add_command (new StatefulDiffCommand (*pl));
4367         }
4368 }
4369
4370 void
4371 Editor::cut_copy_ranges (CutCopyOp op)
4372 {
4373         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4374
4375         /* Sort the track selection now, so that it if is used, the playlists
4376            selected by the calls below to cut_copy_clear are in the order that
4377            their tracks appear in the editor.  This makes things like paste
4378            of ranges work properly.
4379         */
4380
4381         sort_track_selection (ts);
4382
4383         if (ts.empty()) {
4384                 if (!entered_track) {
4385                         return;
4386                 }
4387                 ts.push_back (entered_track);
4388         } 
4389
4390         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4391                 (*i)->cut_copy_clear (*selection, op);
4392         }
4393 }
4394
4395 void
4396 Editor::paste (float times, bool from_context)
4397 {
4398         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4399
4400         paste_internal (get_preferred_edit_position (false, from_context), times);
4401 }
4402
4403 void
4404 Editor::mouse_paste ()
4405 {
4406         framepos_t where;
4407         bool ignored;
4408
4409         if (!mouse_frame (where, ignored)) {
4410                 return;
4411         }
4412
4413         snap_to (where);
4414         paste_internal (where, 1);
4415 }
4416
4417 void
4418 Editor::paste_internal (framepos_t position, float times)
4419 {
4420         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4421
4422         if (cut_buffer->empty(internal_editing())) {
4423                 return;
4424         }
4425
4426         if (position == max_framepos) {
4427                 position = get_preferred_edit_position();
4428                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4429         }
4430
4431         if (position == last_paste_pos) {
4432                 /* repeated paste in the same position */
4433                 ++paste_count;
4434         } else {
4435                 /* paste in new location, reset repeated paste state */
4436                 paste_count = 0;
4437                 last_paste_pos = position;
4438         }
4439
4440         /* get everything in the correct order */
4441
4442         TrackViewList ts;
4443         if (!selection->tracks.empty()) {
4444                 /* If there is a track selection, paste into exactly those tracks and
4445                    only those tracks.  This allows the user to be explicit and override
4446                    the below "do the reasonable thing" logic. */
4447                 ts = selection->tracks.filter_to_unique_playlists ();
4448                 sort_track_selection (ts);
4449         } else {
4450                 /* Figure out which track to base the paste at. */
4451                 TimeAxisView* base_track = NULL;
4452                 if (_edit_point == Editing::EditAtMouse && entered_track) {
4453                         /* With the mouse edit point, paste onto the track under the mouse. */
4454                         base_track = entered_track;
4455                 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4456                         /* With the mouse edit point, paste onto the track of the region under the mouse. */
4457                         base_track = &entered_regionview->get_time_axis_view();
4458                 } else if (_last_cut_copy_source_track) {
4459                         /* Paste to the track that the cut/copy came from (see mantis #333). */
4460                         base_track = _last_cut_copy_source_track;
4461                 } else {
4462                         /* This is "impossible" since we've copied... well, do nothing. */
4463                         return;
4464                 }
4465
4466                 /* Walk up to parent if necessary, so base track is a route. */
4467                 while (base_track->get_parent()) {
4468                         base_track = base_track->get_parent();
4469                 }
4470
4471                 /* Add base track and all tracks below it.  The paste logic will select
4472                    the appropriate object types from the cut buffer in relative order. */
4473                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4474                         if ((*i)->order() >= base_track->order()) {
4475                                 ts.push_back(*i);
4476                         }
4477                 }
4478
4479                 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4480                 sort_track_selection (ts);
4481
4482                 /* Add automation children of each track in order, for pasting several lines. */
4483                 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4484                         /* Add any automation children for pasting several lines */
4485                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4486                         if (!rtv) {
4487                                 continue;
4488                         }
4489
4490                         typedef RouteTimeAxisView::AutomationTracks ATracks;
4491                         const ATracks& atracks = rtv->automation_tracks();
4492                         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4493                                 i = ts.insert(i, a->second.get());
4494                                 ++i;
4495                         }
4496                 }
4497
4498                 /* We now have a list of trackviews starting at base_track, including
4499                    automation children, in the order shown in the editor, e.g. R1,
4500                    R1.A1, R1.A2, R2, R2.A1, ... */
4501         }
4502
4503         begin_reversible_command (Operations::paste);
4504
4505         if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4506             dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4507             /* Only one line copied, and one automation track selected.  Do a
4508                "greedy" paste from one automation type to another. */
4509
4510             PasteContext ctx(paste_count, times, ItemCounts(), true);
4511             ts.front()->paste (position, *cut_buffer, ctx);
4512
4513         } else {
4514
4515                 /* Paste into tracks */
4516
4517                 PasteContext ctx(paste_count, times, ItemCounts(), false);
4518                 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4519                         (*i)->paste (position, *cut_buffer, ctx);
4520                 }
4521         }
4522
4523         commit_reversible_command ();
4524 }
4525
4526 void
4527 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4528 {
4529         boost::shared_ptr<Playlist> playlist;
4530         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4531         RegionSelection foo;
4532
4533         framepos_t const start_frame = regions.start ();
4534         framepos_t const end_frame = regions.end_frame ();
4535
4536         begin_reversible_command (Operations::duplicate_region);
4537
4538         selection->clear_regions ();
4539
4540         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4541
4542                 boost::shared_ptr<Region> r ((*i)->region());
4543
4544                 TimeAxisView& tv = (*i)->get_time_axis_view();
4545                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4546                 latest_regionviews.clear ();
4547                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4548
4549                 playlist = (*i)->region()->playlist();
4550                 playlist->clear_changes ();
4551                 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4552                 _session->add_command(new StatefulDiffCommand (playlist));
4553
4554                 c.disconnect ();
4555
4556                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4557         }
4558
4559         if (!foo.empty()) {
4560                 selection->set (foo);
4561         }
4562
4563         commit_reversible_command ();
4564 }
4565
4566 void
4567 Editor::duplicate_selection (float times)
4568 {
4569         if (selection->time.empty() || selection->tracks.empty()) {
4570                 return;
4571         }
4572
4573         boost::shared_ptr<Playlist> playlist;
4574         vector<boost::shared_ptr<Region> > new_regions;
4575         vector<boost::shared_ptr<Region> >::iterator ri;
4576
4577         create_region_from_selection (new_regions);
4578
4579         if (new_regions.empty()) {
4580                 return;
4581         }
4582
4583         begin_reversible_command (_("duplicate selection"));
4584
4585         ri = new_regions.begin();
4586
4587         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4588
4589         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4590                 if ((playlist = (*i)->playlist()) == 0) {
4591                         continue;
4592                 }
4593                 playlist->clear_changes ();
4594                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4595                 _session->add_command (new StatefulDiffCommand (playlist));
4596
4597                 ++ri;
4598                 if (ri == new_regions.end()) {
4599                         --ri;
4600                 }
4601         }
4602
4603         commit_reversible_command ();
4604 }
4605
4606 /** Reset all selected points to the relevant default value */
4607 void
4608 Editor::reset_point_selection ()
4609 {
4610         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4611                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4612                 (*j)->value = (*i)->line().the_list()->default_value ();
4613         }
4614 }
4615
4616 void
4617 Editor::center_playhead ()
4618 {
4619         float const page = _visible_canvas_width * samples_per_pixel;
4620         center_screen_internal (playhead_cursor->current_frame (), page);
4621 }
4622
4623 void
4624 Editor::center_edit_point ()
4625 {
4626         float const page = _visible_canvas_width * samples_per_pixel;
4627         center_screen_internal (get_preferred_edit_position(), page);
4628 }
4629
4630 /** Caller must begin and commit a reversible command */
4631 void
4632 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4633 {
4634         playlist->clear_changes ();
4635         playlist->clear ();
4636         _session->add_command (new StatefulDiffCommand (playlist));
4637 }
4638
4639 void
4640 Editor::nudge_track (bool use_edit, bool forwards)
4641 {
4642         boost::shared_ptr<Playlist> playlist;
4643         framepos_t distance;
4644         framepos_t next_distance;
4645         framepos_t start;
4646
4647         if (use_edit) {
4648                 start = get_preferred_edit_position();
4649         } else {
4650                 start = 0;
4651         }
4652
4653         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4654                 return;
4655         }
4656
4657         if (selection->tracks.empty()) {
4658                 return;
4659         }
4660
4661         begin_reversible_command (_("nudge track"));
4662
4663         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4664
4665         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4666
4667                 if ((playlist = (*i)->playlist()) == 0) {
4668                         continue;
4669                 }
4670
4671                 playlist->clear_changes ();
4672                 playlist->clear_owned_changes ();
4673
4674                 playlist->nudge_after (start, distance, forwards);
4675
4676                 vector<Command*> cmds;
4677
4678                 playlist->rdiff (cmds);
4679                 _session->add_commands (cmds);
4680
4681                 _session->add_command (new StatefulDiffCommand (playlist));
4682         }
4683
4684         commit_reversible_command ();
4685 }
4686
4687 void
4688 Editor::remove_last_capture ()
4689 {
4690         vector<string> choices;
4691         string prompt;
4692
4693         if (!_session) {
4694                 return;
4695         }
4696
4697         if (Config->get_verify_remove_last_capture()) {
4698                 prompt  = _("Do you really want to destroy the last capture?"
4699                             "\n(This is destructive and cannot be undone)");
4700
4701                 choices.push_back (_("No, do nothing."));
4702                 choices.push_back (_("Yes, destroy it."));
4703
4704                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4705
4706                 if (prompter.run () == 1) {
4707                         _session->remove_last_capture ();
4708                         _regions->redisplay ();
4709                 }
4710
4711         } else {
4712                 _session->remove_last_capture();
4713                 _regions->redisplay ();
4714         }
4715 }
4716
4717 void
4718 Editor::normalize_region ()
4719 {
4720         if (!_session) {
4721                 return;
4722         }
4723
4724         RegionSelection rs = get_regions_from_selection_and_entered ();
4725
4726         if (rs.empty()) {
4727                 return;
4728         }
4729
4730         NormalizeDialog dialog (rs.size() > 1);
4731
4732         if (dialog.run () == RESPONSE_CANCEL) {
4733                 return;
4734         }
4735
4736         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4737         gdk_flush ();
4738
4739         /* XXX: should really only count audio regions here */
4740         int const regions = rs.size ();
4741
4742         /* Make a list of the selected audio regions' maximum amplitudes, and also
4743            obtain the maximum amplitude of them all.
4744         */
4745         list<double> max_amps;
4746         double max_amp = 0;
4747         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4748                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4749                 if (arv) {
4750                         dialog.descend (1.0 / regions);
4751                         double const a = arv->audio_region()->maximum_amplitude (&dialog);
4752
4753                         if (a == -1) {
4754                                 /* the user cancelled the operation */
4755                                 return;
4756                         }
4757
4758                         max_amps.push_back (a);
4759                         max_amp = max (max_amp, a);
4760                         dialog.ascend ();
4761                 }
4762         }
4763
4764         begin_reversible_command (_("normalize"));
4765
4766         list<double>::const_iterator a = max_amps.begin ();
4767
4768         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4769                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4770                 if (!arv) {
4771                         continue;
4772                 }
4773
4774                 arv->region()->clear_changes ();
4775
4776                 double const amp = dialog.normalize_individually() ? *a : max_amp;
4777
4778                 arv->audio_region()->normalize (amp, dialog.target ());
4779                 _session->add_command (new StatefulDiffCommand (arv->region()));
4780
4781                 ++a;
4782         }
4783
4784         commit_reversible_command ();
4785 }
4786
4787
4788 void
4789 Editor::reset_region_scale_amplitude ()
4790 {
4791         if (!_session) {
4792                 return;
4793         }
4794
4795         RegionSelection rs = get_regions_from_selection_and_entered ();
4796
4797         if (rs.empty()) {
4798                 return;
4799         }
4800
4801         begin_reversible_command ("reset gain");
4802
4803         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4804                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4805                 if (!arv)
4806                         continue;
4807                 arv->region()->clear_changes ();
4808                 arv->audio_region()->set_scale_amplitude (1.0f);
4809                 _session->add_command (new StatefulDiffCommand (arv->region()));
4810         }
4811
4812         commit_reversible_command ();
4813 }
4814
4815 void
4816 Editor::adjust_region_gain (bool up)
4817 {
4818         RegionSelection rs = get_regions_from_selection_and_entered ();
4819
4820         if (!_session || rs.empty()) {
4821                 return;
4822         }
4823
4824         begin_reversible_command ("adjust region gain");
4825
4826         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4827                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4828                 if (!arv) {
4829                         continue;
4830                 }
4831
4832                 arv->region()->clear_changes ();
4833
4834                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4835
4836                 if (up) {
4837                         dB += 1;
4838                 } else {
4839                         dB -= 1;
4840                 }
4841
4842                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4843                 _session->add_command (new StatefulDiffCommand (arv->region()));
4844         }
4845
4846         commit_reversible_command ();
4847 }
4848
4849
4850 void
4851 Editor::reverse_region ()
4852 {
4853         if (!_session) {
4854                 return;
4855         }
4856
4857         Reverse rev (*_session);
4858         apply_filter (rev, _("reverse regions"));
4859 }
4860
4861 void
4862 Editor::strip_region_silence ()
4863 {
4864         if (!_session) {
4865                 return;
4866         }
4867
4868         RegionSelection rs = get_regions_from_selection_and_entered ();
4869
4870         if (rs.empty()) {
4871                 return;
4872         }
4873
4874         std::list<RegionView*> audio_only;
4875
4876         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4877                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4878                 if (arv) {
4879                         audio_only.push_back (arv);
4880                 }
4881         }
4882
4883         StripSilenceDialog d (_session, audio_only);
4884         int const r = d.run ();
4885
4886         d.drop_rects ();
4887
4888         if (r == Gtk::RESPONSE_OK) {
4889                 ARDOUR::AudioIntervalMap silences;
4890                 d.silences (silences);
4891                 StripSilence s (*_session, silences, d.fade_length());
4892                 apply_filter (s, _("strip silence"), &d);
4893         }
4894 }
4895
4896 Command*
4897 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4898 {
4899         Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4900         mrv.selection_as_notelist (selected, true);
4901
4902         vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4903         v.push_back (selected);
4904
4905         framepos_t          pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
4906         Evoral::MusicalTime pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4907
4908         return op (mrv.midi_region()->model(), pos_beats, v);
4909 }
4910
4911 void
4912 Editor::apply_midi_note_edit_op (MidiOperator& op)
4913 {
4914         Command* cmd;
4915
4916         RegionSelection rs = get_regions_from_selection_and_entered ();
4917
4918         if (rs.empty()) {
4919                 return;
4920         }
4921
4922         begin_reversible_command (op.name ());
4923
4924         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4925                 RegionSelection::iterator tmp = r;
4926                 ++tmp;
4927
4928                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4929
4930                 if (mrv) {
4931                         cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4932                         if (cmd) {
4933                                 (*cmd)();
4934                                 _session->add_command (cmd);
4935                         }
4936                 }
4937
4938                 r = tmp;
4939         }
4940
4941         commit_reversible_command ();
4942 }
4943
4944 void
4945 Editor::fork_region ()
4946 {
4947         RegionSelection rs = get_regions_from_selection_and_entered ();
4948
4949         if (rs.empty()) {
4950                 return;
4951         }
4952
4953         begin_reversible_command (_("Fork Region(s)"));
4954
4955         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4956         gdk_flush ();
4957
4958         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4959                 RegionSelection::iterator tmp = r;
4960                 ++tmp;
4961
4962                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4963
4964                 if (mrv) {
4965                         try {
4966                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4967                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
4968                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
4969                                 
4970                                 playlist->clear_changes ();
4971                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4972                                 _session->add_command(new StatefulDiffCommand (playlist));
4973                         } catch (...) {
4974                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
4975                         }
4976                 }
4977
4978                 r = tmp;
4979         }
4980
4981         commit_reversible_command ();
4982 }
4983
4984 void
4985 Editor::quantize_region ()
4986 {
4987         int selected_midi_region_cnt = 0;
4988
4989         if (!_session) {
4990                 return;
4991         }
4992
4993         RegionSelection rs = get_regions_from_selection_and_entered ();
4994
4995         if (rs.empty()) {
4996                 return;
4997         }
4998
4999         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5000                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5001                 if (mrv) {
5002                         selected_midi_region_cnt++;
5003                 }
5004         }
5005
5006         if (selected_midi_region_cnt == 0) {
5007                 return;
5008         }
5009
5010         QuantizeDialog* qd = new QuantizeDialog (*this);
5011
5012         qd->present ();
5013         const int r = qd->run ();
5014         qd->hide ();
5015
5016         if (r == Gtk::RESPONSE_OK) {
5017                 Quantize quant (qd->snap_start(), qd->snap_end(),
5018                                 qd->start_grid_size(), qd->end_grid_size(),
5019                                 qd->strength(), qd->swing(), qd->threshold());
5020
5021                 apply_midi_note_edit_op (quant);
5022         }
5023 }
5024
5025 void
5026 Editor::legatize_region (bool shrink_only)
5027 {
5028         int selected_midi_region_cnt = 0;
5029
5030         if (!_session) {
5031                 return;
5032         }
5033
5034         RegionSelection rs = get_regions_from_selection_and_entered ();
5035
5036         if (rs.empty()) {
5037                 return;
5038         }
5039
5040         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5041                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5042                 if (mrv) {
5043                         selected_midi_region_cnt++;
5044                 }
5045         }
5046
5047         if (selected_midi_region_cnt == 0) {
5048                 return;
5049         }
5050
5051         Legatize legatize(shrink_only);
5052         apply_midi_note_edit_op (legatize);
5053 }
5054
5055 void
5056 Editor::insert_patch_change (bool from_context)
5057 {
5058         RegionSelection rs = get_regions_from_selection_and_entered ();
5059
5060         if (rs.empty ()) {
5061                 return;
5062         }
5063
5064         const framepos_t p = get_preferred_edit_position (false, from_context);
5065
5066         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5067            there may be more than one, but the PatchChangeDialog can only offer
5068            one set of patch menus.
5069         */
5070         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5071
5072         Evoral::PatchChange<Evoral::MusicalTime> empty (Evoral::MusicalTime(), 0, 0, 0);
5073         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5074
5075         if (d.run() == RESPONSE_CANCEL) {
5076                 return;
5077         }
5078
5079         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5080                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5081                 if (mrv) {
5082                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5083                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5084                         }
5085                 }
5086         }
5087 }
5088
5089 void
5090 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5091 {
5092         RegionSelection rs = get_regions_from_selection_and_entered ();
5093
5094         if (rs.empty()) {
5095                 return;
5096         }
5097
5098         begin_reversible_command (command);
5099
5100         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5101         gdk_flush ();
5102
5103         int n = 0;
5104         int const N = rs.size ();
5105
5106         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5107                 RegionSelection::iterator tmp = r;
5108                 ++tmp;
5109
5110                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5111                 if (arv) {
5112                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5113
5114                         if (progress) {
5115                                 progress->descend (1.0 / N);
5116                         }
5117
5118                         if (arv->audio_region()->apply (filter, progress) == 0) {
5119
5120                                 playlist->clear_changes ();
5121                                 playlist->clear_owned_changes ();
5122
5123                                 if (filter.results.empty ()) {
5124
5125                                         /* no regions returned; remove the old one */
5126                                         playlist->remove_region (arv->region ());
5127
5128                                 } else {
5129
5130                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5131
5132                                         /* first region replaces the old one */
5133                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5134                                         ++res;
5135
5136                                         /* add the rest */
5137                                         while (res != filter.results.end()) {
5138                                                 playlist->add_region (*res, (*res)->position());
5139                                                 ++res;
5140                                         }
5141
5142                                 }
5143
5144                                 /* We might have removed regions, which alters other regions' layering_index,
5145                                    so we need to do a recursive diff here.
5146                                 */
5147                                 vector<Command*> cmds;
5148                                 playlist->rdiff (cmds);
5149                                 _session->add_commands (cmds);
5150                                 
5151                                 _session->add_command(new StatefulDiffCommand (playlist));
5152                         } else {
5153                                 return;
5154                         }
5155
5156                         if (progress) {
5157                                 progress->ascend ();
5158                         }
5159                 }
5160
5161                 r = tmp;
5162                 ++n;
5163         }
5164
5165         commit_reversible_command ();
5166 }
5167
5168 void
5169 Editor::external_edit_region ()
5170 {
5171         /* more to come */
5172 }
5173
5174 void
5175 Editor::reset_region_gain_envelopes ()
5176 {
5177         RegionSelection rs = get_regions_from_selection_and_entered ();
5178
5179         if (!_session || rs.empty()) {
5180                 return;
5181         }
5182
5183         begin_reversible_command (_("reset region gain"));
5184         
5185         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5186                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5187                 if (arv) {
5188                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5189                         XMLNode& before (alist->get_state());
5190
5191                         arv->audio_region()->set_default_envelope ();
5192                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5193                 }
5194         }
5195         
5196         commit_reversible_command ();
5197 }
5198
5199 void
5200 Editor::set_region_gain_visibility (RegionView* rv)
5201 {
5202         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5203         if (arv) {
5204                 arv->update_envelope_visibility();
5205         }
5206 }
5207
5208 void
5209 Editor::set_gain_envelope_visibility ()
5210 {
5211         if (!_session) {
5212                 return;
5213         }
5214
5215         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5216                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5217                 if (v) {
5218                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5219                 }
5220         }
5221 }
5222
5223 void
5224 Editor::toggle_gain_envelope_active ()
5225 {
5226         if (_ignore_region_action) {
5227                 return;
5228         }
5229
5230         RegionSelection rs = get_regions_from_selection_and_entered ();
5231
5232         if (!_session || rs.empty()) {
5233                 return;
5234         }
5235
5236         begin_reversible_command (_("region gain envelope active"));
5237         
5238         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5239                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5240                 if (arv) {
5241                         arv->region()->clear_changes ();
5242                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5243                         _session->add_command (new StatefulDiffCommand (arv->region()));
5244                 }
5245         }
5246         
5247         commit_reversible_command ();
5248 }
5249
5250 void
5251 Editor::toggle_region_lock ()
5252 {
5253         if (_ignore_region_action) {
5254                 return;
5255         }
5256
5257         RegionSelection rs = get_regions_from_selection_and_entered ();
5258
5259         if (!_session || rs.empty()) {
5260                 return;
5261         }
5262
5263         begin_reversible_command (_("toggle region lock"));
5264         
5265         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5266                 (*i)->region()->clear_changes ();
5267                 (*i)->region()->set_locked (!(*i)->region()->locked());
5268                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5269         }
5270         
5271         commit_reversible_command ();
5272 }
5273
5274 void
5275 Editor::toggle_region_video_lock ()
5276 {
5277         if (_ignore_region_action) {
5278                 return;
5279         }
5280
5281         RegionSelection rs = get_regions_from_selection_and_entered ();
5282
5283         if (!_session || rs.empty()) {
5284                 return;
5285         }
5286
5287         begin_reversible_command (_("Toggle Video Lock"));
5288         
5289         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5290                 (*i)->region()->clear_changes ();
5291                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5292                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5293         }
5294         
5295         commit_reversible_command ();
5296 }
5297
5298 void
5299 Editor::toggle_region_lock_style ()
5300 {
5301         if (_ignore_region_action) {
5302                 return;
5303         }
5304
5305         RegionSelection rs = get_regions_from_selection_and_entered ();
5306
5307         if (!_session || rs.empty()) {
5308                 return;
5309         }
5310
5311         begin_reversible_command (_("region lock style"));
5312         
5313         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5314                 (*i)->region()->clear_changes ();
5315                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5316                 (*i)->region()->set_position_lock_style (ns);
5317                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5318         }
5319         
5320         commit_reversible_command ();
5321 }
5322
5323 void
5324 Editor::toggle_opaque_region ()
5325 {
5326         if (_ignore_region_action) {
5327                 return;
5328         }
5329
5330         RegionSelection rs = get_regions_from_selection_and_entered ();
5331
5332         if (!_session || rs.empty()) {
5333                 return;
5334         }
5335
5336         begin_reversible_command (_("change region opacity"));
5337         
5338         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5339                 (*i)->region()->clear_changes ();
5340                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5341                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5342         }
5343         
5344         commit_reversible_command ();
5345 }
5346
5347 void
5348 Editor::toggle_record_enable ()
5349 {
5350         bool new_state = false;
5351         bool first = true;
5352         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5353                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5354                 if (!rtav)
5355                         continue;
5356                 if (!rtav->is_track())
5357                         continue;
5358
5359                 if (first) {
5360                         new_state = !rtav->track()->record_enabled();
5361                         first = false;
5362                 }
5363
5364                 rtav->track()->set_record_enabled (new_state, this);
5365         }
5366 }
5367
5368 void
5369 Editor::toggle_solo ()
5370 {
5371         bool new_state = false;
5372         bool first = true;
5373         boost::shared_ptr<RouteList> rl (new RouteList);
5374
5375         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5376                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5377
5378                 if (!rtav) {
5379                         continue;
5380                 }
5381
5382                 if (first) {
5383                         new_state = !rtav->route()->soloed ();
5384                         first = false;
5385                 }
5386
5387                 rl->push_back (rtav->route());
5388         }
5389
5390         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5391 }
5392
5393 void
5394 Editor::toggle_mute ()
5395 {
5396         bool new_state = false;
5397         bool first = true;
5398         boost::shared_ptr<RouteList> rl (new RouteList);
5399
5400         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5401                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5402
5403                 if (!rtav) {
5404                         continue;
5405                 }
5406
5407                 if (first) {
5408                         new_state = !rtav->route()->muted();
5409                         first = false;
5410                 }
5411
5412                 rl->push_back (rtav->route());
5413         }
5414
5415         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5416 }
5417
5418 void
5419 Editor::toggle_solo_isolate ()
5420 {
5421 }
5422
5423
5424 void
5425 Editor::fade_range ()
5426 {
5427         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5428
5429         begin_reversible_command (_("fade range"));
5430
5431         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5432                 (*i)->fade_range (selection->time);
5433         }
5434
5435         commit_reversible_command ();
5436 }
5437
5438
5439 void
5440 Editor::set_fade_length (bool in)
5441 {
5442         RegionSelection rs = get_regions_from_selection_and_entered ();
5443
5444         if (rs.empty()) {
5445                 return;
5446         }
5447
5448         /* we need a region to measure the offset from the start */
5449
5450         RegionView* rv = rs.front ();
5451
5452         framepos_t pos = get_preferred_edit_position();
5453         framepos_t len;
5454         char const * cmd;
5455
5456         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5457                 /* edit point is outside the relevant region */
5458                 return;
5459         }
5460
5461         if (in) {
5462                 if (pos <= rv->region()->position()) {
5463                         /* can't do it */
5464                         return;
5465                 }
5466                 len = pos - rv->region()->position();
5467                 cmd = _("set fade in length");
5468         } else {
5469                 if (pos >= rv->region()->last_frame()) {
5470                         /* can't do it */
5471                         return;
5472                 }
5473                 len = rv->region()->last_frame() - pos;
5474                 cmd = _("set fade out length");
5475         }
5476
5477         begin_reversible_command (cmd);
5478
5479         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5480                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5481
5482                 if (!tmp) {
5483                         return;
5484                 }
5485
5486                 boost::shared_ptr<AutomationList> alist;
5487                 if (in) {
5488                         alist = tmp->audio_region()->fade_in();
5489                 } else {
5490                         alist = tmp->audio_region()->fade_out();
5491                 }
5492
5493                 XMLNode &before = alist->get_state();
5494
5495                 if (in) {
5496                         tmp->audio_region()->set_fade_in_length (len);
5497                         tmp->audio_region()->set_fade_in_active (true);
5498                 } else {
5499                         tmp->audio_region()->set_fade_out_length (len);
5500                         tmp->audio_region()->set_fade_out_active (true);
5501                 }
5502
5503                 XMLNode &after = alist->get_state();
5504                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5505         }
5506
5507         commit_reversible_command ();
5508 }
5509
5510 void
5511 Editor::set_fade_in_shape (FadeShape shape)
5512 {
5513         RegionSelection rs = get_regions_from_selection_and_entered ();
5514
5515         if (rs.empty()) {
5516                 return;
5517         }
5518
5519         begin_reversible_command (_("set fade in shape"));
5520
5521         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5522                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5523
5524                 if (!tmp) {
5525                         return;
5526                 }
5527
5528                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5529                 XMLNode &before = alist->get_state();
5530
5531                 tmp->audio_region()->set_fade_in_shape (shape);
5532
5533                 XMLNode &after = alist->get_state();
5534                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5535         }
5536
5537         commit_reversible_command ();
5538
5539 }
5540
5541 void
5542 Editor::set_fade_out_shape (FadeShape shape)
5543 {
5544         RegionSelection rs = get_regions_from_selection_and_entered ();
5545
5546         if (rs.empty()) {
5547                 return;
5548         }
5549
5550         begin_reversible_command (_("set fade out shape"));
5551
5552         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5553                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5554
5555                 if (!tmp) {
5556                         return;
5557                 }
5558
5559                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5560                 XMLNode &before = alist->get_state();
5561
5562                 tmp->audio_region()->set_fade_out_shape (shape);
5563
5564                 XMLNode &after = alist->get_state();
5565                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5566         }
5567
5568         commit_reversible_command ();
5569 }
5570
5571 void
5572 Editor::set_fade_in_active (bool yn)
5573 {
5574         RegionSelection rs = get_regions_from_selection_and_entered ();
5575
5576         if (rs.empty()) {
5577                 return;
5578         }
5579
5580         begin_reversible_command (_("set fade in active"));
5581
5582         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5583                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5584
5585                 if (!tmp) {
5586                         return;
5587                 }
5588
5589
5590                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5591
5592                 ar->clear_changes ();
5593                 ar->set_fade_in_active (yn);
5594                 _session->add_command (new StatefulDiffCommand (ar));
5595         }
5596
5597         commit_reversible_command ();
5598 }
5599
5600 void
5601 Editor::set_fade_out_active (bool yn)
5602 {
5603         RegionSelection rs = get_regions_from_selection_and_entered ();
5604
5605         if (rs.empty()) {
5606                 return;
5607         }
5608
5609         begin_reversible_command (_("set fade out active"));
5610
5611         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5612                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5613
5614                 if (!tmp) {
5615                         return;
5616                 }
5617
5618                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5619
5620                 ar->clear_changes ();
5621                 ar->set_fade_out_active (yn);
5622                 _session->add_command(new StatefulDiffCommand (ar));
5623         }
5624
5625         commit_reversible_command ();
5626 }
5627
5628 void
5629 Editor::toggle_region_fades (int dir)
5630 {
5631         if (_ignore_region_action) {
5632                 return;
5633         }
5634         
5635         boost::shared_ptr<AudioRegion> ar;
5636         bool yn = false;
5637
5638         RegionSelection rs = get_regions_from_selection_and_entered ();
5639
5640         if (rs.empty()) {
5641                 return;
5642         }
5643
5644         RegionSelection::iterator i;
5645         for (i = rs.begin(); i != rs.end(); ++i) {
5646                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5647                         if (dir == -1) {
5648                                 yn = ar->fade_out_active ();
5649                         } else {
5650                                 yn = ar->fade_in_active ();
5651                         }
5652                         break;
5653                 }
5654         }
5655
5656         if (i == rs.end()) {
5657                 return;
5658         }
5659
5660         /* XXX should this undo-able? */
5661
5662         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5663                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5664                         continue;
5665                 }
5666                 if (dir == 1 || dir == 0) {
5667                         ar->set_fade_in_active (!yn);
5668                 }
5669
5670                 if (dir == -1 || dir == 0) {
5671                         ar->set_fade_out_active (!yn);
5672                 }
5673         }
5674 }
5675
5676
5677 /** Update region fade visibility after its configuration has been changed */
5678 void
5679 Editor::update_region_fade_visibility ()
5680 {
5681         bool _fade_visibility = _session->config.get_show_region_fades ();
5682
5683         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5684                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5685                 if (v) {
5686                         if (_fade_visibility) {
5687                                 v->audio_view()->show_all_fades ();
5688                         } else {
5689                                 v->audio_view()->hide_all_fades ();
5690                         }
5691                 }
5692         }
5693 }
5694
5695 void
5696 Editor::set_edit_point ()
5697 {
5698         framepos_t where;
5699         bool ignored;
5700
5701         if (!mouse_frame (where, ignored)) {
5702                 return;
5703         }
5704
5705         snap_to (where);
5706
5707         if (selection->markers.empty()) {
5708
5709                 mouse_add_new_marker (where);
5710
5711         } else {
5712                 bool ignored;
5713
5714                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5715
5716                 if (loc) {
5717                         loc->move_to (where);
5718                 }
5719         }
5720 }
5721
5722 void
5723 Editor::set_playhead_cursor ()
5724 {
5725         if (entered_marker) {
5726                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5727         } else {
5728                 framepos_t where;
5729                 bool ignored;
5730
5731                 if (!mouse_frame (where, ignored)) {
5732                         return;
5733                 }
5734
5735                 snap_to (where);
5736
5737                 if (_session) {
5738                         _session->request_locate (where, _session->transport_rolling());
5739                 }
5740         }
5741
5742         if ( Config->get_follow_edits() )
5743                 cancel_time_selection();
5744 }
5745
5746 void
5747 Editor::split_region ()
5748 {
5749         if ( !selection->time.empty()) {
5750                 separate_regions_between (selection->time);
5751                 return;
5752         }
5753
5754         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5755
5756         framepos_t where = get_preferred_edit_position ();
5757
5758         if (rs.empty()) {
5759                 return;
5760         }
5761
5762         split_regions_at (where, rs);
5763 }
5764
5765 struct EditorOrderRouteSorter {
5766     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5767             return a->order_key () < b->order_key ();
5768     }
5769 };
5770
5771 void
5772 Editor::select_next_route()
5773 {
5774         if (selection->tracks.empty()) {
5775                 selection->set (track_views.front());
5776                 return;
5777         }
5778
5779         TimeAxisView* current = selection->tracks.front();
5780
5781         RouteUI *rui;
5782         do {
5783                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5784                         if (*i == current) {
5785                                 ++i;
5786                                 if (i != track_views.end()) {
5787                                         current = (*i);
5788                                 } else {
5789                                         current = (*(track_views.begin()));
5790                                         //selection->set (*(track_views.begin()));
5791                                 }
5792                                 break;
5793                         }
5794                 }
5795                 rui = dynamic_cast<RouteUI *>(current);
5796         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5797
5798         selection->set(current);
5799
5800         ensure_time_axis_view_is_visible (*current, false);
5801 }
5802
5803 void
5804 Editor::select_prev_route()
5805 {
5806         if (selection->tracks.empty()) {
5807                 selection->set (track_views.front());
5808                 return;
5809         }
5810
5811         TimeAxisView* current = selection->tracks.front();
5812
5813         RouteUI *rui;
5814         do {
5815                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5816                         if (*i == current) {
5817                                 ++i;
5818                                 if (i != track_views.rend()) {
5819                                         current = (*i);
5820                                 } else {
5821                                         current = *(track_views.rbegin());
5822                                 }
5823                                 break;
5824                         }
5825                 }
5826                 rui = dynamic_cast<RouteUI *>(current);
5827         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5828
5829         selection->set (current);
5830
5831         ensure_time_axis_view_is_visible (*current, false);
5832 }
5833
5834 void
5835 Editor::set_loop_from_selection (bool play)
5836 {
5837         if (_session == 0 || selection->time.empty()) {
5838                 return;
5839         }
5840
5841         framepos_t start = selection->time[clicked_selection].start;
5842         framepos_t end = selection->time[clicked_selection].end;
5843
5844         set_loop_range (start, end,  _("set loop range from selection"));
5845
5846         if (play) {
5847                 _session->request_locate (start, true);
5848                 _session->request_play_loop (true);
5849         }
5850 }
5851
5852 void
5853 Editor::set_loop_from_edit_range (bool play)
5854 {
5855         if (_session == 0) {
5856                 return;
5857         }
5858
5859         framepos_t start;
5860         framepos_t end;
5861
5862         if (!get_edit_op_range (start, end)) {
5863                 return;
5864         }
5865
5866         set_loop_range (start, end,  _("set loop range from edit range"));
5867
5868         if (play) {
5869                 _session->request_locate (start, true);
5870                 _session->request_play_loop (true);
5871         }
5872 }
5873
5874 void
5875 Editor::set_loop_from_region (bool play)
5876 {
5877         framepos_t start = max_framepos;
5878         framepos_t end = 0;
5879
5880         RegionSelection rs = get_regions_from_selection_and_entered ();
5881
5882         if (rs.empty()) {
5883                 return;
5884         }
5885
5886         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5887                 if ((*i)->region()->position() < start) {
5888                         start = (*i)->region()->position();
5889                 }
5890                 if ((*i)->region()->last_frame() + 1 > end) {
5891                         end = (*i)->region()->last_frame() + 1;
5892                 }
5893         }
5894
5895         set_loop_range (start, end, _("set loop range from region"));
5896
5897         if (play) {
5898                 _session->request_locate (start, true);
5899                 _session->request_play_loop (true);
5900         }
5901 }
5902
5903 void
5904 Editor::set_punch_from_selection ()
5905 {
5906         if (_session == 0 || selection->time.empty()) {
5907                 return;
5908         }
5909
5910         framepos_t start = selection->time[clicked_selection].start;
5911         framepos_t end = selection->time[clicked_selection].end;
5912
5913         set_punch_range (start, end,  _("set punch range from selection"));
5914 }
5915
5916 void
5917 Editor::set_session_extents_from_selection ()
5918 {
5919         if (_session == 0 || selection->time.empty()) {
5920                 return;
5921         }
5922
5923         begin_reversible_command (_("set session start/stop from selection"));
5924
5925         framepos_t start = selection->time[clicked_selection].start;
5926         framepos_t end = selection->time[clicked_selection].end;
5927
5928         Location* loc;
5929         if ((loc = _session->locations()->session_range_location()) == 0) {
5930                 _session->set_session_extents ( start, end );  // this will create a new session range;  no need for UNDO
5931         } else {
5932                 XMLNode &before = loc->get_state();
5933
5934                 _session->set_session_extents ( start, end );
5935
5936                 XMLNode &after = loc->get_state();
5937
5938                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
5939
5940                 commit_reversible_command ();
5941         }
5942 }
5943
5944 void
5945 Editor::set_punch_from_edit_range ()
5946 {
5947         if (_session == 0) {
5948                 return;
5949         }
5950
5951         framepos_t start;
5952         framepos_t end;
5953
5954         if (!get_edit_op_range (start, end)) {
5955                 return;
5956         }
5957
5958         set_punch_range (start, end,  _("set punch range from edit range"));
5959 }
5960
5961 void
5962 Editor::set_punch_from_region ()
5963 {
5964         framepos_t start = max_framepos;
5965         framepos_t end = 0;
5966
5967         RegionSelection rs = get_regions_from_selection_and_entered ();
5968
5969         if (rs.empty()) {
5970                 return;
5971         }
5972
5973         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5974                 if ((*i)->region()->position() < start) {
5975                         start = (*i)->region()->position();
5976                 }
5977                 if ((*i)->region()->last_frame() + 1 > end) {
5978                         end = (*i)->region()->last_frame() + 1;
5979                 }
5980         }
5981
5982         set_punch_range (start, end, _("set punch range from region"));
5983 }
5984
5985 void
5986 Editor::pitch_shift_region ()
5987 {
5988         RegionSelection rs = get_regions_from_selection_and_entered ();
5989
5990         RegionSelection audio_rs;
5991         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5992                 if (dynamic_cast<AudioRegionView*> (*i)) {
5993                         audio_rs.push_back (*i);
5994                 }
5995         }
5996
5997         if (audio_rs.empty()) {
5998                 return;
5999         }
6000
6001         pitch_shift (audio_rs, 1.2);
6002 }
6003
6004 void
6005 Editor::transpose_region ()
6006 {
6007         RegionSelection rs = get_regions_from_selection_and_entered ();
6008
6009         list<MidiRegionView*> midi_region_views;
6010         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6011                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
6012                 if (mrv) {
6013                         midi_region_views.push_back (mrv);
6014                 }
6015         }
6016
6017         TransposeDialog d;
6018         int const r = d.run ();
6019         if (r != RESPONSE_ACCEPT) {
6020                 return;
6021         }
6022
6023         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
6024                 (*i)->midi_region()->transpose (d.semitones ());
6025         }
6026 }
6027
6028 void
6029 Editor::set_tempo_from_region ()
6030 {
6031         RegionSelection rs = get_regions_from_selection_and_entered ();
6032
6033         if (!_session || rs.empty()) {
6034                 return;
6035         }
6036
6037         RegionView* rv = rs.front();
6038
6039         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6040 }
6041
6042 void
6043 Editor::use_range_as_bar ()
6044 {
6045         framepos_t start, end;
6046         if (get_edit_op_range (start, end)) {
6047                 define_one_bar (start, end);
6048         }
6049 }
6050
6051 void
6052 Editor::define_one_bar (framepos_t start, framepos_t end)
6053 {
6054         framepos_t length = end - start;
6055
6056         const Meter& m (_session->tempo_map().meter_at (start));
6057
6058         /* length = 1 bar */
6059
6060         /* now we want frames per beat.
6061            we have frames per bar, and beats per bar, so ...
6062         */
6063
6064         /* XXXX METER MATH */
6065
6066         double frames_per_beat = length / m.divisions_per_bar();
6067
6068         /* beats per minute = */
6069
6070         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6071
6072         /* now decide whether to:
6073
6074             (a) set global tempo
6075             (b) add a new tempo marker
6076
6077         */
6078
6079         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
6080
6081         bool do_global = false;
6082
6083         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6084
6085                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6086                    at the start, or create a new marker
6087                 */
6088
6089                 vector<string> options;
6090                 options.push_back (_("Cancel"));
6091                 options.push_back (_("Add new marker"));
6092                 options.push_back (_("Set global tempo"));
6093
6094                 Choice c (
6095                         _("Define one bar"),
6096                         _("Do you want to set the global tempo or add a new tempo marker?"),
6097                         options
6098                         );
6099
6100                 c.set_default_response (2);
6101
6102                 switch (c.run()) {
6103                 case 0:
6104                         return;
6105
6106                 case 2:
6107                         do_global = true;
6108                         break;
6109
6110                 default:
6111                         do_global = false;
6112                 }
6113
6114         } else {
6115
6116                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6117                    if the marker is at the region starter, change it, otherwise add
6118                    a new tempo marker
6119                 */
6120         }
6121
6122         begin_reversible_command (_("set tempo from region"));
6123         XMLNode& before (_session->tempo_map().get_state());
6124
6125         if (do_global) {
6126                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6127         } else if (t.frame() == start) {
6128                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6129         } else {
6130                 Timecode::BBT_Time bbt;
6131                 _session->tempo_map().bbt_time (start, bbt);
6132                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
6133         }
6134
6135         XMLNode& after (_session->tempo_map().get_state());
6136
6137         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6138         commit_reversible_command ();
6139 }
6140
6141 void
6142 Editor::split_region_at_transients ()
6143 {
6144         AnalysisFeatureList positions;
6145
6146         RegionSelection rs = get_regions_from_selection_and_entered ();
6147
6148         if (!_session || rs.empty()) {
6149                 return;
6150         }
6151
6152         begin_reversible_command (_("split regions"));
6153         
6154         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6155
6156                 RegionSelection::iterator tmp;
6157
6158                 tmp = i;
6159                 ++tmp;
6160
6161                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6162
6163                 if (ar && (ar->get_transients (positions) == 0)) {
6164                         split_region_at_points ((*i)->region(), positions, true);
6165                         positions.clear ();
6166                 }
6167
6168                 i = tmp;
6169         }
6170         
6171         commit_reversible_command ();
6172
6173 }
6174
6175 void
6176 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6177 {
6178         bool use_rhythmic_rodent = false;
6179
6180         boost::shared_ptr<Playlist> pl = r->playlist();
6181
6182         list<boost::shared_ptr<Region> > new_regions;
6183
6184         if (!pl) {
6185                 return;
6186         }
6187
6188         if (positions.empty()) {
6189                 return;
6190         }
6191
6192
6193         if (positions.size() > 20 && can_ferret) {
6194                 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);
6195                 MessageDialog msg (msgstr,
6196                                    false,
6197                                    Gtk::MESSAGE_INFO,
6198                                    Gtk::BUTTONS_OK_CANCEL);
6199
6200                 if (can_ferret) {
6201                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6202                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6203                 } else {
6204                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6205                 }
6206
6207                 msg.set_title (_("Excessive split?"));
6208                 msg.present ();
6209
6210                 int response = msg.run();
6211                 msg.hide ();
6212
6213                 switch (response) {
6214                 case RESPONSE_OK:
6215                         break;
6216                 case RESPONSE_APPLY:
6217                         use_rhythmic_rodent = true;
6218                         break;
6219                 default:
6220                         return;
6221                 }
6222         }
6223
6224         if (use_rhythmic_rodent) {
6225                 show_rhythm_ferret ();
6226                 return;
6227         }
6228
6229         AnalysisFeatureList::const_iterator x;
6230
6231         pl->clear_changes ();
6232         pl->clear_owned_changes ();
6233
6234         x = positions.begin();
6235
6236         if (x == positions.end()) {
6237                 return;
6238         }
6239
6240         pl->freeze ();
6241         pl->remove_region (r);
6242
6243         framepos_t pos = 0;
6244
6245         while (x != positions.end()) {
6246
6247                 /* deal with positons that are out of scope of present region bounds */
6248                 if (*x <= 0 || *x > r->length()) {
6249                         ++x;
6250                         continue;
6251                 }
6252
6253                 /* file start = original start + how far we from the initial position ?
6254                  */
6255
6256                 framepos_t file_start = r->start() + pos;
6257
6258                 /* length = next position - current position
6259                  */
6260
6261                 framepos_t len = (*x) - pos;
6262
6263                 /* XXX we do we really want to allow even single-sample regions?
6264                    shouldn't we have some kind of lower limit on region size?
6265                 */
6266
6267                 if (len <= 0) {
6268                         break;
6269                 }
6270
6271                 string new_name;
6272
6273                 if (RegionFactory::region_name (new_name, r->name())) {
6274                         break;
6275                 }
6276
6277                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6278
6279                 PropertyList plist;
6280
6281                 plist.add (ARDOUR::Properties::start, file_start);
6282                 plist.add (ARDOUR::Properties::length, len);
6283                 plist.add (ARDOUR::Properties::name, new_name);
6284                 plist.add (ARDOUR::Properties::layer, 0);
6285
6286                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6287                 /* because we set annouce to false, manually add the new region to the
6288                    RegionFactory map
6289                 */
6290                 RegionFactory::map_add (nr);
6291
6292                 pl->add_region (nr, r->position() + pos);
6293
6294                 if (select_new) {
6295                         new_regions.push_front(nr);
6296                 }
6297
6298                 pos += len;
6299                 ++x;
6300         }
6301
6302         string new_name;
6303
6304         RegionFactory::region_name (new_name, r->name());
6305
6306         /* Add the final region */
6307         PropertyList plist;
6308
6309         plist.add (ARDOUR::Properties::start, r->start() + pos);
6310         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6311         plist.add (ARDOUR::Properties::name, new_name);
6312         plist.add (ARDOUR::Properties::layer, 0);
6313
6314         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6315         /* because we set annouce to false, manually add the new region to the
6316            RegionFactory map
6317         */
6318         RegionFactory::map_add (nr);
6319         pl->add_region (nr, r->position() + pos);
6320
6321         if (select_new) {
6322                 new_regions.push_front(nr);
6323         }
6324
6325         pl->thaw ();
6326
6327         /* We might have removed regions, which alters other regions' layering_index,
6328            so we need to do a recursive diff here.
6329         */
6330         vector<Command*> cmds;
6331         pl->rdiff (cmds);
6332         _session->add_commands (cmds);
6333         
6334         _session->add_command (new StatefulDiffCommand (pl));
6335
6336         if (select_new) {
6337
6338                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6339                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6340                 }
6341         }
6342 }
6343
6344 void
6345 Editor::place_transient()
6346 {
6347         if (!_session) {
6348                 return;
6349         }
6350
6351         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6352
6353         if (rs.empty()) {
6354                 return;
6355         }
6356
6357         framepos_t where = get_preferred_edit_position();
6358
6359         begin_reversible_command (_("place transient"));
6360         
6361         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6362                 framepos_t position = (*r)->region()->position();
6363                 (*r)->region()->add_transient(where - position);
6364         }
6365         
6366         commit_reversible_command ();
6367 }
6368
6369 void
6370 Editor::remove_transient(ArdourCanvas::Item* item)
6371 {
6372         if (!_session) {
6373                 return;
6374         }
6375
6376         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6377         assert (_line);
6378
6379         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6380         _arv->remove_transient (*(float*) _line->get_data ("position"));
6381 }
6382
6383 void
6384 Editor::snap_regions_to_grid ()
6385 {
6386         list <boost::shared_ptr<Playlist > > used_playlists;
6387
6388         RegionSelection rs = get_regions_from_selection_and_entered ();
6389
6390         if (!_session || rs.empty()) {
6391                 return;
6392         }
6393
6394         begin_reversible_command (_("snap regions to grid"));
6395         
6396         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6397
6398                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6399
6400                 if (!pl->frozen()) {
6401                         /* we haven't seen this playlist before */
6402
6403                         /* remember used playlists so we can thaw them later */
6404                         used_playlists.push_back(pl);
6405                         pl->freeze();
6406                 }
6407
6408                 framepos_t start_frame = (*r)->region()->first_frame ();
6409                 snap_to (start_frame);
6410                 (*r)->region()->set_position (start_frame);
6411         }
6412
6413         while (used_playlists.size() > 0) {
6414                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6415                 (*i)->thaw();
6416                 used_playlists.pop_front();
6417         }
6418         
6419         commit_reversible_command ();
6420 }
6421
6422 void
6423 Editor::close_region_gaps ()
6424 {
6425         list <boost::shared_ptr<Playlist > > used_playlists;
6426
6427         RegionSelection rs = get_regions_from_selection_and_entered ();
6428
6429         if (!_session || rs.empty()) {
6430                 return;
6431         }
6432
6433         Dialog dialog (_("Close Region Gaps"));
6434
6435         Table table (2, 3);
6436         table.set_spacings (12);
6437         table.set_border_width (12);
6438         Label* l = manage (left_aligned_label (_("Crossfade length")));
6439         table.attach (*l, 0, 1, 0, 1);
6440
6441         SpinButton spin_crossfade (1, 0);
6442         spin_crossfade.set_range (0, 15);
6443         spin_crossfade.set_increments (1, 1);
6444         spin_crossfade.set_value (5);
6445         table.attach (spin_crossfade, 1, 2, 0, 1);
6446
6447         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6448
6449         l = manage (left_aligned_label (_("Pull-back length")));
6450         table.attach (*l, 0, 1, 1, 2);
6451
6452         SpinButton spin_pullback (1, 0);
6453         spin_pullback.set_range (0, 100);
6454         spin_pullback.set_increments (1, 1);
6455         spin_pullback.set_value(30);
6456         table.attach (spin_pullback, 1, 2, 1, 2);
6457
6458         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6459
6460         dialog.get_vbox()->pack_start (table);
6461         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6462         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6463         dialog.show_all ();
6464
6465         if (dialog.run () == RESPONSE_CANCEL) {
6466                 return;
6467         }
6468
6469         framepos_t crossfade_len = spin_crossfade.get_value();
6470         framepos_t pull_back_frames = spin_pullback.get_value();
6471
6472         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6473         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6474
6475         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6476
6477         begin_reversible_command (_("close region gaps"));
6478         
6479         int idx = 0;
6480         boost::shared_ptr<Region> last_region;
6481
6482         rs.sort_by_position_and_track();
6483
6484         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6485
6486                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6487
6488                 if (!pl->frozen()) {
6489                         /* we haven't seen this playlist before */
6490
6491                         /* remember used playlists so we can thaw them later */
6492                         used_playlists.push_back(pl);
6493                         pl->freeze();
6494                 }
6495
6496                 framepos_t position = (*r)->region()->position();
6497
6498                 if (idx == 0 || position < last_region->position()){
6499                         last_region = (*r)->region();
6500                         idx++;
6501                         continue;
6502                 }
6503
6504                 (*r)->region()->trim_front( (position - pull_back_frames));
6505                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6506
6507                 last_region = (*r)->region();
6508
6509                 idx++;
6510         }
6511
6512         while (used_playlists.size() > 0) {
6513                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6514                 (*i)->thaw();
6515                 used_playlists.pop_front();
6516         }
6517         
6518         commit_reversible_command ();
6519 }
6520
6521 void
6522 Editor::tab_to_transient (bool forward)
6523 {
6524         AnalysisFeatureList positions;
6525
6526         RegionSelection rs = get_regions_from_selection_and_entered ();
6527
6528         if (!_session) {
6529                 return;
6530         }
6531
6532         framepos_t pos = _session->audible_frame ();
6533
6534         if (!selection->tracks.empty()) {
6535
6536                 /* don't waste time searching for transients in duplicate playlists.
6537                  */
6538
6539                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6540
6541                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6542
6543                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6544
6545                         if (rtv) {
6546                                 boost::shared_ptr<Track> tr = rtv->track();
6547                                 if (tr) {
6548                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6549                                         if (pl) {
6550                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6551
6552                                                 if (result >= 0) {
6553                                                         positions.push_back (result);
6554                                                 }
6555                                         }
6556                                 }
6557                         }
6558                 }
6559
6560         } else {
6561
6562                 if (rs.empty()) {
6563                         return;
6564                 }
6565
6566                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6567                         (*r)->region()->get_transients (positions);
6568                 }
6569         }
6570
6571         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6572
6573         if (forward) {
6574                 AnalysisFeatureList::iterator x;
6575
6576                 for (x = positions.begin(); x != positions.end(); ++x) {
6577                         if ((*x) > pos) {
6578                                 break;
6579                         }
6580                 }
6581
6582                 if (x != positions.end ()) {
6583                         _session->request_locate (*x);
6584                 }
6585
6586         } else {
6587                 AnalysisFeatureList::reverse_iterator x;
6588
6589                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6590                         if ((*x) < pos) {
6591                                 break;
6592                         }
6593                 }
6594
6595                 if (x != positions.rend ()) {
6596                         _session->request_locate (*x);
6597                 }
6598         }
6599 }
6600
6601 void
6602 Editor::playhead_forward_to_grid ()
6603 {
6604         if (!_session) {
6605                 return;
6606         }
6607         
6608         framepos_t pos = playhead_cursor->current_frame ();
6609         if (pos < max_framepos - 1) {
6610                 pos += 2;
6611                 snap_to_internal (pos, RoundUpAlways, false);
6612                 _session->request_locate (pos);
6613         }
6614 }
6615
6616
6617 void
6618 Editor::playhead_backward_to_grid ()
6619 {
6620         if (!_session) {
6621                 return;
6622         }
6623         
6624         framepos_t pos = playhead_cursor->current_frame ();
6625         if (pos > 2) {
6626                 pos -= 2;
6627                 snap_to_internal (pos, RoundDownAlways, false);
6628                 _session->request_locate (pos);
6629         }
6630 }
6631
6632 void
6633 Editor::set_track_height (Height h)
6634 {
6635         TrackSelection& ts (selection->tracks);
6636
6637         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6638                 (*x)->set_height_enum (h);
6639         }
6640 }
6641
6642 void
6643 Editor::toggle_tracks_active ()
6644 {
6645         TrackSelection& ts (selection->tracks);
6646         bool first = true;
6647         bool target = false;
6648
6649         if (ts.empty()) {
6650                 return;
6651         }
6652
6653         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6654                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6655
6656                 if (rtv) {
6657                         if (first) {
6658                                 target = !rtv->_route->active();
6659                                 first = false;
6660                         }
6661                         rtv->_route->set_active (target, this);
6662                 }
6663         }
6664 }
6665
6666 void
6667 Editor::remove_tracks ()
6668 {
6669         TrackSelection& ts (selection->tracks);
6670
6671         if (ts.empty()) {
6672                 return;
6673         }
6674
6675         vector<string> choices;
6676         string prompt;
6677         int ntracks = 0;
6678         int nbusses = 0;
6679         const char* trackstr;
6680         const char* busstr;
6681         vector<boost::shared_ptr<Route> > routes;
6682         bool special_bus = false;
6683
6684         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6685                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6686                 if (!rtv) {
6687                         continue;
6688                 }
6689                 if (rtv->is_track()) {
6690                         ntracks++;
6691                 } else {
6692                         nbusses++;
6693                 }
6694                 routes.push_back (rtv->_route);
6695
6696                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6697                         special_bus = true;
6698                 }
6699         }
6700
6701         if (special_bus && !Config->get_allow_special_bus_removal()) {
6702                 MessageDialog msg (_("That would be bad news ...."),
6703                                    false,
6704                                    Gtk::MESSAGE_INFO,
6705                                    Gtk::BUTTONS_OK);
6706                 msg.set_secondary_text (string_compose (_(
6707                                                                 "Removing the master or monitor bus is such a bad idea\n\
6708 that %1 is not going to allow it.\n\
6709 \n\
6710 If you really want to do this sort of thing\n\
6711 edit your ardour.rc file to set the\n\
6712 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6713
6714                 msg.present ();
6715                 msg.run ();
6716                 return;
6717         }
6718
6719         if (ntracks + nbusses == 0) {
6720                 return;
6721         }
6722
6723         // XXX should be using gettext plural forms, maybe?
6724         if (ntracks > 1) {
6725                 trackstr = _("tracks");
6726         } else {
6727                 trackstr = _("track");
6728         }
6729
6730         if (nbusses > 1) {
6731                 busstr = _("busses");
6732         } else {
6733                 busstr = _("bus");
6734         }
6735
6736         if (ntracks) {
6737                 if (nbusses) {
6738                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6739                                                     "(You may also lose the playlists associated with the %2)\n\n"
6740                                                     "This action cannot be undone, and the session file will be overwritten!"),
6741                                                   ntracks, trackstr, nbusses, busstr);
6742                 } else {
6743                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
6744                                                     "(You may also lose the playlists associated with the %2)\n\n"
6745                                                     "This action cannot be undone, and the session file will be overwritten!"),
6746                                                   ntracks, trackstr);
6747                 }
6748         } else if (nbusses) {
6749                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
6750                                             "This action cannot be undone, and the session file will be overwritten"),
6751                                           nbusses, busstr);
6752         }
6753
6754         choices.push_back (_("No, do nothing."));
6755         if (ntracks + nbusses > 1) {
6756                 choices.push_back (_("Yes, remove them."));
6757         } else {
6758                 choices.push_back (_("Yes, remove it."));
6759         }
6760
6761         string title;
6762         if (ntracks) {
6763                 title = string_compose (_("Remove %1"), trackstr);
6764         } else {
6765                 title = string_compose (_("Remove %1"), busstr);
6766         }
6767
6768         Choice prompter (title, prompt, choices);
6769
6770         if (prompter.run () != 1) {
6771                 return;
6772         }
6773
6774         {
6775                 Session::StateProtector sp (_session);
6776                 DisplaySuspender ds;
6777                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6778                         _session->remove_route (*x);
6779                 }
6780         }
6781 }
6782
6783 void
6784 Editor::do_insert_time ()
6785 {
6786         if (selection->tracks.empty()) {
6787                 return;
6788         }
6789
6790         InsertTimeDialog d (*this);
6791         int response = d.run ();
6792
6793         if (response != RESPONSE_OK) {
6794                 return;
6795         }
6796
6797         if (d.distance() == 0) {
6798                 return;
6799         }
6800
6801         InsertTimeOption opt = d.intersected_region_action ();
6802
6803         insert_time (
6804                 get_preferred_edit_position(),
6805                 d.distance(),
6806                 opt,
6807                 d.all_playlists(),
6808                 d.move_glued(),
6809                 d.move_markers(),
6810                 d.move_glued_markers(),
6811                 d.move_locked_markers(),
6812                 d.move_tempos()
6813                 );
6814 }
6815
6816 void
6817 Editor::insert_time (
6818         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6819         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6820         )
6821 {
6822         bool commit = false;
6823
6824         if (Config->get_edit_mode() == Lock) {
6825                 return;
6826         }
6827
6828         begin_reversible_command (_("insert time"));
6829
6830         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6831
6832         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6833
6834                 /* regions */
6835
6836                 /* don't operate on any playlist more than once, which could
6837                  * happen if "all playlists" is enabled, but there is more
6838                  * than 1 track using playlists "from" a given track.
6839                  */
6840
6841                 set<boost::shared_ptr<Playlist> > pl;
6842
6843                 if (all_playlists) {
6844                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6845                         if (rtav) {
6846                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6847                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6848                                         pl.insert (*p);
6849                                 }
6850                         }
6851                 } else {
6852                         if ((*x)->playlist ()) {
6853                                 pl.insert ((*x)->playlist ());
6854                         }
6855                 }
6856                 
6857                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6858
6859                         (*i)->clear_changes ();
6860                         (*i)->clear_owned_changes ();
6861
6862                         if (opt == SplitIntersected) {
6863                                 (*i)->split (pos);
6864                         }
6865
6866                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6867
6868                         vector<Command*> cmds;
6869                         (*i)->rdiff (cmds);
6870                         _session->add_commands (cmds);
6871
6872                         _session->add_command (new StatefulDiffCommand (*i));
6873                         commit = true;
6874                 }
6875
6876                 /* automation */
6877                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6878                 if (rtav) {
6879                         rtav->route ()->shift (pos, frames);
6880                         commit = true;
6881                 }
6882         }
6883
6884         /* markers */
6885         if (markers_too) {
6886                 bool moved = false;
6887                 XMLNode& before (_session->locations()->get_state());
6888                 Locations::LocationList copy (_session->locations()->list());
6889
6890                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6891
6892                         Locations::LocationList::const_iterator tmp;
6893
6894                         bool const was_locked = (*i)->locked ();
6895                         if (locked_markers_too) {
6896                                 (*i)->unlock ();
6897                         }
6898
6899                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6900
6901                                 if ((*i)->start() >= pos) {
6902                                         (*i)->set_start ((*i)->start() + frames);
6903                                         if (!(*i)->is_mark()) {
6904                                                 (*i)->set_end ((*i)->end() + frames);
6905                                         }
6906                                         moved = true;
6907                                 }
6908
6909                         }
6910
6911                         if (was_locked) {
6912                                 (*i)->lock ();
6913                         }
6914                 }
6915
6916                 if (moved) {
6917                         XMLNode& after (_session->locations()->get_state());
6918                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6919                 }
6920         }
6921
6922         if (tempo_too) {
6923                 _session->tempo_map().insert_time (pos, frames);
6924         }
6925
6926         if (commit) {
6927                 commit_reversible_command ();
6928         }
6929 }
6930
6931 void
6932 Editor::fit_selected_tracks ()
6933 {
6934         if (!selection->tracks.empty()) {
6935                 fit_tracks (selection->tracks);
6936         } else {
6937                 TrackViewList tvl;
6938
6939                 /* no selected tracks - use tracks with selected regions */
6940
6941                 if (!selection->regions.empty()) {
6942                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6943                                 tvl.push_back (&(*r)->get_time_axis_view ());
6944                         }
6945
6946                         if (!tvl.empty()) {
6947                                 fit_tracks (tvl);
6948                         }
6949                 } else if (internal_editing()) {
6950                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6951                            the entered track
6952                         */
6953                         if (entered_track) {
6954                                 tvl.push_back (entered_track);
6955                                 fit_tracks (tvl);
6956                         }
6957                 }
6958         }
6959
6960 }
6961
6962 void
6963 Editor::fit_tracks (TrackViewList & tracks)
6964 {
6965         if (tracks.empty()) {
6966                 return;
6967         }
6968
6969         uint32_t child_heights = 0;
6970         int visible_tracks = 0;
6971
6972         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6973
6974                 if (!(*t)->marked_for_display()) {
6975                         continue;
6976                 }
6977
6978                 child_heights += (*t)->effective_height() - (*t)->current_height();
6979                 ++visible_tracks;
6980         }
6981
6982         /* compute the per-track height from:
6983
6984            total canvas visible height - 
6985                  height that will be taken by visible children of selected
6986                  tracks - height of the ruler/hscroll area 
6987         */
6988         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
6989         double first_y_pos = DBL_MAX;
6990
6991         if (h < TimeAxisView::preset_height (HeightSmall)) {
6992                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6993                 /* too small to be displayed */
6994                 return;
6995         }
6996
6997         undo_visual_stack.push_back (current_visual_state (true));
6998         no_save_visual = true;
6999
7000         /* build a list of all tracks, including children */
7001
7002         TrackViewList all;
7003         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7004                 all.push_back (*i);
7005                 TimeAxisView::Children c = (*i)->get_child_list ();
7006                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7007                         all.push_back (j->get());
7008                 }
7009         }
7010
7011         bool prev_was_selected = false;
7012         bool is_selected = tracks.contains (all.front());
7013         bool next_is_selected;
7014
7015         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
7016
7017                 TrackViewList::iterator next;
7018
7019                 next = t;
7020                 ++next;
7021
7022                 if (next != all.end()) {
7023                         next_is_selected = tracks.contains (*next);
7024                 } else {
7025                         next_is_selected = false;
7026                 }
7027
7028                 if ((*t)->marked_for_display ()) {
7029                         if (is_selected) {
7030                                 (*t)->set_height (h);
7031                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7032                         } else {
7033                                 if (prev_was_selected && next_is_selected) {
7034                                         hide_track_in_display (*t);
7035                                 }
7036                         }
7037                 }
7038
7039                 prev_was_selected = is_selected;
7040                 is_selected = next_is_selected;
7041         }
7042
7043         /*
7044            set the controls_layout height now, because waiting for its size
7045            request signal handler will cause the vertical adjustment setting to fail
7046         */
7047
7048         controls_layout.property_height () = _full_canvas_height;
7049         vertical_adjustment.set_value (first_y_pos);
7050
7051         redo_visual_stack.push_back (current_visual_state (true));
7052
7053         visible_tracks_selector.set_text (_("Sel"));
7054 }
7055
7056 void
7057 Editor::save_visual_state (uint32_t n)
7058 {
7059         while (visual_states.size() <= n) {
7060                 visual_states.push_back (0);
7061         }
7062
7063         if (visual_states[n] != 0) {
7064                 delete visual_states[n];
7065         }
7066
7067         visual_states[n] = current_visual_state (true);
7068         gdk_beep ();
7069 }
7070
7071 void
7072 Editor::goto_visual_state (uint32_t n)
7073 {
7074         if (visual_states.size() <= n) {
7075                 return;
7076         }
7077
7078         if (visual_states[n] == 0) {
7079                 return;
7080         }
7081
7082         use_visual_state (*visual_states[n]);
7083 }
7084
7085 void
7086 Editor::start_visual_state_op (uint32_t n)
7087 {
7088         save_visual_state (n);
7089         
7090         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7091         char buf[32];
7092         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7093         pup->set_text (buf);
7094         pup->touch();
7095 }
7096
7097 void
7098 Editor::cancel_visual_state_op (uint32_t n)
7099 {
7100         goto_visual_state (n);
7101 }
7102
7103 void
7104 Editor::toggle_region_mute ()
7105 {
7106         if (_ignore_region_action) {
7107                 return;
7108         }
7109
7110         RegionSelection rs = get_regions_from_selection_and_entered ();
7111
7112         if (rs.empty ()) {
7113                 return;
7114         }
7115
7116         if (rs.size() > 1) {
7117                 begin_reversible_command (_("mute regions"));
7118         } else {
7119                 begin_reversible_command (_("mute region"));
7120         }
7121
7122         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7123
7124                 (*i)->region()->playlist()->clear_changes ();
7125                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7126                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7127
7128         }
7129
7130         commit_reversible_command ();
7131 }
7132
7133 void
7134 Editor::combine_regions ()
7135 {
7136         /* foreach track with selected regions, take all selected regions
7137            and join them into a new region containing the subregions (as a
7138            playlist)
7139         */
7140
7141         typedef set<RouteTimeAxisView*> RTVS;
7142         RTVS tracks;
7143
7144         if (selection->regions.empty()) {
7145                 return;
7146         }
7147
7148         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7149                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7150
7151                 if (rtv) {
7152                         tracks.insert (rtv);
7153                 }
7154         }
7155
7156         begin_reversible_command (_("combine regions"));
7157
7158         vector<RegionView*> new_selection;
7159
7160         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7161                 RegionView* rv;
7162
7163                 if ((rv = (*i)->combine_regions ()) != 0) {
7164                         new_selection.push_back (rv);
7165                 }
7166         }
7167
7168         selection->clear_regions ();
7169         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7170                 selection->add (*i);
7171         }
7172
7173         commit_reversible_command ();
7174 }
7175
7176 void
7177 Editor::uncombine_regions ()
7178 {
7179         typedef set<RouteTimeAxisView*> RTVS;
7180         RTVS tracks;
7181
7182         if (selection->regions.empty()) {
7183                 return;
7184         }
7185
7186         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7187                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7188
7189                 if (rtv) {
7190                         tracks.insert (rtv);
7191                 }
7192         }
7193
7194         begin_reversible_command (_("uncombine regions"));
7195
7196         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7197                 (*i)->uncombine_regions ();
7198         }
7199
7200         commit_reversible_command ();
7201 }
7202
7203 void
7204 Editor::toggle_midi_input_active (bool flip_others)
7205 {
7206         bool onoff = false;
7207         boost::shared_ptr<RouteList> rl (new RouteList);
7208
7209         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7210                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7211
7212                 if (!rtav) {
7213                         continue;
7214                 }
7215
7216                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7217
7218                 if (mt) {
7219                         rl->push_back (rtav->route());
7220                         onoff = !mt->input_active();
7221                 }
7222         }
7223         
7224         _session->set_exclusive_input_active (rl, onoff, flip_others);
7225 }
7226
7227 void
7228 Editor::lock ()
7229 {
7230         if (!lock_dialog) {
7231                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
7232
7233                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
7234                 lock_dialog->get_vbox()->pack_start (*padlock);
7235
7236                 ArdourButton* b = manage (new ArdourButton);
7237                 b->set_name ("lock button");
7238                 b->set_text (_("Click to unlock"));
7239                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
7240                 lock_dialog->get_vbox()->pack_start (*b);
7241                 
7242                 lock_dialog->get_vbox()->show_all ();
7243                 lock_dialog->set_size_request (200, 200);
7244         }
7245         
7246 #ifdef __APPLE__
7247         /* The global menu bar continues to be accessible to applications
7248            with modal dialogs, which means that we need to desensitize
7249            all items in the menu bar. Since those items are really just
7250            proxies for actions, that means disabling all actions.
7251         */
7252         ActionManager::disable_all_actions ();
7253 #endif
7254         lock_dialog->present ();
7255 }
7256
7257 void
7258 Editor::unlock ()
7259 {
7260         lock_dialog->hide ();
7261         
7262 #ifdef __APPLE__
7263         ActionManager::pop_action_state ();
7264 #endif  
7265
7266         if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
7267                 start_lock_event_timing ();
7268         }
7269 }
7270
7271 void
7272 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7273 {
7274         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
7275 }
7276
7277 void
7278 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7279 {
7280         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
7281         Gtkmm2ext::UI::instance()->flush_pending ();
7282 }
7283
7284 void
7285 Editor::bring_all_sources_into_session ()
7286 {
7287         if (!_session) {
7288                 return;
7289         }
7290
7291         Gtk::Label msg;
7292         ArdourDialog w (_("Moving embedded files into session folder"));
7293         w.get_vbox()->pack_start (msg);
7294         w.present ();
7295         
7296         /* flush all pending GUI events because we're about to start copying
7297          * files
7298          */
7299         
7300         Gtkmm2ext::UI::instance()->flush_pending ();
7301
7302         cerr << " Do it\n";
7303
7304         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
7305 }