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