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