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