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