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