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