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