Remove some redundant calls to snap_to(). get_preferred_edit_position() already...
[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         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4873         RegionSelection foo;
4874
4875         samplepos_t const start_sample = regions.start ();
4876         samplepos_t const end_sample = regions.end_sample ();
4877         samplecnt_t const gap = end_sample - start_sample + 1;
4878
4879         begin_reversible_command (Operations::duplicate_region);
4880
4881         selection->clear_regions ();
4882
4883         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4884
4885                 boost::shared_ptr<Region> r ((*i)->region());
4886
4887                 TimeAxisView& tv = (*i)->get_time_axis_view();
4888                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4889                 latest_regionviews.clear ();
4890                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4891
4892                 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4893                 playlist = (*i)->region()->playlist();
4894                 playlist->clear_changes ();
4895                 playlist->duplicate (r, position, gap, times);
4896                 _session->add_command(new StatefulDiffCommand (playlist));
4897
4898                 c.disconnect ();
4899
4900                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4901         }
4902
4903         if (!foo.empty()) {
4904                 selection->set (foo);
4905         }
4906
4907         commit_reversible_command ();
4908 }
4909
4910 void
4911 Editor::duplicate_selection (float times)
4912 {
4913         if (selection->time.empty() || selection->tracks.empty()) {
4914                 return;
4915         }
4916
4917         boost::shared_ptr<Playlist> playlist;
4918
4919         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4920
4921         bool in_command = false;
4922
4923         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4924                 if ((playlist = (*i)->playlist()) == 0) {
4925                         continue;
4926                 }
4927                 playlist->clear_changes ();
4928
4929                 if (clicked_selection) {
4930                         playlist->duplicate_range (selection->time[clicked_selection], times);
4931                 } else {
4932                         playlist->duplicate_ranges (selection->time, times);
4933                 }
4934
4935                 if (!in_command) {
4936                         begin_reversible_command (_("duplicate range selection"));
4937                         in_command = true;
4938                 }
4939                 _session->add_command (new StatefulDiffCommand (playlist));
4940
4941         }
4942
4943         if (in_command) {
4944                 if (times == 1.0f) {
4945                         // now "move" range selection to after the current range selection
4946                         samplecnt_t distance = 0;
4947
4948                         if (clicked_selection) {
4949                                 distance =
4950                                     selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4951                         } else {
4952                                 distance = selection->time.end_sample () - selection->time.start ();
4953                         }
4954
4955                         selection->move_time (distance);
4956                 }
4957                 commit_reversible_command ();
4958         }
4959 }
4960
4961 /** Reset all selected points to the relevant default value */
4962 void
4963 Editor::reset_point_selection ()
4964 {
4965         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4966                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4967                 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4968         }
4969 }
4970
4971 void
4972 Editor::center_playhead ()
4973 {
4974         float const page = _visible_canvas_width * samples_per_pixel;
4975         center_screen_internal (playhead_cursor->current_sample (), page);
4976 }
4977
4978 void
4979 Editor::center_edit_point ()
4980 {
4981         float const page = _visible_canvas_width * samples_per_pixel;
4982         center_screen_internal (get_preferred_edit_position(), page);
4983 }
4984
4985 /** Caller must begin and commit a reversible command */
4986 void
4987 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4988 {
4989         playlist->clear_changes ();
4990         playlist->clear ();
4991         _session->add_command (new StatefulDiffCommand (playlist));
4992 }
4993
4994 void
4995 Editor::nudge_track (bool use_edit, bool forwards)
4996 {
4997         boost::shared_ptr<Playlist> playlist;
4998         samplepos_t distance;
4999         samplepos_t next_distance;
5000         samplepos_t start;
5001
5002         if (use_edit) {
5003                 start = get_preferred_edit_position();
5004         } else {
5005                 start = 0;
5006         }
5007
5008         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5009                 return;
5010         }
5011
5012         if (selection->tracks.empty()) {
5013                 return;
5014         }
5015
5016         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5017         bool in_command = false;
5018
5019         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5020
5021                 if ((playlist = (*i)->playlist()) == 0) {
5022                         continue;
5023                 }
5024
5025                 playlist->clear_changes ();
5026                 playlist->clear_owned_changes ();
5027
5028                 playlist->nudge_after (start, distance, forwards);
5029
5030                 if (!in_command) {
5031                         begin_reversible_command (_("nudge track"));
5032                         in_command = true;
5033                 }
5034                 vector<Command*> cmds;
5035
5036                 playlist->rdiff (cmds);
5037                 _session->add_commands (cmds);
5038
5039                 _session->add_command (new StatefulDiffCommand (playlist));
5040         }
5041
5042         if (in_command) {
5043                 commit_reversible_command ();
5044         }
5045 }
5046
5047 void
5048 Editor::remove_last_capture ()
5049 {
5050         vector<string> choices;
5051         string prompt;
5052
5053         if (!_session) {
5054                 return;
5055         }
5056
5057         if (Config->get_verify_remove_last_capture()) {
5058                 prompt  = _("Do you really want to destroy the last capture?"
5059                             "\n(This is destructive and cannot be undone)");
5060
5061                 choices.push_back (_("No, do nothing."));
5062                 choices.push_back (_("Yes, destroy it."));
5063
5064                 Choice prompter (_("Destroy last capture"), prompt, choices);
5065
5066                 if (prompter.run () == 1) {
5067                         _session->remove_last_capture ();
5068                         _regions->redisplay ();
5069                 }
5070
5071         } else {
5072                 _session->remove_last_capture();
5073                 _regions->redisplay ();
5074         }
5075 }
5076
5077 void
5078 Editor::normalize_region ()
5079 {
5080         if (!_session) {
5081                 return;
5082         }
5083
5084         RegionSelection rs = get_regions_from_selection_and_entered ();
5085
5086         if (rs.empty()) {
5087                 return;
5088         }
5089
5090         NormalizeDialog dialog (rs.size() > 1);
5091
5092         if (dialog.run () != RESPONSE_ACCEPT) {
5093                 return;
5094         }
5095
5096         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5097         gdk_flush ();
5098
5099         /* XXX: should really only count audio regions here */
5100         int const regions = rs.size ();
5101
5102         /* Make a list of the selected audio regions' maximum amplitudes, and also
5103            obtain the maximum amplitude of them all.
5104         */
5105         list<double> max_amps;
5106         list<double> rms_vals;
5107         double max_amp = 0;
5108         double max_rms = 0;
5109         bool use_rms = dialog.constrain_rms ();
5110
5111         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5112                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5113                 if (!arv) {
5114                         continue;
5115                 }
5116                 dialog.descend (1.0 / regions);
5117                 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5118                 if (use_rms) {
5119                         double r = arv->audio_region()->rms (&dialog);
5120                         max_rms = max (max_rms, r);
5121                         rms_vals.push_back (r);
5122                 }
5123
5124                 if (a == -1) {
5125                         /* the user cancelled the operation */
5126                         return;
5127                 }
5128
5129                 max_amps.push_back (a);
5130                 max_amp = max (max_amp, a);
5131                 dialog.ascend ();
5132         }
5133
5134         list<double>::const_iterator a = max_amps.begin ();
5135         list<double>::const_iterator l = rms_vals.begin ();
5136         bool in_command = false;
5137
5138         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5139                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5140                 if (!arv) {
5141                         continue;
5142                 }
5143
5144                 arv->region()->clear_changes ();
5145
5146                 double amp = dialog.normalize_individually() ? *a : max_amp;
5147                 double target = dialog.target_peak (); // dB
5148
5149                 if (use_rms) {
5150                         double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5151                         const double t_rms = dialog.target_rms ();
5152                         const gain_t c_peak = dB_to_coefficient (target);
5153                         const gain_t c_rms  = dB_to_coefficient (t_rms);
5154                         if ((amp_rms / c_rms) > (amp / c_peak)) {
5155                                 amp = amp_rms;
5156                                 target = t_rms;
5157                         }
5158                 }
5159
5160                 arv->audio_region()->normalize (amp, target);
5161
5162                 if (!in_command) {
5163                         begin_reversible_command (_("normalize"));
5164                         in_command = true;
5165                 }
5166                 _session->add_command (new StatefulDiffCommand (arv->region()));
5167
5168                 ++a;
5169                 ++l;
5170         }
5171
5172         if (in_command) {
5173                 commit_reversible_command ();
5174         }
5175 }
5176
5177
5178 void
5179 Editor::reset_region_scale_amplitude ()
5180 {
5181         if (!_session) {
5182                 return;
5183         }
5184
5185         RegionSelection rs = get_regions_from_selection_and_entered ();
5186
5187         if (rs.empty()) {
5188                 return;
5189         }
5190
5191         bool in_command = false;
5192
5193         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5194                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5195                 if (!arv)
5196                         continue;
5197                 arv->region()->clear_changes ();
5198                 arv->audio_region()->set_scale_amplitude (1.0f);
5199
5200                 if(!in_command) {
5201                                 begin_reversible_command ("reset gain");
5202                                 in_command = true;
5203                 }
5204                 _session->add_command (new StatefulDiffCommand (arv->region()));
5205         }
5206
5207         if (in_command) {
5208                 commit_reversible_command ();
5209         }
5210 }
5211
5212 void
5213 Editor::adjust_region_gain (bool up)
5214 {
5215         RegionSelection rs = get_regions_from_selection_and_entered ();
5216
5217         if (!_session || rs.empty()) {
5218                 return;
5219         }
5220
5221         bool in_command = false;
5222
5223         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5224                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5225                 if (!arv) {
5226                         continue;
5227                 }
5228
5229                 arv->region()->clear_changes ();
5230
5231                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5232
5233                 if (up) {
5234                         dB += 1;
5235                 } else {
5236                         dB -= 1;
5237                 }
5238
5239                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5240
5241                 if (!in_command) {
5242                                 begin_reversible_command ("adjust region gain");
5243                                 in_command = true;
5244                 }
5245                 _session->add_command (new StatefulDiffCommand (arv->region()));
5246         }
5247
5248         if (in_command) {
5249                 commit_reversible_command ();
5250         }
5251 }
5252
5253 void
5254 Editor::reset_region_gain ()
5255 {
5256         RegionSelection rs = get_regions_from_selection_and_entered ();
5257
5258         if (!_session || rs.empty()) {
5259                 return;
5260         }
5261
5262         bool in_command = false;
5263
5264         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5265                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5266                 if (!arv) {
5267                         continue;
5268                 }
5269
5270                 arv->region()->clear_changes ();
5271
5272                 arv->audio_region()->set_scale_amplitude (1.0f);
5273
5274                 if (!in_command) {
5275                                 begin_reversible_command ("reset region gain");
5276                                 in_command = true;
5277                 }
5278                 _session->add_command (new StatefulDiffCommand (arv->region()));
5279         }
5280
5281         if (in_command) {
5282                 commit_reversible_command ();
5283         }
5284 }
5285
5286 void
5287 Editor::reverse_region ()
5288 {
5289         if (!_session) {
5290                 return;
5291         }
5292
5293         Reverse rev (*_session);
5294         apply_filter (rev, _("reverse regions"));
5295 }
5296
5297 void
5298 Editor::strip_region_silence ()
5299 {
5300         if (!_session) {
5301                 return;
5302         }
5303
5304         RegionSelection rs = get_regions_from_selection_and_entered ();
5305
5306         if (rs.empty()) {
5307                 return;
5308         }
5309
5310         std::list<RegionView*> audio_only;
5311
5312         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5313                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5314                 if (arv) {
5315                         audio_only.push_back (arv);
5316                 }
5317         }
5318
5319         assert (!audio_only.empty());
5320
5321         StripSilenceDialog d (_session, audio_only);
5322         int const r = d.run ();
5323
5324         d.drop_rects ();
5325
5326         if (r == Gtk::RESPONSE_OK) {
5327                 ARDOUR::AudioIntervalMap silences;
5328                 d.silences (silences);
5329                 StripSilence s (*_session, silences, d.fade_length());
5330
5331                 apply_filter (s, _("strip silence"), &d);
5332         }
5333 }
5334
5335 Command*
5336 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5337 {
5338         Evoral::Sequence<Temporal::Beats>::Notes selected;
5339         mrv.selection_as_notelist (selected, true);
5340
5341         vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5342         v.push_back (selected);
5343
5344         Temporal::Beats pos_beats  = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5345
5346         return op (mrv.midi_region()->model(), pos_beats, v);
5347 }
5348
5349 void
5350 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5351 {
5352         if (rs.empty()) {
5353                 return;
5354         }
5355
5356         bool in_command = false;
5357
5358         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5359                 RegionSelection::const_iterator tmp = r;
5360                 ++tmp;
5361
5362                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5363
5364                 if (mrv) {
5365                         Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5366                         if (cmd) {
5367                                 if (!in_command) {
5368                                         begin_reversible_command (op.name ());
5369                                         in_command = true;
5370                                 }
5371                                 (*cmd)();
5372                                 _session->add_command (cmd);
5373                         }
5374                 }
5375
5376                 r = tmp;
5377         }
5378
5379         if (in_command) {
5380                 commit_reversible_command ();
5381                 _session->set_dirty ();
5382         }
5383 }
5384
5385 void
5386 Editor::fork_region ()
5387 {
5388         RegionSelection rs = get_regions_from_selection_and_entered ();
5389
5390         if (rs.empty()) {
5391                 return;
5392         }
5393
5394         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5395         bool in_command = false;
5396
5397         gdk_flush ();
5398
5399         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5400                 RegionSelection::iterator tmp = r;
5401                 ++tmp;
5402
5403                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5404
5405                 if (mrv) {
5406                         try {
5407                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5408                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5409                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5410
5411                                 if (!in_command) {
5412                                         begin_reversible_command (_("Fork Region(s)"));
5413                                         in_command = true;
5414                                 }
5415                                 playlist->clear_changes ();
5416                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5417                                 _session->add_command(new StatefulDiffCommand (playlist));
5418                         } catch (...) {
5419                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5420                         }
5421                 }
5422
5423                 r = tmp;
5424         }
5425
5426         if (in_command) {
5427                 commit_reversible_command ();
5428         }
5429 }
5430
5431 void
5432 Editor::quantize_region ()
5433 {
5434         if (_session) {
5435                 quantize_regions(get_regions_from_selection_and_entered ());
5436         }
5437 }
5438
5439 void
5440 Editor::quantize_regions (const RegionSelection& rs)
5441 {
5442         if (rs.n_midi_regions() == 0) {
5443                 return;
5444         }
5445
5446         if (!quantize_dialog) {
5447                 quantize_dialog = new QuantizeDialog (*this);
5448         }
5449
5450         if (quantize_dialog->is_mapped()) {
5451                 /* in progress already */
5452                 return;
5453         }
5454
5455         quantize_dialog->present ();
5456         const int r = quantize_dialog->run ();
5457         quantize_dialog->hide ();
5458
5459         if (r == Gtk::RESPONSE_OK) {
5460                 Quantize quant (quantize_dialog->snap_start(),
5461                                 quantize_dialog->snap_end(),
5462                                 quantize_dialog->start_grid_size(),
5463                                 quantize_dialog->end_grid_size(),
5464                                 quantize_dialog->strength(),
5465                                 quantize_dialog->swing(),
5466                                 quantize_dialog->threshold());
5467
5468                 apply_midi_note_edit_op (quant, rs);
5469         }
5470 }
5471
5472 void
5473 Editor::legatize_region (bool shrink_only)
5474 {
5475         if (_session) {
5476                 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5477         }
5478 }
5479
5480 void
5481 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5482 {
5483         if (rs.n_midi_regions() == 0) {
5484                 return;
5485         }
5486
5487         Legatize legatize(shrink_only);
5488         apply_midi_note_edit_op (legatize, rs);
5489 }
5490
5491 void
5492 Editor::transform_region ()
5493 {
5494         if (_session) {
5495                 transform_regions(get_regions_from_selection_and_entered ());
5496         }
5497 }
5498
5499 void
5500 Editor::transform_regions (const RegionSelection& rs)
5501 {
5502         if (rs.n_midi_regions() == 0) {
5503                 return;
5504         }
5505
5506         TransformDialog td;
5507
5508         td.present();
5509         const int r = td.run();
5510         td.hide();
5511
5512         if (r == Gtk::RESPONSE_OK) {
5513                 Transform transform(td.get());
5514                 apply_midi_note_edit_op(transform, rs);
5515         }
5516 }
5517
5518 void
5519 Editor::transpose_region ()
5520 {
5521         if (_session) {
5522                 transpose_regions(get_regions_from_selection_and_entered ());
5523         }
5524 }
5525
5526 void
5527 Editor::transpose_regions (const RegionSelection& rs)
5528 {
5529         if (rs.n_midi_regions() == 0) {
5530                 return;
5531         }
5532
5533         TransposeDialog d;
5534         int const r = d.run ();
5535
5536         if (r == RESPONSE_ACCEPT) {
5537                 Transpose transpose(d.semitones ());
5538                 apply_midi_note_edit_op (transpose, rs);
5539         }
5540 }
5541
5542 void
5543 Editor::insert_patch_change (bool from_context)
5544 {
5545         RegionSelection rs = get_regions_from_selection_and_entered ();
5546
5547         if (rs.empty ()) {
5548                 return;
5549         }
5550
5551         const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5552
5553         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5554            there may be more than one, but the PatchChangeDialog can only offer
5555            one set of patch menus.
5556         */
5557         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5558
5559         Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5560         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5561
5562         if (d.run() == RESPONSE_CANCEL) {
5563                 return;
5564         }
5565
5566         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5567                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5568                 if (mrv) {
5569                         if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5570                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5571                         }
5572                 }
5573         }
5574 }
5575
5576 void
5577 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5578 {
5579         RegionSelection rs = get_regions_from_selection_and_entered ();
5580
5581         if (rs.empty()) {
5582                 return;
5583         }
5584
5585         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5586         bool in_command = false;
5587
5588         gdk_flush ();
5589
5590         int n = 0;
5591         int const N = rs.size ();
5592
5593         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5594                 RegionSelection::iterator tmp = r;
5595                 ++tmp;
5596
5597                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5598                 if (arv) {
5599                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5600
5601                         if (progress) {
5602                                 progress->descend (1.0 / N);
5603                         }
5604
5605                         if (arv->audio_region()->apply (filter, progress) == 0) {
5606
5607                                 playlist->clear_changes ();
5608                                 playlist->clear_owned_changes ();
5609
5610                                 if (!in_command) {
5611                                         begin_reversible_command (command);
5612                                         in_command = true;
5613                                 }
5614
5615                                 if (filter.results.empty ()) {
5616
5617                                         /* no regions returned; remove the old one */
5618                                         playlist->remove_region (arv->region ());
5619
5620                                 } else {
5621
5622                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5623
5624                                         /* first region replaces the old one */
5625                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5626                                         ++res;
5627
5628                                         /* add the rest */
5629                                         while (res != filter.results.end()) {
5630                                                 playlist->add_region (*res, (*res)->position());
5631                                                 ++res;
5632                                         }
5633
5634                                 }
5635
5636                                 /* We might have removed regions, which alters other regions' layering_index,
5637                                    so we need to do a recursive diff here.
5638                                 */
5639                                 vector<Command*> cmds;
5640                                 playlist->rdiff (cmds);
5641                                 _session->add_commands (cmds);
5642
5643                                 _session->add_command(new StatefulDiffCommand (playlist));
5644                         }
5645
5646                         if (progress) {
5647                                 progress->ascend ();
5648                         }
5649                 }
5650
5651                 r = tmp;
5652                 ++n;
5653         }
5654
5655         if (in_command) {
5656                 commit_reversible_command ();
5657         }
5658 }
5659
5660 void
5661 Editor::external_edit_region ()
5662 {
5663         /* more to come */
5664 }
5665
5666 void
5667 Editor::reset_region_gain_envelopes ()
5668 {
5669         RegionSelection rs = get_regions_from_selection_and_entered ();
5670
5671         if (!_session || rs.empty()) {
5672                 return;
5673         }
5674
5675         bool in_command = false;
5676
5677         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5678                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5679                 if (arv) {
5680                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5681                         XMLNode& before (alist->get_state());
5682
5683                         arv->audio_region()->set_default_envelope ();
5684
5685                         if (!in_command) {
5686                                 begin_reversible_command (_("reset region gain"));
5687                                 in_command = true;
5688                         }
5689                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5690                 }
5691         }
5692
5693         if (in_command) {
5694                 commit_reversible_command ();
5695         }
5696 }
5697
5698 void
5699 Editor::set_region_gain_visibility (RegionView* rv)
5700 {
5701         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5702         if (arv) {
5703                 arv->update_envelope_visibility();
5704         }
5705 }
5706
5707 void
5708 Editor::set_gain_envelope_visibility ()
5709 {
5710         if (!_session) {
5711                 return;
5712         }
5713
5714         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5715                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5716                 if (v) {
5717                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5718                 }
5719         }
5720 }
5721
5722 void
5723 Editor::toggle_gain_envelope_active ()
5724 {
5725         if (_ignore_region_action) {
5726                 return;
5727         }
5728
5729         RegionSelection rs = get_regions_from_selection_and_entered ();
5730
5731         if (!_session || rs.empty()) {
5732                 return;
5733         }
5734
5735         bool in_command = false;
5736
5737         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5738                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5739                 if (arv) {
5740                         arv->region()->clear_changes ();
5741                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5742
5743                         if (!in_command) {
5744                                 begin_reversible_command (_("region gain envelope active"));
5745                                 in_command = true;
5746                         }
5747                         _session->add_command (new StatefulDiffCommand (arv->region()));
5748                 }
5749         }
5750
5751         if (in_command) {
5752                 commit_reversible_command ();
5753         }
5754 }
5755
5756 void
5757 Editor::toggle_region_lock ()
5758 {
5759         if (_ignore_region_action) {
5760                 return;
5761         }
5762
5763         RegionSelection rs = get_regions_from_selection_and_entered ();
5764
5765         if (!_session || rs.empty()) {
5766                 return;
5767         }
5768
5769         begin_reversible_command (_("toggle region lock"));
5770
5771         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5772                 (*i)->region()->clear_changes ();
5773                 (*i)->region()->set_locked (!(*i)->region()->locked());
5774                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5775         }
5776
5777         commit_reversible_command ();
5778 }
5779
5780 void
5781 Editor::toggle_region_video_lock ()
5782 {
5783         if (_ignore_region_action) {
5784                 return;
5785         }
5786
5787         RegionSelection rs = get_regions_from_selection_and_entered ();
5788
5789         if (!_session || rs.empty()) {
5790                 return;
5791         }
5792
5793         begin_reversible_command (_("Toggle Video Lock"));
5794
5795         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5796                 (*i)->region()->clear_changes ();
5797                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5798                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5799         }
5800
5801         commit_reversible_command ();
5802 }
5803
5804 void
5805 Editor::toggle_region_lock_style ()
5806 {
5807         if (_ignore_region_action) {
5808                 return;
5809         }
5810
5811         RegionSelection rs = get_regions_from_selection_and_entered ();
5812
5813         if (!_session || rs.empty()) {
5814                 return;
5815         }
5816
5817         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5818         vector<Widget*> proxies = a->get_proxies();
5819         Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5820
5821         assert (cmi);
5822
5823         begin_reversible_command (_("toggle region lock style"));
5824
5825         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5826                 (*i)->region()->clear_changes ();
5827                 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5828                 (*i)->region()->set_position_lock_style (ns);
5829                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5830         }
5831
5832         commit_reversible_command ();
5833 }
5834
5835 void
5836 Editor::toggle_opaque_region ()
5837 {
5838         if (_ignore_region_action) {
5839                 return;
5840         }
5841
5842         RegionSelection rs = get_regions_from_selection_and_entered ();
5843
5844         if (!_session || rs.empty()) {
5845                 return;
5846         }
5847
5848         begin_reversible_command (_("change region opacity"));
5849
5850         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5851                 (*i)->region()->clear_changes ();
5852                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5853                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5854         }
5855
5856         commit_reversible_command ();
5857 }
5858
5859 void
5860 Editor::toggle_record_enable ()
5861 {
5862         bool new_state = false;
5863         bool first = true;
5864         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5865                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5866                 if (!rtav)
5867                         continue;
5868                 if (!rtav->is_track())
5869                         continue;
5870
5871                 if (first) {
5872                         new_state = !rtav->track()->rec_enable_control()->get_value();
5873                         first = false;
5874                 }
5875
5876                 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5877         }
5878 }
5879
5880 StripableList
5881 tracklist_to_stripables (TrackViewList list)
5882 {
5883         StripableList ret;
5884
5885         for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
5886                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
5887
5888                 if (rtv && rtv->is_track()) {
5889                         ret.push_back (rtv->track());
5890                 }
5891         }
5892
5893         return ret;
5894 }
5895
5896 void
5897 Editor::play_solo_selection (bool restart)
5898 {
5899         //note: session::solo_selection takes care of invalidating the region playlist
5900
5901         if ((!selection->tracks.empty()) && selection->time.length() > 0) {  //a range is selected; solo the tracks and roll
5902
5903                 StripableList sl = tracklist_to_stripables (selection->tracks);
5904                 _session->solo_selection (sl, true);
5905
5906                 if (restart) {
5907                         samplepos_t start = selection->time.start();
5908                         samplepos_t end = selection->time.end_sample();
5909                         _session->request_bounded_roll (start, end);
5910                 }
5911         } else if (! selection->tracks.empty()) {  //no range is selected, but tracks are selected; solo the tracks and roll
5912                 StripableList sl = tracklist_to_stripables (selection->tracks);
5913                 _session->solo_selection (sl, true);
5914                 _session->request_cancel_play_range();
5915                 transition_to_rolling (true);
5916
5917         } else if (! selection->regions.empty()) {  //solo any tracks with selected regions, and roll
5918                 StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
5919                 _session->solo_selection (sl, true);
5920                 _session->request_cancel_play_range();
5921                 transition_to_rolling (true);
5922         } else {
5923                 _session->request_cancel_play_range();
5924                 transition_to_rolling (true);  //no selection.  just roll.
5925         }
5926 }
5927
5928 void
5929 Editor::toggle_solo ()
5930 {
5931         bool new_state = false;
5932         bool first = true;
5933         boost::shared_ptr<ControlList> cl (new ControlList);
5934
5935         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5936                 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5937
5938                 if (!stav || !stav->stripable()->solo_control()) {
5939                         continue;
5940                 }
5941
5942                 if (first) {
5943                         new_state = !stav->stripable()->solo_control()->soloed ();
5944                         first = false;
5945                 }
5946
5947                 cl->push_back (stav->stripable()->solo_control());
5948         }
5949
5950         _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5951 }
5952
5953 void
5954 Editor::toggle_mute ()
5955 {
5956         bool new_state = false;
5957         bool first = true;
5958         boost::shared_ptr<ControlList> cl (new ControlList);
5959
5960         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5961                 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5962
5963                 if (!stav || !stav->stripable()->mute_control()) {
5964                         continue;
5965                 }
5966
5967                 if (first) {
5968                         new_state = !stav->stripable()->mute_control()->muted();
5969                         first = false;
5970                 }
5971
5972                 cl->push_back (stav->stripable()->mute_control());
5973         }
5974
5975         _session->set_controls (cl, new_state, Controllable::UseGroup);
5976 }
5977
5978 void
5979 Editor::toggle_solo_isolate ()
5980 {
5981 }
5982
5983
5984 void
5985 Editor::fade_range ()
5986 {
5987         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5988
5989         begin_reversible_command (_("fade range"));
5990
5991         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5992                 (*i)->fade_range (selection->time);
5993         }
5994
5995         commit_reversible_command ();
5996 }
5997
5998
5999 void
6000 Editor::set_fade_length (bool in)
6001 {
6002         RegionSelection rs = get_regions_from_selection_and_entered ();
6003
6004         if (rs.empty()) {
6005                 return;
6006         }
6007
6008         /* we need a region to measure the offset from the start */
6009
6010         RegionView* rv = rs.front ();
6011
6012         samplepos_t pos = get_preferred_edit_position();
6013         samplepos_t len;
6014         char const * cmd;
6015
6016         if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6017                 /* edit point is outside the relevant region */
6018                 return;
6019         }
6020
6021         if (in) {
6022                 if (pos <= rv->region()->position()) {
6023                         /* can't do it */
6024                         return;
6025                 }
6026                 len = pos - rv->region()->position();
6027                 cmd = _("set fade in length");
6028         } else {
6029                 if (pos >= rv->region()->last_sample()) {
6030                         /* can't do it */
6031                         return;
6032                 }
6033                 len = rv->region()->last_sample() - pos;
6034                 cmd = _("set fade out length");
6035         }
6036
6037         bool in_command = false;
6038
6039         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6040                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6041
6042                 if (!tmp) {
6043                         continue;
6044                 }
6045
6046                 boost::shared_ptr<AutomationList> alist;
6047                 if (in) {
6048                         alist = tmp->audio_region()->fade_in();
6049                 } else {
6050                         alist = tmp->audio_region()->fade_out();
6051                 }
6052
6053                 XMLNode &before = alist->get_state();
6054
6055                 if (in) {
6056                         tmp->audio_region()->set_fade_in_length (len);
6057                         tmp->audio_region()->set_fade_in_active (true);
6058                 } else {
6059                         tmp->audio_region()->set_fade_out_length (len);
6060                         tmp->audio_region()->set_fade_out_active (true);
6061                 }
6062
6063                 if (!in_command) {
6064                         begin_reversible_command (cmd);
6065                         in_command = true;
6066                 }
6067                 XMLNode &after = alist->get_state();
6068                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6069         }
6070
6071         if (in_command) {
6072                 commit_reversible_command ();
6073         }
6074 }
6075
6076 void
6077 Editor::set_fade_in_shape (FadeShape shape)
6078 {
6079         RegionSelection rs = get_regions_from_selection_and_entered ();
6080
6081         if (rs.empty()) {
6082                 return;
6083         }
6084         bool in_command = false;
6085
6086         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6087                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6088
6089                 if (!tmp) {
6090                         continue;
6091                 }
6092
6093                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6094                 XMLNode &before = alist->get_state();
6095
6096                 tmp->audio_region()->set_fade_in_shape (shape);
6097
6098                 if (!in_command) {
6099                         begin_reversible_command (_("set fade in shape"));
6100                         in_command = true;
6101                 }
6102                 XMLNode &after = alist->get_state();
6103                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6104         }
6105
6106         if (in_command) {
6107                 commit_reversible_command ();
6108         }
6109 }
6110
6111 void
6112 Editor::set_fade_out_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_out();
6129                 XMLNode &before = alist->get_state();
6130
6131                 tmp->audio_region()->set_fade_out_shape (shape);
6132
6133                 if(!in_command) {
6134                         begin_reversible_command (_("set fade out 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_in_active (bool yn)
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
6164                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6165
6166                 ar->clear_changes ();
6167                 ar->set_fade_in_active (yn);
6168
6169                 if (!in_command) {
6170                         begin_reversible_command (_("set fade in active"));
6171                         in_command = true;
6172                 }
6173                 _session->add_command (new StatefulDiffCommand (ar));
6174         }
6175
6176         if (in_command) {
6177                 commit_reversible_command ();
6178         }
6179 }
6180
6181 void
6182 Editor::set_fade_out_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                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6199
6200                 ar->clear_changes ();
6201                 ar->set_fade_out_active (yn);
6202
6203                 if (!in_command) {
6204                         begin_reversible_command (_("set fade out active"));
6205                         in_command = true;
6206                 }
6207                 _session->add_command(new StatefulDiffCommand (ar));
6208         }
6209
6210         if (in_command) {
6211                 commit_reversible_command ();
6212         }
6213 }
6214
6215 void
6216 Editor::toggle_region_fades (int dir)
6217 {
6218         if (_ignore_region_action) {
6219                 return;
6220         }
6221
6222         boost::shared_ptr<AudioRegion> ar;
6223         bool yn = false;
6224
6225         RegionSelection rs = get_regions_from_selection_and_entered ();
6226
6227         if (rs.empty()) {
6228                 return;
6229         }
6230
6231         RegionSelection::iterator i;
6232         for (i = rs.begin(); i != rs.end(); ++i) {
6233                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6234                         if (dir == -1) {
6235                                 yn = ar->fade_out_active ();
6236                         } else {
6237                                 yn = ar->fade_in_active ();
6238                         }
6239                         break;
6240                 }
6241         }
6242
6243         if (i == rs.end()) {
6244                 return;
6245         }
6246
6247         /* XXX should this undo-able? */
6248         bool in_command = false;
6249
6250         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6251                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6252                         continue;
6253                 }
6254                 ar->clear_changes ();
6255
6256                 if (dir == 1 || dir == 0) {
6257                         ar->set_fade_in_active (!yn);
6258                 }
6259
6260                 if (dir == -1 || dir == 0) {
6261                         ar->set_fade_out_active (!yn);
6262                 }
6263                 if (!in_command) {
6264                         begin_reversible_command (_("toggle fade active"));
6265                         in_command = true;
6266                 }
6267                 _session->add_command(new StatefulDiffCommand (ar));
6268         }
6269
6270         if (in_command) {
6271                 commit_reversible_command ();
6272         }
6273 }
6274
6275
6276 /** Update region fade visibility after its configuration has been changed */
6277 void
6278 Editor::update_region_fade_visibility ()
6279 {
6280         bool _fade_visibility = _session->config.get_show_region_fades ();
6281
6282         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6283                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6284                 if (v) {
6285                         if (_fade_visibility) {
6286                                 v->audio_view()->show_all_fades ();
6287                         } else {
6288                                 v->audio_view()->hide_all_fades ();
6289                         }
6290                 }
6291         }
6292 }
6293
6294 void
6295 Editor::set_edit_point ()
6296 {
6297         bool ignored;
6298         MusicSample where (0, 0);
6299
6300         if (!mouse_sample (where.sample, ignored)) {
6301                 return;
6302         }
6303
6304         snap_to (where);
6305
6306         if (selection->markers.empty()) {
6307
6308                 mouse_add_new_marker (where.sample);
6309
6310         } else {
6311                 bool ignored;
6312
6313                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6314
6315                 if (loc) {
6316                         loc->move_to (where.sample, where.division);
6317                 }
6318         }
6319 }
6320
6321 void
6322 Editor::set_playhead_cursor ()
6323 {
6324         if (entered_marker) {
6325                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6326         } else {
6327                 MusicSample where (0, 0);
6328                 bool ignored;
6329
6330                 if (!mouse_sample (where.sample, ignored)) {
6331                         return;
6332                 }
6333
6334                 snap_to (where);
6335
6336                 if (_session) {
6337                         _session->request_locate (where.sample, _session->transport_rolling());
6338                 }
6339         }
6340
6341 //not sure what this was for;  remove it for now.
6342 //      if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6343 //              cancel_time_selection();
6344 //      }
6345
6346 }
6347
6348 void
6349 Editor::split_region ()
6350 {
6351         if (_drags->active ()) {
6352                 return;
6353         }
6354
6355         //if a range is selected, separate it
6356         if (!selection->time.empty()) {
6357                 separate_regions_between (selection->time);
6358                 return;
6359         }
6360
6361         //if no range was selected, try to find some regions to split
6362         if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6363
6364                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6365                 const samplepos_t pos = get_preferred_edit_position();
6366                 const int32_t division = get_grid_music_divisions (0);
6367                 MusicSample where (pos, division);
6368
6369                 if (rs.empty()) {
6370                         return;
6371                 }
6372
6373                 split_regions_at (where, rs);
6374
6375         }
6376 }
6377
6378 void
6379 Editor::select_next_stripable (bool routes_only)
6380 {
6381         if (selection->tracks.empty()) {
6382                 selection->set (track_views.front());
6383                 return;
6384         }
6385
6386         TimeAxisView* current = selection->tracks.front();
6387
6388         bool valid;
6389         do {
6390                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6391
6392                         if (*i == current) {
6393                                 ++i;
6394                                 if (i != track_views.end()) {
6395                                         current = (*i);
6396                                 } else {
6397                                         current = (*(track_views.begin()));
6398                                         //selection->set (*(track_views.begin()));
6399                                 }
6400                                 break;
6401                         }
6402                 }
6403
6404                 if (routes_only) {
6405                         RouteUI* rui = dynamic_cast<RouteUI *>(current);
6406                         valid = rui && rui->route()->active();
6407                 } else {
6408                         valid = 0 != current->stripable ().get();
6409                 }
6410
6411         } while (current->hidden() || !valid);
6412
6413         selection->set (current);
6414
6415         ensure_time_axis_view_is_visible (*current, false);
6416 }
6417
6418 void
6419 Editor::select_prev_stripable (bool routes_only)
6420 {
6421         if (selection->tracks.empty()) {
6422                 selection->set (track_views.front());
6423                 return;
6424         }
6425
6426         TimeAxisView* current = selection->tracks.front();
6427
6428         bool valid;
6429         do {
6430                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6431
6432                         if (*i == current) {
6433                                 ++i;
6434                                 if (i != track_views.rend()) {
6435                                         current = (*i);
6436                                 } else {
6437                                         current = *(track_views.rbegin());
6438                                 }
6439                                 break;
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::set_loop_from_selection (bool play)
6458 {
6459         if (_session == 0) {
6460                 return;
6461         }
6462
6463         samplepos_t start, end;
6464         if (!get_selection_extents (start, end))
6465                 return;
6466
6467         set_loop_range (start, end,  _("set loop range from selection"));
6468
6469         if (play) {
6470                 _session->request_play_loop (true, true);
6471         }
6472 }
6473
6474 void
6475 Editor::set_loop_from_region (bool play)
6476 {
6477         samplepos_t start, end;
6478         if (!get_selection_extents (start, end))
6479                 return;
6480
6481         set_loop_range (start, end, _("set loop range from region"));
6482
6483         if (play) {
6484                 _session->request_locate (start, true);
6485                 _session->request_play_loop (true);
6486         }
6487 }
6488
6489 void
6490 Editor::set_punch_from_selection ()
6491 {
6492         if (_session == 0) {
6493                 return;
6494         }
6495
6496         samplepos_t start, end;
6497         if (!get_selection_extents (start, end))
6498                 return;
6499
6500         set_punch_range (start, end,  _("set punch range from selection"));
6501 }
6502
6503 void
6504 Editor::set_auto_punch_range ()
6505 {
6506         // auto punch in/out button from a single button
6507         // If Punch In is unset, set punch range from playhead to end, enable punch in
6508         // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6509         //   rewound beyond the Punch In marker, in which case that marker will be moved back
6510         //   to the current playhead position.
6511         // If punch out is set, it clears the punch range and Punch In/Out buttons
6512
6513         if (_session == 0) {
6514                 return;
6515         }
6516
6517         Location* tpl = transport_punch_location();
6518         samplepos_t now = playhead_cursor->current_sample();
6519         samplepos_t begin = now;
6520         samplepos_t end = _session->current_end_sample();
6521
6522         if (!_session->config.get_punch_in()) {
6523                 // First Press - set punch in and create range from here to eternity
6524                 set_punch_range (begin, end, _("Auto Punch In"));
6525                 _session->config.set_punch_in(true);
6526         } else if (tpl && !_session->config.get_punch_out()) {
6527                 // Second press - update end range marker and set punch_out
6528                 if (now < tpl->start()) {
6529                         // playhead has been rewound - move start back  and pretend nothing happened
6530                         begin = now;
6531                         set_punch_range (begin, end, _("Auto Punch In/Out"));
6532                 } else {
6533                         // normal case for 2nd press - set the punch out
6534                         end = playhead_cursor->current_sample ();
6535                         set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6536                         _session->config.set_punch_out(true);
6537                 }
6538         } else  {
6539                 if (_session->config.get_punch_out()) {
6540                         _session->config.set_punch_out(false);
6541                 }
6542
6543                 if (_session->config.get_punch_in()) {
6544                         _session->config.set_punch_in(false);
6545                 }
6546
6547                 if (tpl)
6548                 {
6549                         // third press - unset punch in/out and remove range
6550                         _session->locations()->remove(tpl);
6551                 }
6552         }
6553
6554 }
6555
6556 void
6557 Editor::set_session_extents_from_selection ()
6558 {
6559         if (_session == 0) {
6560                 return;
6561         }
6562
6563         samplepos_t start, end;
6564         if (!get_selection_extents (start, end))
6565                 return;
6566
6567         Location* loc;
6568         if ((loc = _session->locations()->session_range_location()) == 0) {
6569                 _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6570         } else {
6571                 XMLNode &before = loc->get_state();
6572
6573                 _session->set_session_extents (start, end);
6574
6575                 XMLNode &after = loc->get_state();
6576
6577                 begin_reversible_command (_("set session start/end from selection"));
6578
6579                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6580
6581                 commit_reversible_command ();
6582         }
6583
6584         _session->set_end_is_free (false);
6585 }
6586
6587 void
6588 Editor::set_punch_start_from_edit_point ()
6589 {
6590         if (_session) {
6591
6592                 MusicSample start (0, 0);
6593                 samplepos_t end = max_samplepos;
6594
6595                 //use the existing punch end, if any
6596                 Location* tpl = transport_punch_location();
6597                 if (tpl) {
6598                         end = tpl->end();
6599                 }
6600
6601                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6602                         start.sample = _session->audible_sample();
6603                 } else {
6604                         start.sample = get_preferred_edit_position();
6605                 }
6606
6607                 //if there's not already a sensible selection endpoint, go "forever"
6608                 if (start.sample > end) {
6609                         end = max_samplepos;
6610                 }
6611
6612                 set_punch_range (start.sample, end, _("set punch start from EP"));
6613         }
6614
6615 }
6616
6617 void
6618 Editor::set_punch_end_from_edit_point ()
6619 {
6620         if (_session) {
6621
6622                 samplepos_t start = 0;
6623                 MusicSample end (max_samplepos, 0);
6624
6625                 //use the existing punch start, if any
6626                 Location* tpl = transport_punch_location();
6627                 if (tpl) {
6628                         start = tpl->start();
6629                 }
6630
6631                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6632                         end.sample = _session->audible_sample();
6633                 } else {
6634                         end.sample = get_preferred_edit_position();
6635                 }
6636
6637                 set_punch_range (start, end.sample, _("set punch end from EP"));
6638
6639         }
6640 }
6641
6642 void
6643 Editor::set_loop_start_from_edit_point ()
6644 {
6645         if (_session) {
6646
6647                 MusicSample start (0, 0);
6648                 samplepos_t end = max_samplepos;
6649
6650                 //use the existing loop end, if any
6651                 Location* tpl = transport_loop_location();
6652                 if (tpl) {
6653                         end = tpl->end();
6654                 }
6655
6656                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6657                         start.sample = _session->audible_sample();
6658                 } else {
6659                         start.sample = get_preferred_edit_position();
6660                 }
6661
6662                 //if there's not already a sensible selection endpoint, go "forever"
6663                 if (start.sample > end) {
6664                         end = max_samplepos;
6665                 }
6666
6667                 set_loop_range (start.sample, end, _("set loop start from EP"));
6668         }
6669
6670 }
6671
6672 void
6673 Editor::set_loop_end_from_edit_point ()
6674 {
6675         if (_session) {
6676
6677                 samplepos_t start = 0;
6678                 MusicSample end (max_samplepos, 0);
6679
6680                 //use the existing loop start, if any
6681                 Location* tpl = transport_loop_location();
6682                 if (tpl) {
6683                         start = tpl->start();
6684                 }
6685
6686                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6687                         end.sample = _session->audible_sample();
6688                 } else {
6689                         end.sample = get_preferred_edit_position();
6690                 }
6691
6692                 set_loop_range (start, end.sample, _("set loop end from EP"));
6693         }
6694 }
6695
6696 void
6697 Editor::set_punch_from_region ()
6698 {
6699         samplepos_t start, end;
6700         if (!get_selection_extents (start, end))
6701                 return;
6702
6703         set_punch_range (start, end, _("set punch range from region"));
6704 }
6705
6706 void
6707 Editor::pitch_shift_region ()
6708 {
6709         RegionSelection rs = get_regions_from_selection_and_entered ();
6710
6711         RegionSelection audio_rs;
6712         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6713                 if (dynamic_cast<AudioRegionView*> (*i)) {
6714                         audio_rs.push_back (*i);
6715                 }
6716         }
6717
6718         if (audio_rs.empty()) {
6719                 return;
6720         }
6721
6722         pitch_shift (audio_rs, 1.2);
6723 }
6724
6725 void
6726 Editor::set_tempo_from_region ()
6727 {
6728         RegionSelection rs = get_regions_from_selection_and_entered ();
6729
6730         if (!_session || rs.empty()) {
6731                 return;
6732         }
6733
6734         RegionView* rv = rs.front();
6735
6736         define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6737 }
6738
6739 void
6740 Editor::use_range_as_bar ()
6741 {
6742         samplepos_t start, end;
6743         if (get_edit_op_range (start, end)) {
6744                 define_one_bar (start, end);
6745         }
6746 }
6747
6748 void
6749 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6750 {
6751         samplepos_t length = end - start;
6752
6753         const Meter& m (_session->tempo_map().meter_at_sample (start));
6754
6755         /* length = 1 bar */
6756
6757         /* We're going to deliver a constant tempo here,
6758            so we can use samples per beat to determine length.
6759            now we want samples per beat.
6760            we have samples per bar, and beats per bar, so ...
6761         */
6762
6763         /* XXXX METER MATH */
6764
6765         double samples_per_beat = length / m.divisions_per_bar();
6766
6767         /* beats per minute = */
6768
6769         double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6770
6771         /* now decide whether to:
6772
6773             (a) set global tempo
6774             (b) add a new tempo marker
6775
6776         */
6777
6778         const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6779
6780         bool do_global = false;
6781
6782         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6783
6784                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6785                    at the start, or create a new marker
6786                 */
6787
6788                 vector<string> options;
6789                 options.push_back (_("Cancel"));
6790                 options.push_back (_("Add new marker"));
6791                 options.push_back (_("Set global tempo"));
6792
6793                 Choice c (
6794                         _("Define one bar"),
6795                         _("Do you want to set the global tempo or add a new tempo marker?"),
6796                         options
6797                         );
6798
6799                 c.set_default_response (2);
6800
6801                 switch (c.run()) {
6802                 case 0:
6803                         return;
6804
6805                 case 2:
6806                         do_global = true;
6807                         break;
6808
6809                 default:
6810                         do_global = false;
6811                 }
6812
6813         } else {
6814
6815                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6816                    if the marker is at the region starter, change it, otherwise add
6817                    a new tempo marker
6818                 */
6819         }
6820
6821         begin_reversible_command (_("set tempo from region"));
6822         XMLNode& before (_session->tempo_map().get_state());
6823
6824         if (do_global) {
6825                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6826         } else if (t.sample() == start) {
6827                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6828         } else {
6829                 /* constant tempo */
6830                 const Tempo tempo (beats_per_minute, t.note_type());
6831                 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6832         }
6833
6834         XMLNode& after (_session->tempo_map().get_state());
6835
6836         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6837         commit_reversible_command ();
6838 }
6839
6840 void
6841 Editor::split_region_at_transients ()
6842 {
6843         AnalysisFeatureList positions;
6844
6845         RegionSelection rs = get_regions_from_selection_and_entered ();
6846
6847         if (!_session || rs.empty()) {
6848                 return;
6849         }
6850
6851         begin_reversible_command (_("split regions"));
6852
6853         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6854
6855                 RegionSelection::iterator tmp;
6856
6857                 tmp = i;
6858                 ++tmp;
6859
6860                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6861
6862                 if (ar) {
6863                         ar->transients (positions);
6864                         split_region_at_points ((*i)->region(), positions, true);
6865                         positions.clear ();
6866                 }
6867
6868                 i = tmp;
6869         }
6870
6871         commit_reversible_command ();
6872
6873 }
6874
6875 void
6876 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6877 {
6878         bool use_rhythmic_rodent = false;
6879
6880         boost::shared_ptr<Playlist> pl = r->playlist();
6881
6882         list<boost::shared_ptr<Region> > new_regions;
6883
6884         if (!pl) {
6885                 return;
6886         }
6887
6888         if (positions.empty()) {
6889                 return;
6890         }
6891
6892         if (positions.size() > 20 && can_ferret) {
6893                 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);
6894                 MessageDialog msg (msgstr,
6895                                    false,
6896                                    Gtk::MESSAGE_INFO,
6897                                    Gtk::BUTTONS_OK_CANCEL);
6898
6899                 if (can_ferret) {
6900                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6901                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6902                 } else {
6903                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6904                 }
6905
6906                 msg.set_title (_("Excessive split?"));
6907                 msg.present ();
6908
6909                 int response = msg.run();
6910                 msg.hide ();
6911
6912                 switch (response) {
6913                 case RESPONSE_OK:
6914                         break;
6915                 case RESPONSE_APPLY:
6916                         use_rhythmic_rodent = true;
6917                         break;
6918                 default:
6919                         return;
6920                 }
6921         }
6922
6923         if (use_rhythmic_rodent) {
6924                 show_rhythm_ferret ();
6925                 return;
6926         }
6927
6928         AnalysisFeatureList::const_iterator x;
6929
6930         pl->clear_changes ();
6931         pl->clear_owned_changes ();
6932
6933         x = positions.begin();
6934
6935         if (x == positions.end()) {
6936                 return;
6937         }
6938
6939         pl->freeze ();
6940         pl->remove_region (r);
6941
6942         samplepos_t pos = 0;
6943
6944         samplepos_t rstart = r->first_sample ();
6945         samplepos_t rend = r->last_sample ();
6946
6947         while (x != positions.end()) {
6948
6949                 /* deal with positons that are out of scope of present region bounds */
6950                 if (*x <= rstart || *x > rend) {
6951                         ++x;
6952                         continue;
6953                 }
6954
6955                 /* file start = original start + how far we from the initial position ?  */
6956
6957                 samplepos_t file_start = r->start() + pos;
6958
6959                 /* length = next position - current position */
6960
6961                 samplepos_t len = (*x) - pos - rstart;
6962
6963                 /* XXX we do we really want to allow even single-sample regions?
6964                  * shouldn't we have some kind of lower limit on region size?
6965                  */
6966
6967                 if (len <= 0) {
6968                         break;
6969                 }
6970
6971                 string new_name;
6972
6973                 if (RegionFactory::region_name (new_name, r->name())) {
6974                         break;
6975                 }
6976
6977                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6978
6979                 PropertyList plist;
6980
6981                 plist.add (ARDOUR::Properties::start, file_start);
6982                 plist.add (ARDOUR::Properties::length, len);
6983                 plist.add (ARDOUR::Properties::name, new_name);
6984                 plist.add (ARDOUR::Properties::layer, 0);
6985                 // TODO set transients_offset
6986
6987                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6988                 /* because we set annouce to false, manually add the new region to the
6989                  * RegionFactory map
6990                  */
6991                 RegionFactory::map_add (nr);
6992
6993                 pl->add_region (nr, rstart + pos);
6994
6995                 if (select_new) {
6996                         new_regions.push_front(nr);
6997                 }
6998
6999                 pos += len;
7000                 ++x;
7001         }
7002
7003         string new_name;
7004
7005         RegionFactory::region_name (new_name, r->name());
7006
7007         /* Add the final region */
7008         PropertyList plist;
7009
7010         plist.add (ARDOUR::Properties::start, r->start() + pos);
7011         plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7012         plist.add (ARDOUR::Properties::name, new_name);
7013         plist.add (ARDOUR::Properties::layer, 0);
7014
7015         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7016         /* because we set annouce to false, manually add the new region to the
7017            RegionFactory map
7018         */
7019         RegionFactory::map_add (nr);
7020         pl->add_region (nr, r->position() + pos);
7021
7022         if (select_new) {
7023                 new_regions.push_front(nr);
7024         }
7025
7026         pl->thaw ();
7027
7028         /* We might have removed regions, which alters other regions' layering_index,
7029            so we need to do a recursive diff here.
7030         */
7031         vector<Command*> cmds;
7032         pl->rdiff (cmds);
7033         _session->add_commands (cmds);
7034
7035         _session->add_command (new StatefulDiffCommand (pl));
7036
7037         if (select_new) {
7038
7039                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7040                         set_selected_regionview_from_region_list ((*i), Selection::Add);
7041                 }
7042         }
7043 }
7044
7045 void
7046 Editor::place_transient()
7047 {
7048         if (!_session) {
7049                 return;
7050         }
7051
7052         RegionSelection rs = get_regions_from_selection_and_edit_point ();
7053
7054         if (rs.empty()) {
7055                 return;
7056         }
7057
7058         samplepos_t where = get_preferred_edit_position();
7059
7060         begin_reversible_command (_("place transient"));
7061
7062         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7063                 (*r)->region()->add_transient(where);
7064         }
7065
7066         commit_reversible_command ();
7067 }
7068
7069 void
7070 Editor::remove_transient(ArdourCanvas::Item* item)
7071 {
7072         if (!_session) {
7073                 return;
7074         }
7075
7076         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7077         assert (_line);
7078
7079         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7080         _arv->remove_transient (*(float*) _line->get_data ("position"));
7081 }
7082
7083 void
7084 Editor::snap_regions_to_grid ()
7085 {
7086         list <boost::shared_ptr<Playlist > > used_playlists;
7087
7088         RegionSelection rs = get_regions_from_selection_and_entered ();
7089
7090         if (!_session || rs.empty()) {
7091                 return;
7092         }
7093
7094         begin_reversible_command (_("snap regions to grid"));
7095
7096         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7097
7098                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7099
7100                 if (!pl->frozen()) {
7101                         /* we haven't seen this playlist before */
7102
7103                         /* remember used playlists so we can thaw them later */
7104                         used_playlists.push_back(pl);
7105                         pl->freeze();
7106                 }
7107                 (*r)->region()->clear_changes ();
7108
7109                 MusicSample start ((*r)->region()->first_sample (), 0);
7110                 snap_to (start, RoundNearest, SnapToGrid);
7111                 (*r)->region()->set_position (start.sample, start.division);
7112                 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7113         }
7114
7115         while (used_playlists.size() > 0) {
7116                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7117                 (*i)->thaw();
7118                 used_playlists.pop_front();
7119         }
7120
7121         commit_reversible_command ();
7122 }
7123
7124 void
7125 Editor::close_region_gaps ()
7126 {
7127         list <boost::shared_ptr<Playlist > > used_playlists;
7128
7129         RegionSelection rs = get_regions_from_selection_and_entered ();
7130
7131         if (!_session || rs.empty()) {
7132                 return;
7133         }
7134
7135         Dialog dialog (_("Close Region Gaps"));
7136
7137         Table table (2, 3);
7138         table.set_spacings (12);
7139         table.set_border_width (12);
7140         Label* l = manage (left_aligned_label (_("Crossfade length")));
7141         table.attach (*l, 0, 1, 0, 1);
7142
7143         SpinButton spin_crossfade (1, 0);
7144         spin_crossfade.set_range (0, 15);
7145         spin_crossfade.set_increments (1, 1);
7146         spin_crossfade.set_value (5);
7147         table.attach (spin_crossfade, 1, 2, 0, 1);
7148
7149         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7150
7151         l = manage (left_aligned_label (_("Pull-back length")));
7152         table.attach (*l, 0, 1, 1, 2);
7153
7154         SpinButton spin_pullback (1, 0);
7155         spin_pullback.set_range (0, 100);
7156         spin_pullback.set_increments (1, 1);
7157         spin_pullback.set_value(30);
7158         table.attach (spin_pullback, 1, 2, 1, 2);
7159
7160         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7161
7162         dialog.get_vbox()->pack_start (table);
7163         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7164         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7165         dialog.show_all ();
7166
7167         if (dialog.run () == RESPONSE_CANCEL) {
7168                 return;
7169         }
7170
7171         samplepos_t crossfade_len = spin_crossfade.get_value();
7172         samplepos_t pull_back_samples = spin_pullback.get_value();
7173
7174         crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7175         pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7176
7177         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7178
7179         begin_reversible_command (_("close region gaps"));
7180
7181         int idx = 0;
7182         boost::shared_ptr<Region> last_region;
7183
7184         rs.sort_by_position_and_track();
7185
7186         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7187
7188                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7189
7190                 if (!pl->frozen()) {
7191                         /* we haven't seen this playlist before */
7192
7193                         /* remember used playlists so we can thaw them later */
7194                         used_playlists.push_back(pl);
7195                         pl->freeze();
7196                 }
7197
7198                 samplepos_t position = (*r)->region()->position();
7199
7200                 if (idx == 0 || position < last_region->position()){
7201                         last_region = (*r)->region();
7202                         idx++;
7203                         continue;
7204                 }
7205
7206                 (*r)->region()->clear_changes ();
7207                 (*r)->region()->trim_front((position - pull_back_samples));
7208
7209                 last_region->clear_changes ();
7210                 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7211
7212                 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7213                 _session->add_command (new StatefulDiffCommand (last_region));
7214
7215                 last_region = (*r)->region();
7216                 idx++;
7217         }
7218
7219         while (used_playlists.size() > 0) {
7220                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7221                 (*i)->thaw();
7222                 used_playlists.pop_front();
7223         }
7224
7225         commit_reversible_command ();
7226 }
7227
7228 void
7229 Editor::tab_to_transient (bool forward)
7230 {
7231         AnalysisFeatureList positions;
7232
7233         RegionSelection rs = get_regions_from_selection_and_entered ();
7234
7235         if (!_session) {
7236                 return;
7237         }
7238
7239         samplepos_t pos = _session->audible_sample ();
7240
7241         if (!selection->tracks.empty()) {
7242
7243                 /* don't waste time searching for transients in duplicate playlists.
7244                  */
7245
7246                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7247
7248                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7249
7250                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7251
7252                         if (rtv) {
7253                                 boost::shared_ptr<Track> tr = rtv->track();
7254                                 if (tr) {
7255                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
7256                                         if (pl) {
7257                                                 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7258
7259                                                 if (result >= 0) {
7260                                                         positions.push_back (result);
7261                                                 }
7262                                         }
7263                                 }
7264                         }
7265                 }
7266
7267         } else {
7268
7269                 if (rs.empty()) {
7270                         return;
7271                 }
7272
7273                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7274                         (*r)->region()->get_transients (positions);
7275                 }
7276         }
7277
7278         TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7279
7280         if (forward) {
7281                 AnalysisFeatureList::iterator x;
7282
7283                 for (x = positions.begin(); x != positions.end(); ++x) {
7284                         if ((*x) > pos) {
7285                                 break;
7286                         }
7287                 }
7288
7289                 if (x != positions.end ()) {
7290                         _session->request_locate (*x);
7291                 }
7292
7293         } else {
7294                 AnalysisFeatureList::reverse_iterator x;
7295
7296                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7297                         if ((*x) < pos) {
7298                                 break;
7299                         }
7300                 }
7301
7302                 if (x != positions.rend ()) {
7303                         _session->request_locate (*x);
7304                 }
7305         }
7306 }
7307
7308 void
7309 Editor::playhead_forward_to_grid ()
7310 {
7311         if (!_session) {
7312                 return;
7313         }
7314
7315         MusicSample pos (playhead_cursor->current_sample (), 0);
7316
7317         if (pos.sample < max_samplepos - 1) {
7318                 pos.sample += 2;
7319                 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7320                 _session->request_locate (pos.sample);
7321         }
7322 }
7323
7324
7325 void
7326 Editor::playhead_backward_to_grid ()
7327 {
7328         if (!_session) {
7329                 return;
7330         }
7331
7332         MusicSample pos  (playhead_cursor->current_sample (), 0);
7333
7334         if (pos.sample > 2) {
7335                 pos.sample -= 2;
7336                 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7337                 _session->request_locate (pos.sample);
7338         }
7339 }
7340
7341 void
7342 Editor::set_track_height (Height h)
7343 {
7344         TrackSelection& ts (selection->tracks);
7345
7346         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7347                 (*x)->set_height_enum (h);
7348         }
7349 }
7350
7351 void
7352 Editor::toggle_tracks_active ()
7353 {
7354         TrackSelection& ts (selection->tracks);
7355         bool first = true;
7356         bool target = false;
7357
7358         if (ts.empty()) {
7359                 return;
7360         }
7361
7362         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7363                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7364
7365                 if (rtv) {
7366                         if (first) {
7367                                 target = !rtv->_route->active();
7368                                 first = false;
7369                         }
7370                         rtv->_route->set_active (target, this);
7371                 }
7372         }
7373 }
7374
7375 void
7376 Editor::remove_tracks ()
7377 {
7378         /* this will delete GUI objects that may be the subject of an event
7379            handler in which this method is called. Defer actual deletion to the
7380            next idle callback, when all event handling is finished.
7381         */
7382         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7383 }
7384
7385 bool
7386 Editor::idle_remove_tracks ()
7387 {
7388         Session::StateProtector sp (_session);
7389         _remove_tracks ();
7390         return false; /* do not call again */
7391 }
7392
7393 void
7394 Editor::_remove_tracks ()
7395 {
7396         TrackSelection& ts (selection->tracks);
7397
7398         if (ts.empty()) {
7399                 return;
7400         }
7401
7402         vector<string> choices;
7403         string prompt;
7404         int ntracks = 0;
7405         int nbusses = 0;
7406         int nvcas = 0;
7407         const char* trackstr;
7408         const char* busstr;
7409         const char* vcastr;
7410         vector<boost::shared_ptr<Route> > routes;
7411         vector<boost::shared_ptr<VCA> > vcas;
7412         bool special_bus = false;
7413
7414         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7415                 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7416                 if (vtv) {
7417                         vcas.push_back (vtv->vca());
7418                         ++nvcas;
7419                         continue;
7420                 }
7421                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7422                 if (!rtv) {
7423                         continue;
7424                 }
7425                 if (rtv->is_track()) {
7426                         ++ntracks;
7427                 } else {
7428                         ++nbusses;
7429                 }
7430                 routes.push_back (rtv->_route);
7431
7432                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7433                         special_bus = true;
7434                 }
7435         }
7436
7437         if (special_bus && !Config->get_allow_special_bus_removal()) {
7438                 MessageDialog msg (_("That would be bad news ...."),
7439                                    false,
7440                                    Gtk::MESSAGE_INFO,
7441                                    Gtk::BUTTONS_OK);
7442                 msg.set_secondary_text (string_compose (_(
7443                                                                 "Removing the master or monitor bus is such a bad idea\n\
7444 that %1 is not going to allow it.\n\
7445 \n\
7446 If you really want to do this sort of thing\n\
7447 edit your ardour.rc file to set the\n\
7448 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7449
7450                 msg.present ();
7451                 msg.run ();
7452                 return;
7453         }
7454
7455         if (ntracks + nbusses + nvcas == 0) {
7456                 return;
7457         }
7458
7459         string title;
7460
7461         trackstr = P_("track", "tracks", ntracks);
7462         busstr = P_("bus", "busses", nbusses);
7463         vcastr = P_("VCA", "VCAs", nvcas);
7464
7465         if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7466                 title = _("Remove various strips");
7467                 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7468                                                   ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7469         }
7470         else if (ntracks > 0 && nbusses > 0) {
7471                 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7472                 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7473                                 ntracks, trackstr, nbusses, busstr);
7474         }
7475         else if (ntracks > 0 && nvcas > 0) {
7476                 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7477                 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7478                                 ntracks, trackstr, nvcas, vcastr);
7479         }
7480         else if (nbusses > 0 && nvcas > 0) {
7481                 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7482                 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7483                                 nbusses, busstr, nvcas, vcastr);
7484         }
7485         else if (ntracks > 0) {
7486                 title = string_compose (_("Remove %1"), trackstr);
7487                 prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7488                                 ntracks, trackstr);
7489         }
7490         else if (nbusses > 0) {
7491                 title = string_compose (_("Remove %1"), busstr);
7492                 prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7493                                 nbusses, busstr);
7494         }
7495         else if (nvcas > 0) {
7496                 title = string_compose (_("Remove %1"), vcastr);
7497                 prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7498                                 nvcas, vcastr);
7499         }
7500         else {
7501                 assert (0);
7502         }
7503
7504         if (ntracks > 0) {
7505                         prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7506         }
7507
7508         prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7509
7510         choices.push_back (_("No, do nothing."));
7511         if (ntracks + nbusses + nvcas > 1) {
7512                 choices.push_back (_("Yes, remove them."));
7513         } else {
7514                 choices.push_back (_("Yes, remove it."));
7515         }
7516
7517         Choice prompter (title, prompt, choices);
7518
7519         if (prompter.run () != 1) {
7520                 return;
7521         }
7522
7523         if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7524                 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7525                  * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7526                  * likely because deletion requires selection) this will call
7527                  * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7528                  * It's likewise likely that the route that has just been displayed in the
7529                  * Editor-Mixer will be next in line for deletion.
7530                  *
7531                  * So simply switch to the master-bus (if present)
7532                  */
7533                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7534                         if ((*i)->stripable ()->is_master ()) {
7535                                 set_selected_mixer_strip (*(*i));
7536                                 break;
7537                         }
7538                 }
7539         }
7540
7541         {
7542                 PresentationInfo::ChangeSuspender cs;
7543                 DisplaySuspender ds;
7544
7545                 boost::shared_ptr<RouteList> rl (new RouteList);
7546                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7547                         rl->push_back (*x);
7548                 }
7549                 _session->remove_routes (rl);
7550
7551                 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7552                         _session->vca_manager().remove_vca (*x);
7553                 }
7554
7555         }
7556         /* TrackSelection and RouteList leave scope,
7557          * destructors are called,
7558          * diskstream drops references, save_state is called (again for every track)
7559          */
7560 }
7561
7562 void
7563 Editor::do_insert_time ()
7564 {
7565         if (selection->tracks.empty()) {
7566                 MessageDialog msg (_("You must first select some tracks to Insert Time."),
7567                                    true, MESSAGE_INFO, BUTTONS_OK, true);
7568                 msg.set_position (WIN_POS_MOUSE);
7569                 msg.run ();
7570                 return;
7571         }
7572
7573         if (Config->get_edit_mode() == Lock) {
7574                 MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7575                                    true, MESSAGE_INFO, BUTTONS_OK, true);
7576                 msg.set_position (WIN_POS_MOUSE);
7577                 msg.run ();
7578                 return;
7579         }
7580
7581         InsertRemoveTimeDialog d (*this);
7582         int response = d.run ();
7583
7584         if (response != RESPONSE_OK) {
7585                 return;
7586         }
7587
7588         if (d.distance() == 0) {
7589                 return;
7590         }
7591
7592         insert_time (
7593                 d.position(),
7594                 d.distance(),
7595                 d.intersected_region_action (),
7596                 d.all_playlists(),
7597                 d.move_glued(),
7598                 d.move_markers(),
7599                 d.move_glued_markers(),
7600                 d.move_locked_markers(),
7601                 d.move_tempos()
7602                 );
7603 }
7604
7605 void
7606 Editor::insert_time (
7607         samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7608         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7609         )
7610 {
7611
7612         if (Config->get_edit_mode() == Lock) {
7613                 return;
7614         }
7615         bool in_command = false;
7616
7617         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7618
7619         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7620
7621                 /* regions */
7622
7623                 /* don't operate on any playlist more than once, which could
7624                  * happen if "all playlists" is enabled, but there is more
7625                  * than 1 track using playlists "from" a given track.
7626                  */
7627
7628                 set<boost::shared_ptr<Playlist> > pl;
7629
7630                 if (all_playlists) {
7631                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7632                         if (rtav && rtav->track ()) {
7633                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7634                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7635                                         pl.insert (*p);
7636                                 }
7637                         }
7638                 } else {
7639                         if ((*x)->playlist ()) {
7640                                 pl.insert ((*x)->playlist ());
7641                         }
7642                 }
7643
7644                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7645
7646                         (*i)->clear_changes ();
7647                         (*i)->clear_owned_changes ();
7648
7649                         if (!in_command) {
7650                                 begin_reversible_command (_("insert time"));
7651                                 in_command = true;
7652                         }
7653
7654                         if (opt == SplitIntersected) {
7655                                 /* non musical split */
7656                                 (*i)->split (MusicSample (pos, 0));
7657                         }
7658
7659                         (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7660
7661                         vector<Command*> cmds;
7662                         (*i)->rdiff (cmds);
7663                         _session->add_commands (cmds);
7664
7665                         _session->add_command (new StatefulDiffCommand (*i));
7666                 }
7667
7668                 /* automation */
7669                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7670                 if (rtav) {
7671                         if (!in_command) {
7672                                 begin_reversible_command (_("insert time"));
7673                                 in_command = true;
7674                         }
7675                         rtav->route ()->shift (pos, samples);
7676                 }
7677         }
7678
7679         /* markers */
7680         if (markers_too) {
7681                 bool moved = false;
7682                 const int32_t divisions = get_grid_music_divisions (0);
7683                 XMLNode& before (_session->locations()->get_state());
7684                 Locations::LocationList copy (_session->locations()->list());
7685
7686                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7687
7688                         Locations::LocationList::const_iterator tmp;
7689
7690                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7691                                 bool const was_locked = (*i)->locked ();
7692                                 if (locked_markers_too) {
7693                                         (*i)->unlock ();
7694                                 }
7695
7696                                 if ((*i)->start() >= pos) {
7697                                         // move end first, in case we're moving by more than the length of the range
7698                                         if (!(*i)->is_mark()) {
7699                                                 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7700                                         }
7701                                         (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7702                                         moved = true;
7703                                 }
7704
7705                                 if (was_locked) {
7706                                         (*i)->lock ();
7707                                 }
7708                         }
7709                 }
7710
7711                 if (moved) {
7712                         if (!in_command) {
7713                                 begin_reversible_command (_("insert time"));
7714                                 in_command = true;
7715                         }
7716                         XMLNode& after (_session->locations()->get_state());
7717                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7718                 }
7719         }
7720
7721         if (tempo_too) {
7722                 if (!in_command) {
7723                         begin_reversible_command (_("insert time"));
7724                         in_command = true;
7725                 }
7726                 XMLNode& before (_session->tempo_map().get_state());
7727                 _session->tempo_map().insert_time (pos, samples);
7728                 XMLNode& after (_session->tempo_map().get_state());
7729                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7730         }
7731
7732         if (in_command) {
7733                 commit_reversible_command ();
7734         }
7735 }
7736
7737 void
7738 Editor::do_remove_time ()
7739 {
7740         if (selection->tracks.empty()) {
7741                 MessageDialog msg (_("You must first select some tracks to Remove Time."),
7742                                    true, MESSAGE_INFO, BUTTONS_OK, true);
7743                 msg.set_position (WIN_POS_MOUSE);
7744                 msg.run ();
7745                 return;
7746         }
7747
7748         if (Config->get_edit_mode() == Lock) {
7749                 MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
7750                                    true, MESSAGE_INFO, BUTTONS_OK, true);
7751                 msg.set_position (WIN_POS_MOUSE);
7752                 msg.run ();
7753                 return;
7754         }
7755
7756         InsertRemoveTimeDialog d (*this, true);
7757
7758         int response = d.run ();
7759
7760         if (response != RESPONSE_OK) {
7761                 return;
7762         }
7763
7764         samplecnt_t distance = d.distance();
7765
7766         if (distance == 0) {
7767                 return;
7768         }
7769
7770         remove_time (
7771                 d.position(),
7772                 distance,
7773                 SplitIntersected,
7774                 d.move_glued(),
7775                 d.move_markers(),
7776                 d.move_glued_markers(),
7777                 d.move_locked_markers(),
7778                 d.move_tempos()
7779         );
7780 }
7781
7782 void
7783 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7784                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7785 {
7786         if (Config->get_edit_mode() == Lock) {
7787                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7788                 return;
7789         }
7790         bool in_command = false;
7791
7792         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7793                 /* regions */
7794                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7795
7796                 if (pl) {
7797
7798                         XMLNode &before = pl->get_state();
7799
7800                         if (!in_command) {
7801                                 begin_reversible_command (_("remove time"));
7802                                 in_command = true;
7803                         }
7804
7805                         std::list<AudioRange> rl;
7806                         AudioRange ar(pos, pos+samples, 0);
7807                         rl.push_back(ar);
7808                         pl->cut (rl);
7809                         pl->shift (pos, -samples, true, ignore_music_glue);
7810
7811                         XMLNode &after = pl->get_state();
7812
7813                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7814                 }
7815
7816                 /* automation */
7817                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7818                 if (rtav) {
7819                         if (!in_command) {
7820                                 begin_reversible_command (_("remove time"));
7821                                 in_command = true;
7822                         }
7823                         rtav->route ()->shift (pos, -samples);
7824                 }
7825         }
7826
7827         const int32_t divisions = get_grid_music_divisions (0);
7828         std::list<Location*> loc_kill_list;
7829
7830         /* markers */
7831         if (markers_too) {
7832                 bool moved = false;
7833                 XMLNode& before (_session->locations()->get_state());
7834                 Locations::LocationList copy (_session->locations()->list());
7835
7836                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7837                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7838
7839                                 bool const was_locked = (*i)->locked ();
7840                                 if (locked_markers_too) {
7841                                         (*i)->unlock ();
7842                                 }
7843
7844                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7845                                         if ((*i)->end() >= pos
7846                                         && (*i)->end() < pos+samples
7847                                         && (*i)->start() >= pos
7848                                         && (*i)->end() < pos+samples) {  // range is completely enclosed;  kill it
7849                                                 moved = true;
7850                                                 loc_kill_list.push_back(*i);
7851                                         } else {  // only start or end is included, try to do the right thing
7852                                                 // move start before moving end, to avoid trying to move the end to before the start
7853                                                 // if we're removing more time than the length of the range
7854                                                 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7855                                                         // start is within cut
7856                                                         (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
7857                                                         moved = true;
7858                                                 } else if ((*i)->start() >= pos+samples) {
7859                                                         // start (and thus entire range) lies beyond end of cut
7860                                                         (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7861                                                         moved = true;
7862                                                 }
7863                                                 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7864                                                         // end is inside cut
7865                                                         (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
7866                                                         moved = true;
7867                                                 } else if ((*i)->end() >= pos+samples) {
7868                                                         // end is beyond end of cut
7869                                                         (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7870                                                         moved = true;
7871                                                 }
7872
7873                                         }
7874                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7875                                         loc_kill_list.push_back(*i);
7876                                         moved = true;
7877                                 } else if ((*i)->start() >= pos) {
7878                                         (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7879                                         moved = true;
7880                                 }
7881
7882                                 if (was_locked) {
7883                                         (*i)->lock ();
7884                                 }
7885                         }
7886                 }
7887
7888                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7889                         _session->locations()->remove (*i);
7890                 }
7891
7892                 if (moved) {
7893                         if (!in_command) {
7894                                 begin_reversible_command (_("remove time"));
7895                                 in_command = true;
7896                         }
7897                         XMLNode& after (_session->locations()->get_state());
7898                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7899                 }
7900         }
7901
7902         if (tempo_too) {
7903                 XMLNode& before (_session->tempo_map().get_state());
7904
7905                 if (_session->tempo_map().remove_time (pos, samples)) {
7906                         if (!in_command) {
7907                                 begin_reversible_command (_("remove time"));
7908                                 in_command = true;
7909                         }
7910                         XMLNode& after (_session->tempo_map().get_state());
7911                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7912                 }
7913         }
7914
7915         if (in_command) {
7916                 commit_reversible_command ();
7917         }
7918 }
7919
7920 void
7921 Editor::fit_selection ()
7922 {
7923         if (!selection->tracks.empty()) {
7924                 fit_tracks (selection->tracks);
7925         } else {
7926                 TrackViewList tvl;
7927
7928                 /* no selected tracks - use tracks with selected regions */
7929
7930                 if (!selection->regions.empty()) {
7931                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7932                                 tvl.push_back (&(*r)->get_time_axis_view ());
7933                         }
7934
7935                         if (!tvl.empty()) {
7936                                 fit_tracks (tvl);
7937                         }
7938                 } else if (internal_editing()) {
7939                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7940                          * the entered track
7941                          */
7942                         if (entered_track) {
7943                                 tvl.push_back (entered_track);
7944                                 fit_tracks (tvl);
7945                         }
7946                 }
7947         }
7948 }
7949
7950 void
7951 Editor::fit_tracks (TrackViewList & tracks)
7952 {
7953         if (tracks.empty()) {
7954                 return;
7955         }
7956
7957         uint32_t child_heights = 0;
7958         int visible_tracks = 0;
7959
7960         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7961
7962                 if (!(*t)->marked_for_display()) {
7963                         continue;
7964                 }
7965
7966                 child_heights += (*t)->effective_height() - (*t)->current_height();
7967                 ++visible_tracks;
7968         }
7969
7970         /* compute the per-track height from:
7971          *
7972          * total canvas visible height
7973          *  - height that will be taken by visible children of selected tracks
7974          *  - height of the ruler/hscroll area
7975          */
7976         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7977         double first_y_pos = DBL_MAX;
7978
7979         if (h < TimeAxisView::preset_height (HeightSmall)) {
7980                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7981                 /* too small to be displayed */
7982                 return;
7983         }
7984
7985         undo_visual_stack.push_back (current_visual_state (true));
7986         PBD::Unwinder<bool> nsv (no_save_visual, true);
7987
7988         /* build a list of all tracks, including children */
7989
7990         TrackViewList all;
7991         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7992                 all.push_back (*i);
7993                 TimeAxisView::Children c = (*i)->get_child_list ();
7994                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7995                         all.push_back (j->get());
7996                 }
7997         }
7998
7999
8000         // find selection range.
8001         // if someone knows how to user TrackViewList::iterator for this
8002         // I'm all ears.
8003         int selected_top = -1;
8004         int selected_bottom = -1;
8005         int i = 0;
8006         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8007                 if ((*t)->marked_for_display ()) {
8008                         if (tracks.contains(*t)) {
8009                                 if (selected_top == -1) {
8010                                         selected_top = i;
8011                                 }
8012                                 selected_bottom = i;
8013                         }
8014                 }
8015         }
8016
8017         i = 0;
8018         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8019                 if ((*t)->marked_for_display ()) {
8020                         if (tracks.contains(*t)) {
8021                                 (*t)->set_height (h);
8022                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8023                         } else {
8024                                 if (i > selected_top && i < selected_bottom) {
8025                                         hide_track_in_display (*t);
8026                                 }
8027                         }
8028                 }
8029         }
8030
8031         /*
8032            set the controls_layout height now, because waiting for its size
8033            request signal handler will cause the vertical adjustment setting to fail
8034         */
8035
8036         controls_layout.property_height () = _full_canvas_height;
8037         vertical_adjustment.set_value (first_y_pos);
8038
8039         redo_visual_stack.push_back (current_visual_state (true));
8040
8041         visible_tracks_selector.set_text (_("Sel"));
8042 }
8043
8044 void
8045 Editor::save_visual_state (uint32_t n)
8046 {
8047         while (visual_states.size() <= n) {
8048                 visual_states.push_back (0);
8049         }
8050
8051         if (visual_states[n] != 0) {
8052                 delete visual_states[n];
8053         }
8054
8055         visual_states[n] = current_visual_state (true);
8056         gdk_beep ();
8057 }
8058
8059 void
8060 Editor::goto_visual_state (uint32_t n)
8061 {
8062         if (visual_states.size() <= n) {
8063                 return;
8064         }
8065
8066         if (visual_states[n] == 0) {
8067                 return;
8068         }
8069
8070         use_visual_state (*visual_states[n]);
8071 }
8072
8073 void
8074 Editor::start_visual_state_op (uint32_t n)
8075 {
8076         save_visual_state (n);
8077
8078         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8079         char buf[32];
8080         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8081         pup->set_text (buf);
8082         pup->touch();
8083 }
8084
8085 void
8086 Editor::cancel_visual_state_op (uint32_t n)
8087 {
8088         goto_visual_state (n);
8089 }
8090
8091 void
8092 Editor::toggle_region_mute ()
8093 {
8094         if (_ignore_region_action) {
8095                 return;
8096         }
8097
8098         RegionSelection rs = get_regions_from_selection_and_entered ();
8099
8100         if (rs.empty ()) {
8101                 return;
8102         }
8103
8104         if (rs.size() > 1) {
8105                 begin_reversible_command (_("mute regions"));
8106         } else {
8107                 begin_reversible_command (_("mute region"));
8108         }
8109
8110         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8111
8112                 (*i)->region()->playlist()->clear_changes ();
8113                 (*i)->region()->set_muted (!(*i)->region()->muted ());
8114                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8115
8116         }
8117
8118         commit_reversible_command ();
8119 }
8120
8121 void
8122 Editor::combine_regions ()
8123 {
8124         /* foreach track with selected regions, take all selected regions
8125            and join them into a new region containing the subregions (as a
8126            playlist)
8127         */
8128
8129         typedef set<RouteTimeAxisView*> RTVS;
8130         RTVS tracks;
8131
8132         if (selection->regions.empty()) {
8133                 return;
8134         }
8135
8136         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8137                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8138
8139                 if (rtv) {
8140                         tracks.insert (rtv);
8141                 }
8142         }
8143
8144         begin_reversible_command (_("combine regions"));
8145
8146         vector<RegionView*> new_selection;
8147
8148         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8149                 RegionView* rv;
8150
8151                 if ((rv = (*i)->combine_regions ()) != 0) {
8152                         new_selection.push_back (rv);
8153                 }
8154         }
8155
8156         selection->clear_regions ();
8157         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8158                 selection->add (*i);
8159         }
8160
8161         commit_reversible_command ();
8162 }
8163
8164 void
8165 Editor::uncombine_regions ()
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 (_("uncombine regions"));
8183
8184         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8185                 (*i)->uncombine_regions ();
8186         }
8187
8188         commit_reversible_command ();
8189 }
8190
8191 void
8192 Editor::toggle_midi_input_active (bool flip_others)
8193 {
8194         bool onoff = false;
8195         boost::shared_ptr<RouteList> rl (new RouteList);
8196
8197         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8198                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8199
8200                 if (!rtav) {
8201                         continue;
8202                 }
8203
8204                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8205
8206                 if (mt) {
8207                         rl->push_back (rtav->route());
8208                         onoff = !mt->input_active();
8209                 }
8210         }
8211
8212         _session->set_exclusive_input_active (rl, onoff, flip_others);
8213 }
8214
8215 static bool ok_fine (GdkEventAny*) { return true; }
8216
8217 void
8218 Editor::lock ()
8219 {
8220         if (!lock_dialog) {
8221                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8222
8223                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8224                 lock_dialog->get_vbox()->pack_start (*padlock);
8225                 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8226
8227                 ArdourButton* b = manage (new ArdourButton);
8228                 b->set_name ("lock button");
8229                 b->set_text (_("Click to unlock"));
8230                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8231                 lock_dialog->get_vbox()->pack_start (*b);
8232
8233                 lock_dialog->get_vbox()->show_all ();
8234                 lock_dialog->set_size_request (200, 200);
8235         }
8236
8237         delete _main_menu_disabler;
8238         _main_menu_disabler = new MainMenuDisabler;
8239
8240         lock_dialog->present ();
8241
8242         lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8243 }
8244
8245 void
8246 Editor::unlock ()
8247 {
8248         lock_dialog->hide ();
8249
8250         delete _main_menu_disabler;
8251         _main_menu_disabler = 0;
8252
8253         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8254                 start_lock_event_timing ();
8255         }
8256 }
8257
8258 void
8259 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8260 {
8261         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8262 }
8263
8264 void
8265 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8266 {
8267         Timers::TimerSuspender t;
8268         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8269         Gtkmm2ext::UI::instance()->flush_pending (1);
8270 }
8271
8272 void
8273 Editor::bring_all_sources_into_session ()
8274 {
8275         if (!_session) {
8276                 return;
8277         }
8278
8279         Gtk::Label msg;
8280         ArdourDialog w (_("Moving embedded files into session folder"));
8281         w.get_vbox()->pack_start (msg);
8282         w.present ();
8283
8284         /* flush all pending GUI events because we're about to start copying
8285          * files
8286          */
8287
8288         Timers::TimerSuspender t;
8289         Gtkmm2ext::UI::instance()->flush_pending (3);
8290
8291         cerr << " Do it\n";
8292
8293         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8294 }