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