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