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