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