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