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