more messing about with inconsistent menu state.
[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         bool have_position_lock_style_audio = false;
5747         bool have_position_lock_style_music = false;
5748         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5749                 if ((*i)->region()->position_lock_style() == MusicTime) {
5750                         have_position_lock_style_music = true;
5751                 } else {
5752                         have_position_lock_style_audio = true;
5753                 }
5754         }
5755         bool const inconsistent = have_position_lock_style_audio && have_position_lock_style_music;
5756
5757         begin_reversible_command (_("toggle region lock style"));
5758
5759         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5760                 (*i)->region()->clear_changes ();
5761                 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !inconsistent) ? MusicTime : AudioTime;
5762                 (*i)->region()->set_position_lock_style (ns);
5763                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5764         }
5765
5766         commit_reversible_command ();
5767 }
5768
5769 void
5770 Editor::toggle_opaque_region ()
5771 {
5772         if (_ignore_region_action) {
5773                 return;
5774         }
5775
5776         RegionSelection rs = get_regions_from_selection_and_entered ();
5777
5778         if (!_session || rs.empty()) {
5779                 return;
5780         }
5781
5782         begin_reversible_command (_("change region opacity"));
5783
5784         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5785                 (*i)->region()->clear_changes ();
5786                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5787                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5788         }
5789
5790         commit_reversible_command ();
5791 }
5792
5793 void
5794 Editor::toggle_record_enable ()
5795 {
5796         bool new_state = false;
5797         bool first = true;
5798         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5799                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5800                 if (!rtav)
5801                         continue;
5802                 if (!rtav->is_track())
5803                         continue;
5804
5805                 if (first) {
5806                         new_state = !rtav->track()->rec_enable_control()->get_value();
5807                         first = false;
5808                 }
5809
5810                 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5811         }
5812 }
5813
5814 void
5815 Editor::toggle_solo ()
5816 {
5817         bool new_state = false;
5818         bool first = true;
5819         boost::shared_ptr<ControlList> cl (new ControlList);
5820
5821         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5822                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5823
5824                 if (!rtav) {
5825                         continue;
5826                 }
5827
5828                 if (first) {
5829                         new_state = !rtav->route()->soloed ();
5830                         first = false;
5831                 }
5832
5833                 cl->push_back (rtav->route()->solo_control());
5834         }
5835
5836         _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5837 }
5838
5839 void
5840 Editor::toggle_mute ()
5841 {
5842         bool new_state = false;
5843         bool first = true;
5844         boost::shared_ptr<RouteList> rl (new RouteList);
5845
5846         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5847                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5848
5849                 if (!rtav) {
5850                         continue;
5851                 }
5852
5853                 if (first) {
5854                         new_state = !rtav->route()->muted();
5855                         first = false;
5856                 }
5857
5858                 rl->push_back (rtav->route());
5859         }
5860
5861         _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5862 }
5863
5864 void
5865 Editor::toggle_solo_isolate ()
5866 {
5867 }
5868
5869
5870 void
5871 Editor::fade_range ()
5872 {
5873         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5874
5875         begin_reversible_command (_("fade range"));
5876
5877         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5878                 (*i)->fade_range (selection->time);
5879         }
5880
5881         commit_reversible_command ();
5882 }
5883
5884
5885 void
5886 Editor::set_fade_length (bool in)
5887 {
5888         RegionSelection rs = get_regions_from_selection_and_entered ();
5889
5890         if (rs.empty()) {
5891                 return;
5892         }
5893
5894         /* we need a region to measure the offset from the start */
5895
5896         RegionView* rv = rs.front ();
5897
5898         framepos_t pos = get_preferred_edit_position();
5899         framepos_t len;
5900         char const * cmd;
5901
5902         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5903                 /* edit point is outside the relevant region */
5904                 return;
5905         }
5906
5907         if (in) {
5908                 if (pos <= rv->region()->position()) {
5909                         /* can't do it */
5910                         return;
5911                 }
5912                 len = pos - rv->region()->position();
5913                 cmd = _("set fade in length");
5914         } else {
5915                 if (pos >= rv->region()->last_frame()) {
5916                         /* can't do it */
5917                         return;
5918                 }
5919                 len = rv->region()->last_frame() - pos;
5920                 cmd = _("set fade out length");
5921         }
5922
5923         bool in_command = false;
5924
5925         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5926                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5927
5928                 if (!tmp) {
5929                         continue;
5930                 }
5931
5932                 boost::shared_ptr<AutomationList> alist;
5933                 if (in) {
5934                         alist = tmp->audio_region()->fade_in();
5935                 } else {
5936                         alist = tmp->audio_region()->fade_out();
5937                 }
5938
5939                 XMLNode &before = alist->get_state();
5940
5941                 if (in) {
5942                         tmp->audio_region()->set_fade_in_length (len);
5943                         tmp->audio_region()->set_fade_in_active (true);
5944                 } else {
5945                         tmp->audio_region()->set_fade_out_length (len);
5946                         tmp->audio_region()->set_fade_out_active (true);
5947                 }
5948
5949                 if (!in_command) {
5950                         begin_reversible_command (cmd);
5951                         in_command = true;
5952                 }
5953                 XMLNode &after = alist->get_state();
5954                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5955         }
5956
5957         if (in_command) {
5958                 commit_reversible_command ();
5959         }
5960 }
5961
5962 void
5963 Editor::set_fade_in_shape (FadeShape shape)
5964 {
5965         RegionSelection rs = get_regions_from_selection_and_entered ();
5966
5967         if (rs.empty()) {
5968                 return;
5969         }
5970         bool in_command = false;
5971
5972         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5973                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5974
5975                 if (!tmp) {
5976                         continue;
5977                 }
5978
5979                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5980                 XMLNode &before = alist->get_state();
5981
5982                 tmp->audio_region()->set_fade_in_shape (shape);
5983
5984                 if (!in_command) {
5985                         begin_reversible_command (_("set fade in shape"));
5986                         in_command = true;
5987                 }
5988                 XMLNode &after = alist->get_state();
5989                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5990         }
5991
5992         if (in_command) {
5993                 commit_reversible_command ();
5994         }
5995 }
5996
5997 void
5998 Editor::set_fade_out_shape (FadeShape shape)
5999 {
6000         RegionSelection rs = get_regions_from_selection_and_entered ();
6001
6002         if (rs.empty()) {
6003                 return;
6004         }
6005         bool in_command = false;
6006
6007         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6008                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6009
6010                 if (!tmp) {
6011                         continue;
6012                 }
6013
6014                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6015                 XMLNode &before = alist->get_state();
6016
6017                 tmp->audio_region()->set_fade_out_shape (shape);
6018
6019                 if(!in_command) {
6020                         begin_reversible_command (_("set fade out shape"));
6021                         in_command = true;
6022                 }
6023                 XMLNode &after = alist->get_state();
6024                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6025         }
6026
6027         if (in_command) {
6028                 commit_reversible_command ();
6029         }
6030 }
6031
6032 void
6033 Editor::set_fade_in_active (bool yn)
6034 {
6035         RegionSelection rs = get_regions_from_selection_and_entered ();
6036
6037         if (rs.empty()) {
6038                 return;
6039         }
6040         bool in_command = false;
6041
6042         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6043                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6044
6045                 if (!tmp) {
6046                         continue;
6047                 }
6048
6049
6050                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6051
6052                 ar->clear_changes ();
6053                 ar->set_fade_in_active (yn);
6054
6055                 if (!in_command) {
6056                         begin_reversible_command (_("set fade in active"));
6057                         in_command = true;
6058                 }
6059                 _session->add_command (new StatefulDiffCommand (ar));
6060         }
6061
6062         if (in_command) {
6063                 commit_reversible_command ();
6064         }
6065 }
6066
6067 void
6068 Editor::set_fade_out_active (bool yn)
6069 {
6070         RegionSelection rs = get_regions_from_selection_and_entered ();
6071
6072         if (rs.empty()) {
6073                 return;
6074         }
6075         bool in_command = false;
6076
6077         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6078                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6079
6080                 if (!tmp) {
6081                         continue;
6082                 }
6083
6084                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6085
6086                 ar->clear_changes ();
6087                 ar->set_fade_out_active (yn);
6088
6089                 if (!in_command) {
6090                         begin_reversible_command (_("set fade out active"));
6091                         in_command = true;
6092                 }
6093                 _session->add_command(new StatefulDiffCommand (ar));
6094         }
6095
6096         if (in_command) {
6097                 commit_reversible_command ();
6098         }
6099 }
6100
6101 void
6102 Editor::toggle_region_fades (int dir)
6103 {
6104         if (_ignore_region_action) {
6105                 return;
6106         }
6107
6108         boost::shared_ptr<AudioRegion> ar;
6109         bool yn = false;
6110
6111         RegionSelection rs = get_regions_from_selection_and_entered ();
6112
6113         if (rs.empty()) {
6114                 return;
6115         }
6116
6117         RegionSelection::iterator i;
6118         for (i = rs.begin(); i != rs.end(); ++i) {
6119                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6120                         if (dir == -1) {
6121                                 yn = ar->fade_out_active ();
6122                         } else {
6123                                 yn = ar->fade_in_active ();
6124                         }
6125                         break;
6126                 }
6127         }
6128
6129         if (i == rs.end()) {
6130                 return;
6131         }
6132
6133         /* XXX should this undo-able? */
6134         bool in_command = false;
6135
6136         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6137                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6138                         continue;
6139                 }
6140                 ar->clear_changes ();
6141
6142                 if (dir == 1 || dir == 0) {
6143                         ar->set_fade_in_active (!yn);
6144                 }
6145
6146                 if (dir == -1 || dir == 0) {
6147                         ar->set_fade_out_active (!yn);
6148                 }
6149                 if (!in_command) {
6150                         begin_reversible_command (_("toggle fade active"));
6151                         in_command = true;
6152                 }
6153                 _session->add_command(new StatefulDiffCommand (ar));
6154         }
6155
6156         if (in_command) {
6157                 commit_reversible_command ();
6158         }
6159 }
6160
6161
6162 /** Update region fade visibility after its configuration has been changed */
6163 void
6164 Editor::update_region_fade_visibility ()
6165 {
6166         bool _fade_visibility = _session->config.get_show_region_fades ();
6167
6168         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6169                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6170                 if (v) {
6171                         if (_fade_visibility) {
6172                                 v->audio_view()->show_all_fades ();
6173                         } else {
6174                                 v->audio_view()->hide_all_fades ();
6175                         }
6176                 }
6177         }
6178 }
6179
6180 void
6181 Editor::set_edit_point ()
6182 {
6183         bool ignored;
6184         MusicFrame where (0, 0);
6185
6186         if (!mouse_frame (where.frame, ignored)) {
6187                 return;
6188         }
6189
6190         snap_to (where);
6191
6192         if (selection->markers.empty()) {
6193
6194                 mouse_add_new_marker (where.frame);
6195
6196         } else {
6197                 bool ignored;
6198
6199                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6200
6201                 if (loc) {
6202                         loc->move_to (where.frame, where.division);
6203                 }
6204         }
6205 }
6206
6207 void
6208 Editor::set_playhead_cursor ()
6209 {
6210         if (entered_marker) {
6211                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6212         } else {
6213                 MusicFrame where (0, 0);
6214                 bool ignored;
6215
6216                 if (!mouse_frame (where.frame, ignored)) {
6217                         return;
6218                 }
6219
6220                 snap_to (where);
6221
6222                 if (_session) {
6223                         _session->request_locate (where.frame, _session->transport_rolling());
6224                 }
6225         }
6226
6227 //not sure what this was for;  remove it for now.
6228 //      if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6229 //              cancel_time_selection();
6230 //      }
6231
6232 }
6233
6234 void
6235 Editor::split_region ()
6236 {
6237         if (_drags->active ()) {
6238                 return;
6239         }
6240
6241         //if a range is selected, separate it
6242         if ( !selection->time.empty()) {
6243                 separate_regions_between (selection->time);
6244                 return;
6245         }
6246
6247         //if no range was selected, try to find some regions to split
6248         if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6249
6250                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6251                 const framepos_t pos = get_preferred_edit_position();
6252                 const int32_t division = get_grid_music_divisions (0);
6253                 MusicFrame where (pos, division);
6254
6255                 if (rs.empty()) {
6256                         return;
6257                 }
6258
6259                 split_regions_at (where, rs);
6260
6261         }
6262 }
6263
6264 void
6265 Editor::select_next_route()
6266 {
6267         if (selection->tracks.empty()) {
6268                 selection->set (track_views.front());
6269                 return;
6270         }
6271
6272         TimeAxisView* current = selection->tracks.front();
6273
6274         RouteUI *rui;
6275         do {
6276                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6277
6278                         if (*i == current) {
6279                                 ++i;
6280                                 if (i != track_views.end()) {
6281                                         current = (*i);
6282                                 } else {
6283                                         current = (*(track_views.begin()));
6284                                         //selection->set (*(track_views.begin()));
6285                                 }
6286                                 break;
6287                         }
6288                 }
6289
6290                 rui = dynamic_cast<RouteUI *>(current);
6291
6292         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6293
6294         selection->set (current);
6295
6296         ensure_time_axis_view_is_visible (*current, false);
6297 }
6298
6299 void
6300 Editor::select_prev_route()
6301 {
6302         if (selection->tracks.empty()) {
6303                 selection->set (track_views.front());
6304                 return;
6305         }
6306
6307         TimeAxisView* current = selection->tracks.front();
6308
6309         RouteUI *rui;
6310         do {
6311                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6312
6313                         if (*i == current) {
6314                                 ++i;
6315                                 if (i != track_views.rend()) {
6316                                         current = (*i);
6317                                 } else {
6318                                         current = *(track_views.rbegin());
6319                                 }
6320                                 break;
6321                         }
6322                 }
6323                 rui = dynamic_cast<RouteUI *>(current);
6324
6325         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6326
6327         selection->set (current);
6328
6329         ensure_time_axis_view_is_visible (*current, false);
6330 }
6331
6332 void
6333 Editor::set_loop_from_selection (bool play)
6334 {
6335         if (_session == 0) {
6336                 return;
6337         }
6338
6339         framepos_t start, end;
6340         if (!get_selection_extents ( start, end))
6341                 return;
6342
6343         set_loop_range (start, end,  _("set loop range from selection"));
6344
6345         if (play) {
6346                 _session->request_play_loop (true, true);
6347         }
6348 }
6349
6350 void
6351 Editor::set_loop_from_region (bool play)
6352 {
6353         framepos_t start, end;
6354         if (!get_selection_extents ( start, end))
6355                 return;
6356
6357         set_loop_range (start, end, _("set loop range from region"));
6358
6359         if (play) {
6360                 _session->request_locate (start, true);
6361                 _session->request_play_loop (true);
6362         }
6363 }
6364
6365 void
6366 Editor::set_punch_from_selection ()
6367 {
6368         if (_session == 0) {
6369                 return;
6370         }
6371
6372         framepos_t start, end;
6373         if (!get_selection_extents ( start, end))
6374                 return;
6375
6376         set_punch_range (start, end,  _("set punch range from selection"));
6377 }
6378
6379 void
6380 Editor::set_auto_punch_range ()
6381 {
6382         // auto punch in/out button from a single button
6383         // If Punch In is unset, set punch range from playhead to end, enable punch in
6384         // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6385         //   rewound beyond the Punch In marker, in which case that marker will be moved back
6386         //   to the current playhead position.
6387         // If punch out is set, it clears the punch range and Punch In/Out buttons
6388
6389         if (_session == 0) {
6390                 return;
6391         }
6392
6393         Location* tpl = transport_punch_location();
6394         framepos_t now = playhead_cursor->current_frame();
6395         framepos_t begin = now;
6396         framepos_t end = _session->current_end_frame();
6397
6398         if (!_session->config.get_punch_in()) {
6399                 // First Press - set punch in and create range from here to eternity
6400                 set_punch_range (begin, end, _("Auto Punch In"));
6401                 _session->config.set_punch_in(true);
6402         } else if (tpl && !_session->config.get_punch_out()) {
6403                 // Second press - update end range marker and set punch_out
6404                 if (now < tpl->start()) {
6405                         // playhead has been rewound - move start back  and pretend nothing happened
6406                         begin = now;
6407                         set_punch_range (begin, end, _("Auto Punch In/Out"));
6408                 } else {
6409                         // normal case for 2nd press - set the punch out
6410                         end = playhead_cursor->current_frame ();
6411                         set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6412                         _session->config.set_punch_out(true);
6413                 }
6414         } else  {
6415                 if (_session->config.get_punch_out()) {
6416                         _session->config.set_punch_out(false);
6417                 }
6418
6419                 if (_session->config.get_punch_in()) {
6420                         _session->config.set_punch_in(false);
6421                 }
6422
6423                 if (tpl)
6424                 {
6425                         // third press - unset punch in/out and remove range
6426                         _session->locations()->remove(tpl);
6427                 }
6428         }
6429
6430 }
6431
6432 void
6433 Editor::set_session_extents_from_selection ()
6434 {
6435         if (_session == 0) {
6436                 return;
6437         }
6438
6439         framepos_t start, end;
6440         if (!get_selection_extents ( start, end))
6441                 return;
6442
6443         Location* loc;
6444         if ((loc = _session->locations()->session_range_location()) == 0) {
6445                 _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6446         } else {
6447                 XMLNode &before = loc->get_state();
6448
6449                 _session->set_session_extents (start, end);
6450
6451                 XMLNode &after = loc->get_state();
6452
6453                 begin_reversible_command (_("set session start/end from selection"));
6454
6455                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6456
6457                 commit_reversible_command ();
6458         }
6459
6460         _session->set_end_is_free (false);
6461 }
6462
6463 void
6464 Editor::set_punch_start_from_edit_point ()
6465 {
6466         if (_session) {
6467
6468                 MusicFrame start (0, 0);
6469                 framepos_t end = max_framepos;
6470
6471                 //use the existing punch end, if any
6472                 Location* tpl = transport_punch_location();
6473                 if (tpl) {
6474                         end = tpl->end();
6475                 }
6476
6477                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6478                         start.frame = _session->audible_frame();
6479                 } else {
6480                         start.frame = get_preferred_edit_position();
6481                 }
6482
6483                 //snap the selection start/end
6484                 snap_to(start);
6485
6486                 //if there's not already a sensible selection endpoint, go "forever"
6487                 if (start.frame > end ) {
6488                         end = max_framepos;
6489                 }
6490
6491                 set_punch_range (start.frame, end, _("set punch start from EP"));
6492         }
6493
6494 }
6495
6496 void
6497 Editor::set_punch_end_from_edit_point ()
6498 {
6499         if (_session) {
6500
6501                 framepos_t start = 0;
6502                 MusicFrame end (max_framepos, 0);
6503
6504                 //use the existing punch start, if any
6505                 Location* tpl = transport_punch_location();
6506                 if (tpl) {
6507                         start = tpl->start();
6508                 }
6509
6510                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6511                         end.frame = _session->audible_frame();
6512                 } else {
6513                         end.frame = get_preferred_edit_position();
6514                 }
6515
6516                 //snap the selection start/end
6517                 snap_to (end);
6518
6519                 set_punch_range (start, end.frame, _("set punch end from EP"));
6520
6521         }
6522 }
6523
6524 void
6525 Editor::set_loop_start_from_edit_point ()
6526 {
6527         if (_session) {
6528
6529                 MusicFrame start (0, 0);
6530                 framepos_t end = max_framepos;
6531
6532                 //use the existing loop end, if any
6533                 Location* tpl = transport_loop_location();
6534                 if (tpl) {
6535                         end = tpl->end();
6536                 }
6537
6538                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6539                         start.frame = _session->audible_frame();
6540                 } else {
6541                         start.frame = get_preferred_edit_position();
6542                 }
6543
6544                 //snap the selection start/end
6545                 snap_to (start);
6546
6547                 //if there's not already a sensible selection endpoint, go "forever"
6548                 if (start.frame > end ) {
6549                         end = max_framepos;
6550                 }
6551
6552                 set_loop_range (start.frame, end, _("set loop start from EP"));
6553         }
6554
6555 }
6556
6557 void
6558 Editor::set_loop_end_from_edit_point ()
6559 {
6560         if (_session) {
6561
6562                 framepos_t start = 0;
6563                 MusicFrame end (max_framepos, 0);
6564
6565                 //use the existing loop start, if any
6566                 Location* tpl = transport_loop_location();
6567                 if (tpl) {
6568                         start = tpl->start();
6569                 }
6570
6571                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6572                         end.frame = _session->audible_frame();
6573                 } else {
6574                         end.frame = get_preferred_edit_position();
6575                 }
6576
6577                 //snap the selection start/end
6578                 snap_to(end);
6579
6580                 set_loop_range (start, end.frame, _("set loop end from EP"));
6581         }
6582 }
6583
6584 void
6585 Editor::set_punch_from_region ()
6586 {
6587         framepos_t start, end;
6588         if (!get_selection_extents ( start, end))
6589                 return;
6590
6591         set_punch_range (start, end, _("set punch range from region"));
6592 }
6593
6594 void
6595 Editor::pitch_shift_region ()
6596 {
6597         RegionSelection rs = get_regions_from_selection_and_entered ();
6598
6599         RegionSelection audio_rs;
6600         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6601                 if (dynamic_cast<AudioRegionView*> (*i)) {
6602                         audio_rs.push_back (*i);
6603                 }
6604         }
6605
6606         if (audio_rs.empty()) {
6607                 return;
6608         }
6609
6610         pitch_shift (audio_rs, 1.2);
6611 }
6612
6613 void
6614 Editor::set_tempo_from_region ()
6615 {
6616         RegionSelection rs = get_regions_from_selection_and_entered ();
6617
6618         if (!_session || rs.empty()) {
6619                 return;
6620         }
6621
6622         RegionView* rv = rs.front();
6623
6624         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6625 }
6626
6627 void
6628 Editor::use_range_as_bar ()
6629 {
6630         framepos_t start, end;
6631         if (get_edit_op_range (start, end)) {
6632                 define_one_bar (start, end);
6633         }
6634 }
6635
6636 void
6637 Editor::define_one_bar (framepos_t start, framepos_t end)
6638 {
6639         framepos_t length = end - start;
6640
6641         const Meter& m (_session->tempo_map().meter_at_frame (start));
6642
6643         /* length = 1 bar */
6644
6645         /* We're going to deliver a constant tempo here,
6646            so we can use frames per beat to determine length.
6647            now we want frames per beat.
6648            we have frames per bar, and beats per bar, so ...
6649         */
6650
6651         /* XXXX METER MATH */
6652
6653         double frames_per_beat = length / m.divisions_per_bar();
6654
6655         /* beats per minute = */
6656
6657         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6658
6659         /* now decide whether to:
6660
6661             (a) set global tempo
6662             (b) add a new tempo marker
6663
6664         */
6665
6666         const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6667
6668         bool do_global = false;
6669
6670         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6671
6672                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6673                    at the start, or create a new marker
6674                 */
6675
6676                 vector<string> options;
6677                 options.push_back (_("Cancel"));
6678                 options.push_back (_("Add new marker"));
6679                 options.push_back (_("Set global tempo"));
6680
6681                 Choice c (
6682                         _("Define one bar"),
6683                         _("Do you want to set the global tempo or add a new tempo marker?"),
6684                         options
6685                         );
6686
6687                 c.set_default_response (2);
6688
6689                 switch (c.run()) {
6690                 case 0:
6691                         return;
6692
6693                 case 2:
6694                         do_global = true;
6695                         break;
6696
6697                 default:
6698                         do_global = false;
6699                 }
6700
6701         } else {
6702
6703                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6704                    if the marker is at the region starter, change it, otherwise add
6705                    a new tempo marker
6706                 */
6707         }
6708
6709         begin_reversible_command (_("set tempo from region"));
6710         XMLNode& before (_session->tempo_map().get_state());
6711
6712         if (do_global) {
6713                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6714         } else if (t.frame() == start) {
6715                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6716         } else {
6717                 const Tempo tempo (beats_per_minute, t.note_type());
6718                 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6719         }
6720
6721         XMLNode& after (_session->tempo_map().get_state());
6722
6723         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6724         commit_reversible_command ();
6725 }
6726
6727 void
6728 Editor::split_region_at_transients ()
6729 {
6730         AnalysisFeatureList positions;
6731
6732         RegionSelection rs = get_regions_from_selection_and_entered ();
6733
6734         if (!_session || rs.empty()) {
6735                 return;
6736         }
6737
6738         begin_reversible_command (_("split regions"));
6739
6740         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6741
6742                 RegionSelection::iterator tmp;
6743
6744                 tmp = i;
6745                 ++tmp;
6746
6747                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6748
6749                 if (ar) {
6750                         ar->transients (positions);
6751                         split_region_at_points ((*i)->region(), positions, true);
6752                         positions.clear ();
6753                 }
6754
6755                 i = tmp;
6756         }
6757
6758         commit_reversible_command ();
6759
6760 }
6761
6762 void
6763 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6764 {
6765         bool use_rhythmic_rodent = false;
6766
6767         boost::shared_ptr<Playlist> pl = r->playlist();
6768
6769         list<boost::shared_ptr<Region> > new_regions;
6770
6771         if (!pl) {
6772                 return;
6773         }
6774
6775         if (positions.empty()) {
6776                 return;
6777         }
6778
6779         if (positions.size() > 20 && can_ferret) {
6780                 std::string msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), r->name(), positions.size() + 1);
6781                 MessageDialog msg (msgstr,
6782                                    false,
6783                                    Gtk::MESSAGE_INFO,
6784                                    Gtk::BUTTONS_OK_CANCEL);
6785
6786                 if (can_ferret) {
6787                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6788                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6789                 } else {
6790                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6791                 }
6792
6793                 msg.set_title (_("Excessive split?"));
6794                 msg.present ();
6795
6796                 int response = msg.run();
6797                 msg.hide ();
6798
6799                 switch (response) {
6800                 case RESPONSE_OK:
6801                         break;
6802                 case RESPONSE_APPLY:
6803                         use_rhythmic_rodent = true;
6804                         break;
6805                 default:
6806                         return;
6807                 }
6808         }
6809
6810         if (use_rhythmic_rodent) {
6811                 show_rhythm_ferret ();
6812                 return;
6813         }
6814
6815         AnalysisFeatureList::const_iterator x;
6816
6817         pl->clear_changes ();
6818         pl->clear_owned_changes ();
6819
6820         x = positions.begin();
6821
6822         if (x == positions.end()) {
6823                 return;
6824         }
6825
6826         pl->freeze ();
6827         pl->remove_region (r);
6828
6829         framepos_t pos = 0;
6830
6831         framepos_t rstart = r->first_frame ();
6832         framepos_t rend = r->last_frame ();
6833
6834         while (x != positions.end()) {
6835
6836                 /* deal with positons that are out of scope of present region bounds */
6837                 if (*x <= rstart || *x > rend) {
6838                         ++x;
6839                         continue;
6840                 }
6841
6842                 /* file start = original start + how far we from the initial position ?  */
6843
6844                 framepos_t file_start = r->start() + pos;
6845
6846                 /* length = next position - current position */
6847
6848                 framepos_t len = (*x) - pos - rstart;
6849
6850                 /* XXX we do we really want to allow even single-sample regions?
6851                  * shouldn't we have some kind of lower limit on region size?
6852                  */
6853
6854                 if (len <= 0) {
6855                         break;
6856                 }
6857
6858                 string new_name;
6859
6860                 if (RegionFactory::region_name (new_name, r->name())) {
6861                         break;
6862                 }
6863
6864                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6865
6866                 PropertyList plist;
6867
6868                 plist.add (ARDOUR::Properties::start, file_start);
6869                 plist.add (ARDOUR::Properties::length, len);
6870                 plist.add (ARDOUR::Properties::name, new_name);
6871                 plist.add (ARDOUR::Properties::layer, 0);
6872                 // TODO set transients_offset
6873
6874                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6875                 /* because we set annouce to false, manually add the new region to the
6876                  * RegionFactory map
6877                  */
6878                 RegionFactory::map_add (nr);
6879
6880                 pl->add_region (nr, rstart + pos);
6881
6882                 if (select_new) {
6883                         new_regions.push_front(nr);
6884                 }
6885
6886                 pos += len;
6887                 ++x;
6888         }
6889
6890         string new_name;
6891
6892         RegionFactory::region_name (new_name, r->name());
6893
6894         /* Add the final region */
6895         PropertyList plist;
6896
6897         plist.add (ARDOUR::Properties::start, r->start() + pos);
6898         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6899         plist.add (ARDOUR::Properties::name, new_name);
6900         plist.add (ARDOUR::Properties::layer, 0);
6901
6902         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6903         /* because we set annouce to false, manually add the new region to the
6904            RegionFactory map
6905         */
6906         RegionFactory::map_add (nr);
6907         pl->add_region (nr, r->position() + pos);
6908
6909         if (select_new) {
6910                 new_regions.push_front(nr);
6911         }
6912
6913         pl->thaw ();
6914
6915         /* We might have removed regions, which alters other regions' layering_index,
6916            so we need to do a recursive diff here.
6917         */
6918         vector<Command*> cmds;
6919         pl->rdiff (cmds);
6920         _session->add_commands (cmds);
6921
6922         _session->add_command (new StatefulDiffCommand (pl));
6923
6924         if (select_new) {
6925
6926                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6927                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6928                 }
6929         }
6930 }
6931
6932 void
6933 Editor::place_transient()
6934 {
6935         if (!_session) {
6936                 return;
6937         }
6938
6939         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6940
6941         if (rs.empty()) {
6942                 return;
6943         }
6944
6945         framepos_t where = get_preferred_edit_position();
6946
6947         begin_reversible_command (_("place transient"));
6948
6949         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6950                 (*r)->region()->add_transient(where);
6951         }
6952
6953         commit_reversible_command ();
6954 }
6955
6956 void
6957 Editor::remove_transient(ArdourCanvas::Item* item)
6958 {
6959         if (!_session) {
6960                 return;
6961         }
6962
6963         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6964         assert (_line);
6965
6966         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6967         _arv->remove_transient (*(float*) _line->get_data ("position"));
6968 }
6969
6970 void
6971 Editor::snap_regions_to_grid ()
6972 {
6973         list <boost::shared_ptr<Playlist > > used_playlists;
6974
6975         RegionSelection rs = get_regions_from_selection_and_entered ();
6976
6977         if (!_session || rs.empty()) {
6978                 return;
6979         }
6980
6981         begin_reversible_command (_("snap regions to grid"));
6982
6983         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6984
6985                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6986
6987                 if (!pl->frozen()) {
6988                         /* we haven't seen this playlist before */
6989
6990                         /* remember used playlists so we can thaw them later */
6991                         used_playlists.push_back(pl);
6992                         pl->freeze();
6993                 }
6994
6995                 MusicFrame start ((*r)->region()->first_frame (), 0);
6996                 snap_to (start);
6997                 (*r)->region()->set_position (start.frame, start.division);
6998         }
6999
7000         while (used_playlists.size() > 0) {
7001                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7002                 (*i)->thaw();
7003                 used_playlists.pop_front();
7004         }
7005
7006         commit_reversible_command ();
7007 }
7008
7009 void
7010 Editor::close_region_gaps ()
7011 {
7012         list <boost::shared_ptr<Playlist > > used_playlists;
7013
7014         RegionSelection rs = get_regions_from_selection_and_entered ();
7015
7016         if (!_session || rs.empty()) {
7017                 return;
7018         }
7019
7020         Dialog dialog (_("Close Region Gaps"));
7021
7022         Table table (2, 3);
7023         table.set_spacings (12);
7024         table.set_border_width (12);
7025         Label* l = manage (left_aligned_label (_("Crossfade length")));
7026         table.attach (*l, 0, 1, 0, 1);
7027
7028         SpinButton spin_crossfade (1, 0);
7029         spin_crossfade.set_range (0, 15);
7030         spin_crossfade.set_increments (1, 1);
7031         spin_crossfade.set_value (5);
7032         table.attach (spin_crossfade, 1, 2, 0, 1);
7033
7034         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7035
7036         l = manage (left_aligned_label (_("Pull-back length")));
7037         table.attach (*l, 0, 1, 1, 2);
7038
7039         SpinButton spin_pullback (1, 0);
7040         spin_pullback.set_range (0, 100);
7041         spin_pullback.set_increments (1, 1);
7042         spin_pullback.set_value(30);
7043         table.attach (spin_pullback, 1, 2, 1, 2);
7044
7045         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7046
7047         dialog.get_vbox()->pack_start (table);
7048         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7049         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7050         dialog.show_all ();
7051
7052         if (dialog.run () == RESPONSE_CANCEL) {
7053                 return;
7054         }
7055
7056         framepos_t crossfade_len = spin_crossfade.get_value();
7057         framepos_t pull_back_frames = spin_pullback.get_value();
7058
7059         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7060         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7061
7062         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7063
7064         begin_reversible_command (_("close region gaps"));
7065
7066         int idx = 0;
7067         boost::shared_ptr<Region> last_region;
7068
7069         rs.sort_by_position_and_track();
7070
7071         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7072
7073                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7074
7075                 if (!pl->frozen()) {
7076                         /* we haven't seen this playlist before */
7077
7078                         /* remember used playlists so we can thaw them later */
7079                         used_playlists.push_back(pl);
7080                         pl->freeze();
7081                 }
7082
7083                 framepos_t position = (*r)->region()->position();
7084
7085                 if (idx == 0 || position < last_region->position()){
7086                         last_region = (*r)->region();
7087                         idx++;
7088                         continue;
7089                 }
7090
7091                 (*r)->region()->clear_changes ();
7092                 (*r)->region()->trim_front( (position - pull_back_frames));
7093                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7094
7095                 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7096                 last_region = (*r)->region();
7097
7098                 idx++;
7099         }
7100
7101         while (used_playlists.size() > 0) {
7102                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7103                 (*i)->thaw();
7104                 used_playlists.pop_front();
7105         }
7106
7107         commit_reversible_command ();
7108 }
7109
7110 void
7111 Editor::tab_to_transient (bool forward)
7112 {
7113         AnalysisFeatureList positions;
7114
7115         RegionSelection rs = get_regions_from_selection_and_entered ();
7116
7117         if (!_session) {
7118                 return;
7119         }
7120
7121         framepos_t pos = _session->audible_frame ();
7122
7123         if (!selection->tracks.empty()) {
7124
7125                 /* don't waste time searching for transients in duplicate playlists.
7126                  */
7127
7128                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7129
7130                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7131
7132                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7133
7134                         if (rtv) {
7135                                 boost::shared_ptr<Track> tr = rtv->track();
7136                                 if (tr) {
7137                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
7138                                         if (pl) {
7139                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7140
7141                                                 if (result >= 0) {
7142                                                         positions.push_back (result);
7143                                                 }
7144                                         }
7145                                 }
7146                         }
7147                 }
7148
7149         } else {
7150
7151                 if (rs.empty()) {
7152                         return;
7153                 }
7154
7155                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7156                         (*r)->region()->get_transients (positions);
7157                 }
7158         }
7159
7160         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7161
7162         if (forward) {
7163                 AnalysisFeatureList::iterator x;
7164
7165                 for (x = positions.begin(); x != positions.end(); ++x) {
7166                         if ((*x) > pos) {
7167                                 break;
7168                         }
7169                 }
7170
7171                 if (x != positions.end ()) {
7172                         _session->request_locate (*x);
7173                 }
7174
7175         } else {
7176                 AnalysisFeatureList::reverse_iterator x;
7177
7178                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7179                         if ((*x) < pos) {
7180                                 break;
7181                         }
7182                 }
7183
7184                 if (x != positions.rend ()) {
7185                         _session->request_locate (*x);
7186                 }
7187         }
7188 }
7189
7190 void
7191 Editor::playhead_forward_to_grid ()
7192 {
7193         if (!_session) {
7194                 return;
7195         }
7196
7197         MusicFrame pos (playhead_cursor->current_frame (), 0);
7198
7199         if (pos.frame < max_framepos - 1) {
7200                 pos.frame += 2;
7201                 snap_to_internal (pos, RoundUpAlways, false);
7202                 _session->request_locate (pos.frame);
7203         }
7204 }
7205
7206
7207 void
7208 Editor::playhead_backward_to_grid ()
7209 {
7210         if (!_session) {
7211                 return;
7212         }
7213
7214         MusicFrame pos  (playhead_cursor->current_frame (), 0);
7215
7216         if (pos.frame > 2) {
7217                 pos.frame -= 2;
7218                 snap_to_internal (pos, RoundDownAlways, false);
7219                 _session->request_locate (pos.frame);
7220         }
7221 }
7222
7223 void
7224 Editor::set_track_height (Height h)
7225 {
7226         TrackSelection& ts (selection->tracks);
7227
7228         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7229                 (*x)->set_height_enum (h);
7230         }
7231 }
7232
7233 void
7234 Editor::toggle_tracks_active ()
7235 {
7236         TrackSelection& ts (selection->tracks);
7237         bool first = true;
7238         bool target = false;
7239
7240         if (ts.empty()) {
7241                 return;
7242         }
7243
7244         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7245                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7246
7247                 if (rtv) {
7248                         if (first) {
7249                                 target = !rtv->_route->active();
7250                                 first = false;
7251                         }
7252                         rtv->_route->set_active (target, this);
7253                 }
7254         }
7255 }
7256
7257 void
7258 Editor::remove_tracks ()
7259 {
7260         /* this will delete GUI objects that may be the subject of an event
7261            handler in which this method is called. Defer actual deletion to the
7262            next idle callback, when all event handling is finished.
7263         */
7264         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7265 }
7266
7267 bool
7268 Editor::idle_remove_tracks ()
7269 {
7270         Session::StateProtector sp (_session);
7271         _remove_tracks ();
7272         return false; /* do not call again */
7273 }
7274
7275 void
7276 Editor::_remove_tracks ()
7277 {
7278         TrackSelection& ts (selection->tracks);
7279
7280         if (ts.empty()) {
7281                 return;
7282         }
7283
7284         vector<string> choices;
7285         string prompt;
7286         int ntracks = 0;
7287         int nbusses = 0;
7288         const char* trackstr;
7289         const char* busstr;
7290         vector<boost::shared_ptr<Route> > routes;
7291         bool special_bus = false;
7292
7293         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7294                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7295                 if (!rtv) {
7296                         continue;
7297                 }
7298                 if (rtv->is_track()) {
7299                         ntracks++;
7300                 } else {
7301                         nbusses++;
7302                 }
7303                 routes.push_back (rtv->_route);
7304
7305                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7306                         special_bus = true;
7307                 }
7308         }
7309
7310         if (special_bus && !Config->get_allow_special_bus_removal()) {
7311                 MessageDialog msg (_("That would be bad news ...."),
7312                                    false,
7313                                    Gtk::MESSAGE_INFO,
7314                                    Gtk::BUTTONS_OK);
7315                 msg.set_secondary_text (string_compose (_(
7316                                                                 "Removing the master or monitor bus is such a bad idea\n\
7317 that %1 is not going to allow it.\n\
7318 \n\
7319 If you really want to do this sort of thing\n\
7320 edit your ardour.rc file to set the\n\
7321 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7322
7323                 msg.present ();
7324                 msg.run ();
7325                 return;
7326         }
7327
7328         if (ntracks + nbusses == 0) {
7329                 return;
7330         }
7331
7332         trackstr = P_("track", "tracks", ntracks);
7333         busstr = P_("bus", "busses", nbusses);
7334
7335         if (ntracks) {
7336                 if (nbusses) {
7337                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\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, nbusses, busstr);
7341                 } else {
7342                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7343                                                     "(You may also lose the playlists associated with the %2)\n\n"
7344                                                     "This action cannot be undone, and the session file will be overwritten!"),
7345                                                   ntracks, trackstr);
7346                 }
7347         } else if (nbusses) {
7348                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7349                                             "This action cannot be undone, and the session file will be overwritten"),
7350                                           nbusses, busstr);
7351         }
7352
7353         choices.push_back (_("No, do nothing."));
7354         if (ntracks + nbusses > 1) {
7355                 choices.push_back (_("Yes, remove them."));
7356         } else {
7357                 choices.push_back (_("Yes, remove it."));
7358         }
7359
7360         string title;
7361         if (ntracks) {
7362                 title = string_compose (_("Remove %1"), trackstr);
7363         } else {
7364                 title = string_compose (_("Remove %1"), busstr);
7365         }
7366
7367         Choice prompter (title, prompt, choices);
7368
7369         if (prompter.run () != 1) {
7370                 return;
7371         }
7372
7373         if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7374                 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7375                  * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7376                  * likely because deletion requires selection) this will call
7377                  * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7378                  * It's likewise likely that the route that has just been displayed in the
7379                  * Editor-Mixer will be next in line for deletion.
7380                  *
7381                  * So simply switch to the master-bus (if present)
7382                  */
7383                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7384                         if ((*i)->stripable ()->is_master ()) {
7385                                 set_selected_mixer_strip (*(*i));
7386                                 break;
7387                         }
7388                 }
7389         }
7390
7391         {
7392                 PresentationInfo::ChangeSuspender cs;
7393                 DisplaySuspender ds;
7394
7395                 boost::shared_ptr<RouteList> rl (new RouteList);
7396                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7397                         rl->push_back (*x);
7398                 }
7399                 _session->remove_routes (rl);
7400         }
7401         /* TrackSelection and RouteList leave scope,
7402          * destructors are called,
7403          * diskstream drops references, save_state is called (again for every track)
7404          */
7405 }
7406
7407 void
7408 Editor::do_insert_time ()
7409 {
7410         if (selection->tracks.empty()) {
7411                 return;
7412         }
7413
7414         InsertRemoveTimeDialog d (*this);
7415         int response = d.run ();
7416
7417         if (response != RESPONSE_OK) {
7418                 return;
7419         }
7420
7421         if (d.distance() == 0) {
7422                 return;
7423         }
7424
7425         insert_time (
7426                 d.position(),
7427                 d.distance(),
7428                 d.intersected_region_action (),
7429                 d.all_playlists(),
7430                 d.move_glued(),
7431                 d.move_markers(),
7432                 d.move_glued_markers(),
7433                 d.move_locked_markers(),
7434                 d.move_tempos()
7435                 );
7436 }
7437
7438 void
7439 Editor::insert_time (
7440         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7441         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7442         )
7443 {
7444
7445         if (Config->get_edit_mode() == Lock) {
7446                 return;
7447         }
7448         bool in_command = false;
7449
7450         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7451
7452         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7453
7454                 /* regions */
7455
7456                 /* don't operate on any playlist more than once, which could
7457                  * happen if "all playlists" is enabled, but there is more
7458                  * than 1 track using playlists "from" a given track.
7459                  */
7460
7461                 set<boost::shared_ptr<Playlist> > pl;
7462
7463                 if (all_playlists) {
7464                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7465                         if (rtav && rtav->track ()) {
7466                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7467                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7468                                         pl.insert (*p);
7469                                 }
7470                         }
7471                 } else {
7472                         if ((*x)->playlist ()) {
7473                                 pl.insert ((*x)->playlist ());
7474                         }
7475                 }
7476
7477                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7478
7479                         (*i)->clear_changes ();
7480                         (*i)->clear_owned_changes ();
7481
7482                         if (opt == SplitIntersected) {
7483                                 /* non musical split */
7484                                 (*i)->split (MusicFrame (pos, 0));
7485                         }
7486
7487                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7488
7489                         if (!in_command) {
7490                                 begin_reversible_command (_("insert time"));
7491                                 in_command = true;
7492                         }
7493                         vector<Command*> cmds;
7494                         (*i)->rdiff (cmds);
7495                         _session->add_commands (cmds);
7496
7497                         _session->add_command (new StatefulDiffCommand (*i));
7498                 }
7499
7500                 /* automation */
7501                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7502                 if (rtav) {
7503                         if (!in_command) {
7504                                 begin_reversible_command (_("insert time"));
7505                                 in_command = true;
7506                         }
7507                         rtav->route ()->shift (pos, frames);
7508                 }
7509         }
7510
7511         /* markers */
7512         if (markers_too) {
7513                 bool moved = false;
7514                 const int32_t divisions = get_grid_music_divisions (0);
7515                 XMLNode& before (_session->locations()->get_state());
7516                 Locations::LocationList copy (_session->locations()->list());
7517
7518                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7519
7520                         Locations::LocationList::const_iterator tmp;
7521
7522                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7523                                 bool const was_locked = (*i)->locked ();
7524                                 if (locked_markers_too) {
7525                                         (*i)->unlock ();
7526                                 }
7527
7528                                 if ((*i)->start() >= pos) {
7529                                         // move end first, in case we're moving by more than the length of the range
7530                                         if (!(*i)->is_mark()) {
7531                                                 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7532                                         }
7533                                         (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7534                                         moved = true;
7535                                 }
7536
7537                                 if (was_locked) {
7538                                         (*i)->lock ();
7539                                 }
7540                         }
7541                 }
7542
7543                 if (moved) {
7544                         if (!in_command) {
7545                                 begin_reversible_command (_("insert time"));
7546                                 in_command = true;
7547                         }
7548                         XMLNode& after (_session->locations()->get_state());
7549                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7550                 }
7551         }
7552
7553         if (tempo_too) {
7554                 if (!in_command) {
7555                         begin_reversible_command (_("insert time"));
7556                         in_command = true;
7557                 }
7558                 XMLNode& before (_session->tempo_map().get_state());
7559                 _session->tempo_map().insert_time (pos, frames);
7560                 XMLNode& after (_session->tempo_map().get_state());
7561                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7562         }
7563
7564         if (in_command) {
7565                 commit_reversible_command ();
7566         }
7567 }
7568
7569 void
7570 Editor::do_remove_time ()
7571 {
7572         if (selection->tracks.empty()) {
7573                 return;
7574         }
7575
7576         InsertRemoveTimeDialog d (*this, true);
7577
7578         int response = d.run ();
7579
7580         if (response != RESPONSE_OK) {
7581                 return;
7582         }
7583
7584         framecnt_t distance = d.distance();
7585
7586         if (distance == 0) {
7587                 return;
7588         }
7589
7590         remove_time (
7591                 d.position(),
7592                 distance,
7593                 SplitIntersected,
7594                 d.move_glued(),
7595                 d.move_markers(),
7596                 d.move_glued_markers(),
7597                 d.move_locked_markers(),
7598                 d.move_tempos()
7599         );
7600 }
7601
7602 void
7603 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7604                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7605 {
7606         if (Config->get_edit_mode() == Lock) {
7607                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7608                 return;
7609         }
7610         bool in_command = false;
7611
7612         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7613                 /* regions */
7614                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7615
7616                 if (pl) {
7617
7618                         XMLNode &before = pl->get_state();
7619
7620                         std::list<AudioRange> rl;
7621                         AudioRange ar(pos, pos+frames, 0);
7622                         rl.push_back(ar);
7623                         pl->cut (rl);
7624                         pl->shift (pos, -frames, true, ignore_music_glue);
7625
7626                         if (!in_command) {
7627                                 begin_reversible_command (_("remove time"));
7628                                 in_command = true;
7629                         }
7630                         XMLNode &after = pl->get_state();
7631
7632                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7633                 }
7634
7635                 /* automation */
7636                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7637                 if (rtav) {
7638                         if (!in_command) {
7639                                 begin_reversible_command (_("remove time"));
7640                                 in_command = true;
7641                         }
7642                         rtav->route ()->shift (pos, -frames);
7643                 }
7644         }
7645
7646         const int32_t divisions = get_grid_music_divisions (0);
7647         std::list<Location*> loc_kill_list;
7648
7649         /* markers */
7650         if (markers_too) {
7651                 bool moved = false;
7652                 XMLNode& before (_session->locations()->get_state());
7653                 Locations::LocationList copy (_session->locations()->list());
7654
7655                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7656                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7657
7658                                 bool const was_locked = (*i)->locked ();
7659                                 if (locked_markers_too) {
7660                                         (*i)->unlock ();
7661                                 }
7662
7663                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7664                                         if ((*i)->end() >= pos
7665                                         && (*i)->end() < pos+frames
7666                                         && (*i)->start() >= pos
7667                                         && (*i)->end() < pos+frames) {  // range is completely enclosed;  kill it
7668                                                 moved = true;
7669                                                 loc_kill_list.push_back(*i);
7670                                         } else {  // only start or end is included, try to do the right thing
7671                                                 // move start before moving end, to avoid trying to move the end to before the start
7672                                                 // if we're removing more time than the length of the range
7673                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7674                                                         // start is within cut
7675                                                         (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
7676                                                         moved = true;
7677                                                 } else if ((*i)->start() >= pos+frames) {
7678                                                         // start (and thus entire range) lies beyond end of cut
7679                                                         (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7680                                                         moved = true;
7681                                                 }
7682                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7683                                                         // end is inside cut
7684                                                         (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
7685                                                         moved = true;
7686                                                 } else if ((*i)->end() >= pos+frames) {
7687                                                         // end is beyond end of cut
7688                                                         (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7689                                                         moved = true;
7690                                                 }
7691
7692                                         }
7693                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7694                                         loc_kill_list.push_back(*i);
7695                                         moved = true;
7696                                 } else if ((*i)->start() >= pos) {
7697                                         (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7698                                         moved = true;
7699                                 }
7700
7701                                 if (was_locked) {
7702                                         (*i)->lock ();
7703                                 }
7704                         }
7705                 }
7706
7707                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7708                         _session->locations()->remove( *i );
7709                 }
7710
7711                 if (moved) {
7712                         if (!in_command) {
7713                                 begin_reversible_command (_("remove time"));
7714                                 in_command = true;
7715                         }
7716                         XMLNode& after (_session->locations()->get_state());
7717                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7718                 }
7719         }
7720
7721         if (tempo_too) {
7722                 XMLNode& before (_session->tempo_map().get_state());
7723
7724                 if (_session->tempo_map().remove_time (pos, frames) ) {
7725                         if (!in_command) {
7726                                 begin_reversible_command (_("remove time"));
7727                                 in_command = true;
7728                         }
7729                         XMLNode& after (_session->tempo_map().get_state());
7730                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7731                 }
7732         }
7733
7734         if (in_command) {
7735                 commit_reversible_command ();
7736         }
7737 }
7738
7739 void
7740 Editor::fit_selection ()
7741 {
7742         if (!selection->tracks.empty()) {
7743                 fit_tracks (selection->tracks);
7744         } else {
7745                 TrackViewList tvl;
7746
7747                 /* no selected tracks - use tracks with selected regions */
7748
7749                 if (!selection->regions.empty()) {
7750                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7751                                 tvl.push_back (&(*r)->get_time_axis_view ());
7752                         }
7753
7754                         if (!tvl.empty()) {
7755                                 fit_tracks (tvl);
7756                         }
7757                 } else if (internal_editing()) {
7758                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7759                            the entered track
7760                         */
7761                         if (entered_track) {
7762                                 tvl.push_back (entered_track);
7763                                 fit_tracks (tvl);
7764                         }
7765                 }
7766         }
7767
7768 }
7769
7770 void
7771 Editor::fit_tracks (TrackViewList & tracks)
7772 {
7773         if (tracks.empty()) {
7774                 return;
7775         }
7776
7777         uint32_t child_heights = 0;
7778         int visible_tracks = 0;
7779
7780         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7781
7782                 if (!(*t)->marked_for_display()) {
7783                         continue;
7784                 }
7785
7786                 child_heights += (*t)->effective_height() - (*t)->current_height();
7787                 ++visible_tracks;
7788         }
7789
7790         /* compute the per-track height from:
7791
7792            total canvas visible height -
7793                  height that will be taken by visible children of selected
7794                  tracks - height of the ruler/hscroll area
7795         */
7796         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7797         double first_y_pos = DBL_MAX;
7798
7799         if (h < TimeAxisView::preset_height (HeightSmall)) {
7800                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7801                 /* too small to be displayed */
7802                 return;
7803         }
7804
7805         undo_visual_stack.push_back (current_visual_state (true));
7806         PBD::Unwinder<bool> nsv (no_save_visual, true);
7807
7808         /* build a list of all tracks, including children */
7809
7810         TrackViewList all;
7811         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7812                 all.push_back (*i);
7813                 TimeAxisView::Children c = (*i)->get_child_list ();
7814                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7815                         all.push_back (j->get());
7816                 }
7817         }
7818
7819
7820         // find selection range.
7821         // if someone knows how to user TrackViewList::iterator for this
7822         // I'm all ears.
7823         int selected_top = -1;
7824         int selected_bottom = -1;
7825         int i = 0;
7826         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7827                 if ((*t)->marked_for_display ()) {
7828                         if (tracks.contains(*t)) {
7829                                 if (selected_top == -1) {
7830                                         selected_top = i;
7831                                 }
7832                                 selected_bottom = i;
7833                         }
7834                 }
7835         }
7836
7837         i = 0;
7838         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7839                 if ((*t)->marked_for_display ()) {
7840                         if (tracks.contains(*t)) {
7841                                 (*t)->set_height (h);
7842                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7843                         } else {
7844                                 if (i > selected_top && i < selected_bottom) {
7845                                         hide_track_in_display (*t);
7846                                 }
7847                         }
7848                 }
7849         }
7850
7851         /*
7852            set the controls_layout height now, because waiting for its size
7853            request signal handler will cause the vertical adjustment setting to fail
7854         */
7855
7856         controls_layout.property_height () = _full_canvas_height;
7857         vertical_adjustment.set_value (first_y_pos);
7858
7859         redo_visual_stack.push_back (current_visual_state (true));
7860
7861         visible_tracks_selector.set_text (_("Sel"));
7862 }
7863
7864 void
7865 Editor::save_visual_state (uint32_t n)
7866 {
7867         while (visual_states.size() <= n) {
7868                 visual_states.push_back (0);
7869         }
7870
7871         if (visual_states[n] != 0) {
7872                 delete visual_states[n];
7873         }
7874
7875         visual_states[n] = current_visual_state (true);
7876         gdk_beep ();
7877 }
7878
7879 void
7880 Editor::goto_visual_state (uint32_t n)
7881 {
7882         if (visual_states.size() <= n) {
7883                 return;
7884         }
7885
7886         if (visual_states[n] == 0) {
7887                 return;
7888         }
7889
7890         use_visual_state (*visual_states[n]);
7891 }
7892
7893 void
7894 Editor::start_visual_state_op (uint32_t n)
7895 {
7896         save_visual_state (n);
7897
7898         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7899         char buf[32];
7900         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7901         pup->set_text (buf);
7902         pup->touch();
7903 }
7904
7905 void
7906 Editor::cancel_visual_state_op (uint32_t n)
7907 {
7908         goto_visual_state (n);
7909 }
7910
7911 void
7912 Editor::toggle_region_mute ()
7913 {
7914         if (_ignore_region_action) {
7915                 return;
7916         }
7917
7918         RegionSelection rs = get_regions_from_selection_and_entered ();
7919
7920         if (rs.empty ()) {
7921                 return;
7922         }
7923
7924         if (rs.size() > 1) {
7925                 begin_reversible_command (_("mute regions"));
7926         } else {
7927                 begin_reversible_command (_("mute region"));
7928         }
7929
7930         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7931
7932                 (*i)->region()->playlist()->clear_changes ();
7933                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7934                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7935
7936         }
7937
7938         commit_reversible_command ();
7939 }
7940
7941 void
7942 Editor::combine_regions ()
7943 {
7944         /* foreach track with selected regions, take all selected regions
7945            and join them into a new region containing the subregions (as a
7946            playlist)
7947         */
7948
7949         typedef set<RouteTimeAxisView*> RTVS;
7950         RTVS tracks;
7951
7952         if (selection->regions.empty()) {
7953                 return;
7954         }
7955
7956         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7957                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7958
7959                 if (rtv) {
7960                         tracks.insert (rtv);
7961                 }
7962         }
7963
7964         begin_reversible_command (_("combine regions"));
7965
7966         vector<RegionView*> new_selection;
7967
7968         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7969                 RegionView* rv;
7970
7971                 if ((rv = (*i)->combine_regions ()) != 0) {
7972                         new_selection.push_back (rv);
7973                 }
7974         }
7975
7976         selection->clear_regions ();
7977         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7978                 selection->add (*i);
7979         }
7980
7981         commit_reversible_command ();
7982 }
7983
7984 void
7985 Editor::uncombine_regions ()
7986 {
7987         typedef set<RouteTimeAxisView*> RTVS;
7988         RTVS tracks;
7989
7990         if (selection->regions.empty()) {
7991                 return;
7992         }
7993
7994         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7995                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7996
7997                 if (rtv) {
7998                         tracks.insert (rtv);
7999                 }
8000         }
8001
8002         begin_reversible_command (_("uncombine regions"));
8003
8004         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8005                 (*i)->uncombine_regions ();
8006         }
8007
8008         commit_reversible_command ();
8009 }
8010
8011 void
8012 Editor::toggle_midi_input_active (bool flip_others)
8013 {
8014         bool onoff = false;
8015         boost::shared_ptr<RouteList> rl (new RouteList);
8016
8017         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8018                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8019
8020                 if (!rtav) {
8021                         continue;
8022                 }
8023
8024                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8025
8026                 if (mt) {
8027                         rl->push_back (rtav->route());
8028                         onoff = !mt->input_active();
8029                 }
8030         }
8031
8032         _session->set_exclusive_input_active (rl, onoff, flip_others);
8033 }
8034
8035 static bool ok_fine (GdkEventAny*) { return true; }
8036
8037 void
8038 Editor::lock ()
8039 {
8040         if (!lock_dialog) {
8041                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8042
8043                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8044                 lock_dialog->get_vbox()->pack_start (*padlock);
8045                 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8046
8047                 ArdourButton* b = manage (new ArdourButton);
8048                 b->set_name ("lock button");
8049                 b->set_text (_("Click to unlock"));
8050                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8051                 lock_dialog->get_vbox()->pack_start (*b);
8052
8053                 lock_dialog->get_vbox()->show_all ();
8054                 lock_dialog->set_size_request (200, 200);
8055         }
8056
8057         delete _main_menu_disabler;
8058         _main_menu_disabler = new MainMenuDisabler;
8059
8060         lock_dialog->present ();
8061
8062         lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8063 }
8064
8065 void
8066 Editor::unlock ()
8067 {
8068         lock_dialog->hide ();
8069
8070         delete _main_menu_disabler;
8071         _main_menu_disabler = 0;
8072
8073         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8074                 start_lock_event_timing ();
8075         }
8076 }
8077
8078 void
8079 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8080 {
8081         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8082 }
8083
8084 void
8085 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8086 {
8087         Timers::TimerSuspender t;
8088         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8089         Gtkmm2ext::UI::instance()->flush_pending (1);
8090 }
8091
8092 void
8093 Editor::bring_all_sources_into_session ()
8094 {
8095         if (!_session) {
8096                 return;
8097         }
8098
8099         Gtk::Label msg;
8100         ArdourDialog w (_("Moving embedded files into session folder"));
8101         w.get_vbox()->pack_start (msg);
8102         w.present ();
8103
8104         /* flush all pending GUI events because we're about to start copying
8105          * files
8106          */
8107
8108         Timers::TimerSuspender t;
8109         Gtkmm2ext::UI::instance()->flush_pending (3);
8110
8111         cerr << " Do it\n";
8112
8113         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8114 }