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