Use a better pattern to avoid abort_reversible_command().
[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_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         QuantizeDialog* qd = new QuantizeDialog (*this);
5305
5306         qd->present ();
5307         const int r = qd->run ();
5308         qd->hide ();
5309
5310         if (r == Gtk::RESPONSE_OK) {
5311                 Quantize quant (qd->snap_start(), qd->snap_end(),
5312                                 qd->start_grid_size(), qd->end_grid_size(),
5313                                 qd->strength(), qd->swing(), qd->threshold());
5314
5315                 apply_midi_note_edit_op (quant, rs);
5316         }
5317 }
5318
5319 void
5320 Editor::legatize_region (bool shrink_only)
5321 {
5322         if (_session) {
5323                 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5324         }
5325 }
5326
5327 void
5328 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5329 {
5330         if (rs.n_midi_regions() == 0) {
5331                 return;
5332         }
5333
5334         Legatize legatize(shrink_only);
5335         apply_midi_note_edit_op (legatize, rs);
5336 }
5337
5338 void
5339 Editor::transform_region ()
5340 {
5341         if (_session) {
5342                 transform_regions(get_regions_from_selection_and_entered ());
5343         }
5344 }
5345
5346 void
5347 Editor::transform_regions (const RegionSelection& rs)
5348 {
5349         if (rs.n_midi_regions() == 0) {
5350                 return;
5351         }
5352
5353         TransformDialog* td = new TransformDialog();
5354
5355         td->present();
5356         const int r = td->run();
5357         td->hide();
5358
5359         if (r == Gtk::RESPONSE_OK) {
5360                 Transform transform(td->get());
5361                 apply_midi_note_edit_op(transform, rs);
5362         }
5363 }
5364
5365 void
5366 Editor::insert_patch_change (bool from_context)
5367 {
5368         RegionSelection rs = get_regions_from_selection_and_entered ();
5369
5370         if (rs.empty ()) {
5371                 return;
5372         }
5373
5374         const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5375
5376         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5377            there may be more than one, but the PatchChangeDialog can only offer
5378            one set of patch menus.
5379         */
5380         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5381
5382         Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5383         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5384
5385         if (d.run() == RESPONSE_CANCEL) {
5386                 return;
5387         }
5388
5389         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5390                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5391                 if (mrv) {
5392                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5393                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5394                         }
5395                 }
5396         }
5397 }
5398
5399 void
5400 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5401 {
5402         RegionSelection rs = get_regions_from_selection_and_entered ();
5403
5404         if (rs.empty()) {
5405                 return;
5406         }
5407
5408         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5409         bool in_command = false;
5410
5411         gdk_flush ();
5412
5413         int n = 0;
5414         int const N = rs.size ();
5415
5416         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5417                 RegionSelection::iterator tmp = r;
5418                 ++tmp;
5419
5420                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5421                 if (arv) {
5422                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5423
5424                         if (progress) {
5425                                 progress->descend (1.0 / N);
5426                         }
5427
5428                         if (arv->audio_region()->apply (filter, progress) == 0) {
5429
5430                                 playlist->clear_changes ();
5431                                 playlist->clear_owned_changes ();
5432
5433                                 if (filter.results.empty ()) {
5434
5435                                         /* no regions returned; remove the old one */
5436                                         playlist->remove_region (arv->region ());
5437
5438                                 } else {
5439
5440                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5441
5442                                         /* first region replaces the old one */
5443                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5444                                         ++res;
5445
5446                                         /* add the rest */
5447                                         while (res != filter.results.end()) {
5448                                                 playlist->add_region (*res, (*res)->position());
5449                                                 ++res;
5450                                         }
5451
5452                                 }
5453                                 /* We might have removed regions, which alters other regions' layering_index,
5454                                    so we need to do a recursive diff here.
5455                                 */
5456
5457                                 if (!in_command) {
5458                                         begin_reversible_command (command);
5459                                         in_command = true;
5460                                 }
5461                                 vector<Command*> cmds;
5462                                 playlist->rdiff (cmds);
5463                                 _session->add_commands (cmds);
5464                                 
5465                                 _session->add_command(new StatefulDiffCommand (playlist));
5466
5467                         } else {
5468                                 continue;
5469                         }
5470
5471                         if (progress) {
5472                                 progress->ascend ();
5473                         }
5474                 }
5475
5476                 r = tmp;
5477                 ++n;
5478         }
5479
5480         if (in_command) {
5481                 commit_reversible_command ();
5482         }
5483 }
5484
5485 void
5486 Editor::external_edit_region ()
5487 {
5488         /* more to come */
5489 }
5490
5491 void
5492 Editor::reset_region_gain_envelopes ()
5493 {
5494         RegionSelection rs = get_regions_from_selection_and_entered ();
5495
5496         if (!_session || rs.empty()) {
5497                 return;
5498         }
5499
5500         bool in_command = false;
5501         
5502         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5503                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5504                 if (arv) {
5505                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5506                         XMLNode& before (alist->get_state());
5507
5508                         arv->audio_region()->set_default_envelope ();
5509
5510                         if (!in_command) {
5511                                 begin_reversible_command (_("reset region gain"));
5512                                 in_command = true;
5513                         }
5514                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5515                 }
5516         }
5517
5518         if (in_command) {
5519                 commit_reversible_command ();
5520         }
5521 }
5522
5523 void
5524 Editor::set_region_gain_visibility (RegionView* rv)
5525 {
5526         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5527         if (arv) {
5528                 arv->update_envelope_visibility();
5529         }
5530 }
5531
5532 void
5533 Editor::set_gain_envelope_visibility ()
5534 {
5535         if (!_session) {
5536                 return;
5537         }
5538
5539         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5540                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5541                 if (v) {
5542                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5543                 }
5544         }
5545 }
5546
5547 void
5548 Editor::toggle_gain_envelope_active ()
5549 {
5550         if (_ignore_region_action) {
5551                 return;
5552         }
5553
5554         RegionSelection rs = get_regions_from_selection_and_entered ();
5555
5556         if (!_session || rs.empty()) {
5557                 return;
5558         }
5559
5560         bool in_command = false;
5561
5562         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5563                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5564                 if (arv) {
5565                         arv->region()->clear_changes ();
5566                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5567
5568                         if (!in_command) {
5569                                 begin_reversible_command (_("region gain envelope active"));
5570                                 in_command = true;
5571                         }
5572                         _session->add_command (new StatefulDiffCommand (arv->region()));
5573                 }
5574         }
5575
5576         if (in_command) {
5577                 commit_reversible_command ();
5578         }
5579 }
5580
5581 void
5582 Editor::toggle_region_lock ()
5583 {
5584         if (_ignore_region_action) {
5585                 return;
5586         }
5587
5588         RegionSelection rs = get_regions_from_selection_and_entered ();
5589
5590         if (!_session || rs.empty()) {
5591                 return;
5592         }
5593
5594         begin_reversible_command (_("toggle region lock"));
5595         
5596         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5597                 (*i)->region()->clear_changes ();
5598                 (*i)->region()->set_locked (!(*i)->region()->locked());
5599                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5600         }
5601         
5602         commit_reversible_command ();
5603 }
5604
5605 void
5606 Editor::toggle_region_video_lock ()
5607 {
5608         if (_ignore_region_action) {
5609                 return;
5610         }
5611
5612         RegionSelection rs = get_regions_from_selection_and_entered ();
5613
5614         if (!_session || rs.empty()) {
5615                 return;
5616         }
5617
5618         begin_reversible_command (_("Toggle Video Lock"));
5619         
5620         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5621                 (*i)->region()->clear_changes ();
5622                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5623                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5624         }
5625         
5626         commit_reversible_command ();
5627 }
5628
5629 void
5630 Editor::toggle_region_lock_style ()
5631 {
5632         if (_ignore_region_action) {
5633                 return;
5634         }
5635
5636         RegionSelection rs = get_regions_from_selection_and_entered ();
5637
5638         if (!_session || rs.empty()) {
5639                 return;
5640         }
5641
5642         begin_reversible_command (_("region lock style"));
5643         
5644         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5645                 (*i)->region()->clear_changes ();
5646                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5647                 (*i)->region()->set_position_lock_style (ns);
5648                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5649         }
5650         
5651         commit_reversible_command ();
5652 }
5653
5654 void
5655 Editor::toggle_opaque_region ()
5656 {
5657         if (_ignore_region_action) {
5658                 return;
5659         }
5660
5661         RegionSelection rs = get_regions_from_selection_and_entered ();
5662
5663         if (!_session || rs.empty()) {
5664                 return;
5665         }
5666
5667         begin_reversible_command (_("change region opacity"));
5668         
5669         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5670                 (*i)->region()->clear_changes ();
5671                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5672                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5673         }
5674         
5675         commit_reversible_command ();
5676 }
5677
5678 void
5679 Editor::toggle_record_enable ()
5680 {
5681         bool new_state = false;
5682         bool first = true;
5683         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5684                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5685                 if (!rtav)
5686                         continue;
5687                 if (!rtav->is_track())
5688                         continue;
5689
5690                 if (first) {
5691                         new_state = !rtav->track()->record_enabled();
5692                         first = false;
5693                 }
5694
5695                 rtav->track()->set_record_enabled (new_state, this);
5696         }
5697 }
5698
5699 void
5700 Editor::toggle_solo ()
5701 {
5702         bool new_state = false;
5703         bool first = true;
5704         boost::shared_ptr<RouteList> rl (new RouteList);
5705
5706         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5707                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5708
5709                 if (!rtav) {
5710                         continue;
5711                 }
5712
5713                 if (first) {
5714                         new_state = !rtav->route()->soloed ();
5715                         first = false;
5716                 }
5717
5718                 rl->push_back (rtav->route());
5719         }
5720
5721         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5722 }
5723
5724 void
5725 Editor::toggle_mute ()
5726 {
5727         bool new_state = false;
5728         bool first = true;
5729         boost::shared_ptr<RouteList> rl (new RouteList);
5730
5731         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5732                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5733
5734                 if (!rtav) {
5735                         continue;
5736                 }
5737
5738                 if (first) {
5739                         new_state = !rtav->route()->muted();
5740                         first = false;
5741                 }
5742
5743                 rl->push_back (rtav->route());
5744         }
5745
5746         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5747 }
5748
5749 void
5750 Editor::toggle_solo_isolate ()
5751 {
5752 }
5753
5754
5755 void
5756 Editor::fade_range ()
5757 {
5758         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5759
5760         begin_reversible_command (_("fade range"));
5761
5762         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5763                 (*i)->fade_range (selection->time);
5764         }
5765
5766         commit_reversible_command ();
5767 }
5768
5769
5770 void
5771 Editor::set_fade_length (bool in)
5772 {
5773         RegionSelection rs = get_regions_from_selection_and_entered ();
5774
5775         if (rs.empty()) {
5776                 return;
5777         }
5778
5779         /* we need a region to measure the offset from the start */
5780
5781         RegionView* rv = rs.front ();
5782
5783         framepos_t pos = get_preferred_edit_position();
5784         framepos_t len;
5785         char const * cmd;
5786
5787         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5788                 /* edit point is outside the relevant region */
5789                 return;
5790         }
5791
5792         if (in) {
5793                 if (pos <= rv->region()->position()) {
5794                         /* can't do it */
5795                         return;
5796                 }
5797                 len = pos - rv->region()->position();
5798                 cmd = _("set fade in length");
5799         } else {
5800                 if (pos >= rv->region()->last_frame()) {
5801                         /* can't do it */
5802                         return;
5803                 }
5804                 len = rv->region()->last_frame() - pos;
5805                 cmd = _("set fade out length");
5806         }
5807
5808         bool in_command = false;
5809
5810         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5811                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5812
5813                 if (!tmp) {
5814                         continue;
5815                 }
5816
5817                 boost::shared_ptr<AutomationList> alist;
5818                 if (in) {
5819                         alist = tmp->audio_region()->fade_in();
5820                 } else {
5821                         alist = tmp->audio_region()->fade_out();
5822                 }
5823
5824                 XMLNode &before = alist->get_state();
5825
5826                 if (in) {
5827                         tmp->audio_region()->set_fade_in_length (len);
5828                         tmp->audio_region()->set_fade_in_active (true);
5829                 } else {
5830                         tmp->audio_region()->set_fade_out_length (len);
5831                         tmp->audio_region()->set_fade_out_active (true);
5832                 }
5833
5834                 if (!in_command) {
5835                         begin_reversible_command (cmd);
5836                         in_command = true;
5837                 }
5838                 XMLNode &after = alist->get_state();
5839                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5840         }
5841
5842         if (in_command) {
5843                 commit_reversible_command ();
5844         }
5845 }
5846
5847 void
5848 Editor::set_fade_in_shape (FadeShape shape)
5849 {
5850         RegionSelection rs = get_regions_from_selection_and_entered ();
5851
5852         if (rs.empty()) {
5853                 return;
5854         }
5855         bool in_command = false;
5856
5857         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5858                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5859
5860                 if (!tmp) {
5861                         continue;
5862                 }
5863
5864                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5865                 XMLNode &before = alist->get_state();
5866
5867                 tmp->audio_region()->set_fade_in_shape (shape);
5868
5869                 if (!in_command) {
5870                         begin_reversible_command (_("set fade in shape"));
5871                         in_command = true;
5872                 }
5873                 XMLNode &after = alist->get_state();
5874                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5875         }
5876
5877         if (in_command) {
5878                 commit_reversible_command ();
5879         }
5880 }
5881
5882 void
5883 Editor::set_fade_out_shape (FadeShape shape)
5884 {
5885         RegionSelection rs = get_regions_from_selection_and_entered ();
5886
5887         if (rs.empty()) {
5888                 return;
5889         }
5890         bool in_command = false;
5891
5892         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5893                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5894
5895                 if (!tmp) {
5896                         continue;
5897                 }
5898
5899                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5900                 XMLNode &before = alist->get_state();
5901
5902                 tmp->audio_region()->set_fade_out_shape (shape);
5903
5904                 if(!in_command) {
5905                         begin_reversible_command (_("set fade out shape"));
5906                         in_command = true;
5907                 }
5908                 XMLNode &after = alist->get_state();
5909                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5910         }
5911
5912         if (in_command) {
5913                 commit_reversible_command ();
5914         }
5915 }
5916
5917 void
5918 Editor::set_fade_in_active (bool yn)
5919 {
5920         RegionSelection rs = get_regions_from_selection_and_entered ();
5921
5922         if (rs.empty()) {
5923                 return;
5924         }
5925         bool in_command = false;
5926
5927         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5928                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5929
5930                 if (!tmp) {
5931                         continue;
5932                 }
5933
5934
5935                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5936
5937                 ar->clear_changes ();
5938                 ar->set_fade_in_active (yn);
5939
5940                 if (!in_command) {
5941                         begin_reversible_command (_("set fade in active"));
5942                         in_command = true;
5943                 }
5944                 _session->add_command (new StatefulDiffCommand (ar));
5945         }
5946
5947         if (in_command) {
5948                 commit_reversible_command ();
5949         }
5950 }
5951
5952 void
5953 Editor::set_fade_out_active (bool yn)
5954 {
5955         RegionSelection rs = get_regions_from_selection_and_entered ();
5956
5957         if (rs.empty()) {
5958                 return;
5959         }
5960         bool in_command = false;
5961
5962         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5963                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5964
5965                 if (!tmp) {
5966                         continue;
5967                 }
5968
5969                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5970
5971                 ar->clear_changes ();
5972                 ar->set_fade_out_active (yn);
5973
5974                 if (!in_command) {
5975                         begin_reversible_command (_("set fade out active"));
5976                         in_command = true;
5977                 }
5978                 _session->add_command(new StatefulDiffCommand (ar));
5979         }
5980
5981         if (in_command) {
5982                 commit_reversible_command ();
5983         }
5984 }
5985
5986 void
5987 Editor::toggle_region_fades (int dir)
5988 {
5989         if (_ignore_region_action) {
5990                 return;
5991         }
5992         
5993         boost::shared_ptr<AudioRegion> ar;
5994         bool yn = false;
5995
5996         RegionSelection rs = get_regions_from_selection_and_entered ();
5997
5998         if (rs.empty()) {
5999                 return;
6000         }
6001
6002         RegionSelection::iterator i;
6003         for (i = rs.begin(); i != rs.end(); ++i) {
6004                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6005                         if (dir == -1) {
6006                                 yn = ar->fade_out_active ();
6007                         } else {
6008                                 yn = ar->fade_in_active ();
6009                         }
6010                         break;
6011                 }
6012         }
6013
6014         if (i == rs.end()) {
6015                 return;
6016         }
6017
6018         /* XXX should this undo-able? */
6019         bool in_command = false;
6020
6021         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6022                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6023                         continue;
6024                 }
6025                 ar->clear_changes ();
6026
6027                 if (dir == 1 || dir == 0) {
6028                         ar->set_fade_in_active (!yn);
6029                 }
6030
6031                 if (dir == -1 || dir == 0) {
6032                         ar->set_fade_out_active (!yn);
6033                 }
6034                 if (!in_command) {
6035                         begin_reversible_command (_("toggle fade active"));
6036                         in_command = true;
6037                 }
6038                 _session->add_command(new StatefulDiffCommand (ar));
6039         }
6040
6041         if (in_command) {
6042                 commit_reversible_command ();
6043         }
6044 }
6045
6046
6047 /** Update region fade visibility after its configuration has been changed */
6048 void
6049 Editor::update_region_fade_visibility ()
6050 {
6051         bool _fade_visibility = _session->config.get_show_region_fades ();
6052
6053         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6054                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6055                 if (v) {
6056                         if (_fade_visibility) {
6057                                 v->audio_view()->show_all_fades ();
6058                         } else {
6059                                 v->audio_view()->hide_all_fades ();
6060                         }
6061                 }
6062         }
6063 }
6064
6065 void
6066 Editor::set_edit_point ()
6067 {
6068         framepos_t where;
6069         bool ignored;
6070
6071         if (!mouse_frame (where, ignored)) {
6072                 return;
6073         }
6074
6075         snap_to (where);
6076
6077         if (selection->markers.empty()) {
6078
6079                 mouse_add_new_marker (where);
6080
6081         } else {
6082                 bool ignored;
6083
6084                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6085
6086                 if (loc) {
6087                         loc->move_to (where);
6088                 }
6089         }
6090 }
6091
6092 void
6093 Editor::set_playhead_cursor ()
6094 {
6095         if (entered_marker) {
6096                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6097         } else {
6098                 framepos_t where;
6099                 bool ignored;
6100
6101                 if (!mouse_frame (where, ignored)) {
6102                         return;
6103                 }
6104
6105                 snap_to (where);
6106
6107                 if (_session) {
6108                         _session->request_locate (where, _session->transport_rolling());
6109                 }
6110         }
6111
6112         if (ARDOUR_UI::config()->get_follow_edits()) {
6113                 cancel_time_selection();
6114         }
6115 }
6116
6117 void
6118 Editor::split_region ()
6119 {
6120         //if a range is selected, separate it
6121         if ( !selection->time.empty()) {
6122                 separate_regions_between (selection->time);
6123                 return;
6124         }
6125
6126         //if no range was selected, try to find some regions to split
6127         if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6128         
6129                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6130
6131                 framepos_t where = get_preferred_edit_position ();
6132
6133                 if (rs.empty()) {
6134                         return;
6135                 }
6136
6137                 split_regions_at (where, rs);
6138         }
6139 }
6140
6141 struct EditorOrderRouteSorter {
6142     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
6143             return a->order_key () < b->order_key ();
6144     }
6145 };
6146
6147 void
6148 Editor::select_next_route()
6149 {
6150         if (selection->tracks.empty()) {
6151                 selection->set (track_views.front());
6152                 return;
6153         }
6154
6155         TimeAxisView* current = selection->tracks.front();
6156
6157         RouteUI *rui;
6158         do {
6159                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6160                         if (*i == current) {
6161                                 ++i;
6162                                 if (i != track_views.end()) {
6163                                         current = (*i);
6164                                 } else {
6165                                         current = (*(track_views.begin()));
6166                                         //selection->set (*(track_views.begin()));
6167                                 }
6168                                 break;
6169                         }
6170                 }
6171                 rui = dynamic_cast<RouteUI *>(current);
6172         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
6173
6174         selection->set(current);
6175
6176         ensure_time_axis_view_is_visible (*current, false);
6177 }
6178
6179 void
6180 Editor::select_prev_route()
6181 {
6182         if (selection->tracks.empty()) {
6183                 selection->set (track_views.front());
6184                 return;
6185         }
6186
6187         TimeAxisView* current = selection->tracks.front();
6188
6189         RouteUI *rui;
6190         do {
6191                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6192                         if (*i == current) {
6193                                 ++i;
6194                                 if (i != track_views.rend()) {
6195                                         current = (*i);
6196                                 } else {
6197                                         current = *(track_views.rbegin());
6198                                 }
6199                                 break;
6200                         }
6201                 }
6202                 rui = dynamic_cast<RouteUI *>(current);
6203         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
6204
6205         selection->set (current);
6206
6207         ensure_time_axis_view_is_visible (*current, false);
6208 }
6209
6210 void
6211 Editor::set_loop_from_selection (bool play)
6212 {
6213         if (_session == 0) {
6214                 return;
6215         }
6216
6217         framepos_t start, end;
6218         if (!get_selection_extents ( start, end))
6219                 return;
6220
6221         set_loop_range (start, end,  _("set loop range from selection"));
6222
6223         if (play) {
6224                 _session->request_play_loop (true, true);
6225         }
6226 }
6227
6228 void
6229 Editor::set_loop_from_region (bool play)
6230 {
6231         framepos_t start, end;
6232         if (!get_selection_extents ( start, end))
6233                 return;
6234
6235         set_loop_range (start, end, _("set loop range from region"));
6236
6237         if (play) {
6238                 _session->request_locate (start, true);
6239                 _session->request_play_loop (true);
6240         }
6241 }
6242
6243 void
6244 Editor::set_punch_from_selection ()
6245 {
6246         if (_session == 0) {
6247                 return;
6248         }
6249
6250         framepos_t start, end;
6251         if (!get_selection_extents ( start, end))
6252                 return;
6253
6254         set_punch_range (start, end,  _("set punch range from selection"));
6255 }
6256
6257 void
6258 Editor::set_session_extents_from_selection ()
6259 {
6260         if (_session == 0) {
6261                 return;
6262         }
6263         
6264         framepos_t start, end;
6265         if (!get_selection_extents ( start, end))
6266                 return;
6267
6268         Location* loc;
6269         if ((loc = _session->locations()->session_range_location()) == 0) {
6270                 _session->set_session_extents ( start, end );  // this will create a new session range;  no need for UNDO
6271         } else {
6272                 XMLNode &before = loc->get_state();
6273
6274                 _session->set_session_extents ( start, end );
6275
6276                 XMLNode &after = loc->get_state();
6277
6278                 begin_reversible_command (_("set session start/end from selection"));
6279
6280                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6281
6282                 commit_reversible_command ();
6283         }
6284 }
6285
6286 void
6287 Editor::set_punch_from_region ()
6288 {
6289         framepos_t start, end;
6290         if (!get_selection_extents ( start, end))
6291                 return;
6292
6293         set_punch_range (start, end, _("set punch range from region"));
6294 }
6295
6296 void
6297 Editor::pitch_shift_region ()
6298 {
6299         RegionSelection rs = get_regions_from_selection_and_entered ();
6300
6301         RegionSelection audio_rs;
6302         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6303                 if (dynamic_cast<AudioRegionView*> (*i)) {
6304                         audio_rs.push_back (*i);
6305                 }
6306         }
6307
6308         if (audio_rs.empty()) {
6309                 return;
6310         }
6311
6312         pitch_shift (audio_rs, 1.2);
6313 }
6314
6315 void
6316 Editor::transpose_region ()
6317 {
6318         RegionSelection rs = get_regions_from_selection_and_entered ();
6319
6320         list<MidiRegionView*> midi_region_views;
6321         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6322                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
6323                 if (mrv) {
6324                         midi_region_views.push_back (mrv);
6325                 }
6326         }
6327
6328         TransposeDialog d;
6329         int const r = d.run ();
6330         if (r != RESPONSE_ACCEPT) {
6331                 return;
6332         }
6333
6334         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
6335                 (*i)->midi_region()->transpose (d.semitones ());
6336         }
6337 }
6338
6339 void
6340 Editor::set_tempo_from_region ()
6341 {
6342         RegionSelection rs = get_regions_from_selection_and_entered ();
6343
6344         if (!_session || rs.empty()) {
6345                 return;
6346         }
6347
6348         RegionView* rv = rs.front();
6349
6350         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6351 }
6352
6353 void
6354 Editor::use_range_as_bar ()
6355 {
6356         framepos_t start, end;
6357         if (get_edit_op_range (start, end)) {
6358                 define_one_bar (start, end);
6359         }
6360 }
6361
6362 void
6363 Editor::define_one_bar (framepos_t start, framepos_t end)
6364 {
6365         framepos_t length = end - start;
6366
6367         const Meter& m (_session->tempo_map().meter_at (start));
6368
6369         /* length = 1 bar */
6370
6371         /* now we want frames per beat.
6372            we have frames per bar, and beats per bar, so ...
6373         */
6374
6375         /* XXXX METER MATH */
6376
6377         double frames_per_beat = length / m.divisions_per_bar();
6378
6379         /* beats per minute = */
6380
6381         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6382
6383         /* now decide whether to:
6384
6385             (a) set global tempo
6386             (b) add a new tempo marker
6387
6388         */
6389
6390         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
6391
6392         bool do_global = false;
6393
6394         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6395
6396                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6397                    at the start, or create a new marker
6398                 */
6399
6400                 vector<string> options;
6401                 options.push_back (_("Cancel"));
6402                 options.push_back (_("Add new marker"));
6403                 options.push_back (_("Set global tempo"));
6404
6405                 Choice c (
6406                         _("Define one bar"),
6407                         _("Do you want to set the global tempo or add a new tempo marker?"),
6408                         options
6409                         );
6410
6411                 c.set_default_response (2);
6412
6413                 switch (c.run()) {
6414                 case 0:
6415                         return;
6416
6417                 case 2:
6418                         do_global = true;
6419                         break;
6420
6421                 default:
6422                         do_global = false;
6423                 }
6424
6425         } else {
6426
6427                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6428                    if the marker is at the region starter, change it, otherwise add
6429                    a new tempo marker
6430                 */
6431         }
6432
6433         begin_reversible_command (_("set tempo from region"));
6434         XMLNode& before (_session->tempo_map().get_state());
6435
6436         if (do_global) {
6437                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6438         } else if (t.frame() == start) {
6439                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6440         } else {
6441                 Timecode::BBT_Time bbt;
6442                 _session->tempo_map().bbt_time (start, bbt);
6443                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
6444         }
6445
6446         XMLNode& after (_session->tempo_map().get_state());
6447
6448         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6449         commit_reversible_command ();
6450 }
6451
6452 void
6453 Editor::split_region_at_transients ()
6454 {
6455         AnalysisFeatureList positions;
6456
6457         RegionSelection rs = get_regions_from_selection_and_entered ();
6458
6459         if (!_session || rs.empty()) {
6460                 return;
6461         }
6462
6463         begin_reversible_command (_("split regions"));
6464         
6465         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6466
6467                 RegionSelection::iterator tmp;
6468
6469                 tmp = i;
6470                 ++tmp;
6471
6472                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6473
6474                 if (ar && (ar->get_transients (positions) == 0)) {
6475                         split_region_at_points ((*i)->region(), positions, true);
6476                         positions.clear ();
6477                 }
6478
6479                 i = tmp;
6480         }
6481         
6482         commit_reversible_command ();
6483
6484 }
6485
6486 void
6487 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6488 {
6489         bool use_rhythmic_rodent = false;
6490
6491         boost::shared_ptr<Playlist> pl = r->playlist();
6492
6493         list<boost::shared_ptr<Region> > new_regions;
6494
6495         if (!pl) {
6496                 return;
6497         }
6498
6499         if (positions.empty()) {
6500                 return;
6501         }
6502
6503
6504         if (positions.size() > 20 && can_ferret) {
6505                 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);
6506                 MessageDialog msg (msgstr,
6507                                    false,
6508                                    Gtk::MESSAGE_INFO,
6509                                    Gtk::BUTTONS_OK_CANCEL);
6510
6511                 if (can_ferret) {
6512                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6513                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6514                 } else {
6515                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6516                 }
6517
6518                 msg.set_title (_("Excessive split?"));
6519                 msg.present ();
6520
6521                 int response = msg.run();
6522                 msg.hide ();
6523
6524                 switch (response) {
6525                 case RESPONSE_OK:
6526                         break;
6527                 case RESPONSE_APPLY:
6528                         use_rhythmic_rodent = true;
6529                         break;
6530                 default:
6531                         return;
6532                 }
6533         }
6534
6535         if (use_rhythmic_rodent) {
6536                 show_rhythm_ferret ();
6537                 return;
6538         }
6539
6540         AnalysisFeatureList::const_iterator x;
6541
6542         pl->clear_changes ();
6543         pl->clear_owned_changes ();
6544
6545         x = positions.begin();
6546
6547         if (x == positions.end()) {
6548                 return;
6549         }
6550
6551         pl->freeze ();
6552         pl->remove_region (r);
6553
6554         framepos_t pos = 0;
6555
6556         while (x != positions.end()) {
6557
6558                 /* deal with positons that are out of scope of present region bounds */
6559                 if (*x <= 0 || *x > r->length()) {
6560                         ++x;
6561                         continue;
6562                 }
6563
6564                 /* file start = original start + how far we from the initial position ?
6565                  */
6566
6567                 framepos_t file_start = r->start() + pos;
6568
6569                 /* length = next position - current position
6570                  */
6571
6572                 framepos_t len = (*x) - pos;
6573
6574                 /* XXX we do we really want to allow even single-sample regions?
6575                    shouldn't we have some kind of lower limit on region size?
6576                 */
6577
6578                 if (len <= 0) {
6579                         break;
6580                 }
6581
6582                 string new_name;
6583
6584                 if (RegionFactory::region_name (new_name, r->name())) {
6585                         break;
6586                 }
6587
6588                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6589
6590                 PropertyList plist;
6591
6592                 plist.add (ARDOUR::Properties::start, file_start);
6593                 plist.add (ARDOUR::Properties::length, len);
6594                 plist.add (ARDOUR::Properties::name, new_name);
6595                 plist.add (ARDOUR::Properties::layer, 0);
6596
6597                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6598                 /* because we set annouce to false, manually add the new region to the
6599                    RegionFactory map
6600                 */
6601                 RegionFactory::map_add (nr);
6602
6603                 pl->add_region (nr, r->position() + pos);
6604
6605                 if (select_new) {
6606                         new_regions.push_front(nr);
6607                 }
6608
6609                 pos += len;
6610                 ++x;
6611         }
6612
6613         string new_name;
6614
6615         RegionFactory::region_name (new_name, r->name());
6616
6617         /* Add the final region */
6618         PropertyList plist;
6619
6620         plist.add (ARDOUR::Properties::start, r->start() + pos);
6621         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6622         plist.add (ARDOUR::Properties::name, new_name);
6623         plist.add (ARDOUR::Properties::layer, 0);
6624
6625         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6626         /* because we set annouce to false, manually add the new region to the
6627            RegionFactory map
6628         */
6629         RegionFactory::map_add (nr);
6630         pl->add_region (nr, r->position() + pos);
6631
6632         if (select_new) {
6633                 new_regions.push_front(nr);
6634         }
6635
6636         pl->thaw ();
6637
6638         /* We might have removed regions, which alters other regions' layering_index,
6639            so we need to do a recursive diff here.
6640         */
6641         vector<Command*> cmds;
6642         pl->rdiff (cmds);
6643         _session->add_commands (cmds);
6644         
6645         _session->add_command (new StatefulDiffCommand (pl));
6646
6647         if (select_new) {
6648
6649                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6650                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6651                 }
6652         }
6653 }
6654
6655 void
6656 Editor::place_transient()
6657 {
6658         if (!_session) {
6659                 return;
6660         }
6661
6662         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6663
6664         if (rs.empty()) {
6665                 return;
6666         }
6667
6668         framepos_t where = get_preferred_edit_position();
6669
6670         begin_reversible_command (_("place transient"));
6671         
6672         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6673                 framepos_t position = (*r)->region()->position();
6674                 (*r)->region()->add_transient(where - position);
6675         }
6676         
6677         commit_reversible_command ();
6678 }
6679
6680 void
6681 Editor::remove_transient(ArdourCanvas::Item* item)
6682 {
6683         if (!_session) {
6684                 return;
6685         }
6686
6687         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6688         assert (_line);
6689
6690         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6691         _arv->remove_transient (*(float*) _line->get_data ("position"));
6692 }
6693
6694 void
6695 Editor::snap_regions_to_grid ()
6696 {
6697         list <boost::shared_ptr<Playlist > > used_playlists;
6698
6699         RegionSelection rs = get_regions_from_selection_and_entered ();
6700
6701         if (!_session || rs.empty()) {
6702                 return;
6703         }
6704
6705         begin_reversible_command (_("snap regions to grid"));
6706         
6707         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6708
6709                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6710
6711                 if (!pl->frozen()) {
6712                         /* we haven't seen this playlist before */
6713
6714                         /* remember used playlists so we can thaw them later */
6715                         used_playlists.push_back(pl);
6716                         pl->freeze();
6717                 }
6718
6719                 framepos_t start_frame = (*r)->region()->first_frame ();
6720                 snap_to (start_frame);
6721                 (*r)->region()->set_position (start_frame);
6722         }
6723
6724         while (used_playlists.size() > 0) {
6725                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6726                 (*i)->thaw();
6727                 used_playlists.pop_front();
6728         }
6729         
6730         commit_reversible_command ();
6731 }
6732
6733 void
6734 Editor::close_region_gaps ()
6735 {
6736         list <boost::shared_ptr<Playlist > > used_playlists;
6737
6738         RegionSelection rs = get_regions_from_selection_and_entered ();
6739
6740         if (!_session || rs.empty()) {
6741                 return;
6742         }
6743
6744         Dialog dialog (_("Close Region Gaps"));
6745
6746         Table table (2, 3);
6747         table.set_spacings (12);
6748         table.set_border_width (12);
6749         Label* l = manage (left_aligned_label (_("Crossfade length")));
6750         table.attach (*l, 0, 1, 0, 1);
6751
6752         SpinButton spin_crossfade (1, 0);
6753         spin_crossfade.set_range (0, 15);
6754         spin_crossfade.set_increments (1, 1);
6755         spin_crossfade.set_value (5);
6756         table.attach (spin_crossfade, 1, 2, 0, 1);
6757
6758         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6759
6760         l = manage (left_aligned_label (_("Pull-back length")));
6761         table.attach (*l, 0, 1, 1, 2);
6762
6763         SpinButton spin_pullback (1, 0);
6764         spin_pullback.set_range (0, 100);
6765         spin_pullback.set_increments (1, 1);
6766         spin_pullback.set_value(30);
6767         table.attach (spin_pullback, 1, 2, 1, 2);
6768
6769         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6770
6771         dialog.get_vbox()->pack_start (table);
6772         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6773         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6774         dialog.show_all ();
6775
6776         if (dialog.run () == RESPONSE_CANCEL) {
6777                 return;
6778         }
6779
6780         framepos_t crossfade_len = spin_crossfade.get_value();
6781         framepos_t pull_back_frames = spin_pullback.get_value();
6782
6783         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6784         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6785
6786         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6787
6788         begin_reversible_command (_("close region gaps"));
6789         
6790         int idx = 0;
6791         boost::shared_ptr<Region> last_region;
6792
6793         rs.sort_by_position_and_track();
6794
6795         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6796
6797                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6798
6799                 if (!pl->frozen()) {
6800                         /* we haven't seen this playlist before */
6801
6802                         /* remember used playlists so we can thaw them later */
6803                         used_playlists.push_back(pl);
6804                         pl->freeze();
6805                 }
6806
6807                 framepos_t position = (*r)->region()->position();
6808
6809                 if (idx == 0 || position < last_region->position()){
6810                         last_region = (*r)->region();
6811                         idx++;
6812                         continue;
6813                 }
6814
6815                 (*r)->region()->trim_front( (position - pull_back_frames));
6816                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6817
6818                 last_region = (*r)->region();
6819
6820                 idx++;
6821         }
6822
6823         while (used_playlists.size() > 0) {
6824                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6825                 (*i)->thaw();
6826                 used_playlists.pop_front();
6827         }
6828         
6829         commit_reversible_command ();
6830 }
6831
6832 void
6833 Editor::tab_to_transient (bool forward)
6834 {
6835         AnalysisFeatureList positions;
6836
6837         RegionSelection rs = get_regions_from_selection_and_entered ();
6838
6839         if (!_session) {
6840                 return;
6841         }
6842
6843         framepos_t pos = _session->audible_frame ();
6844
6845         if (!selection->tracks.empty()) {
6846
6847                 /* don't waste time searching for transients in duplicate playlists.
6848                  */
6849
6850                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6851
6852                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6853
6854                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6855
6856                         if (rtv) {
6857                                 boost::shared_ptr<Track> tr = rtv->track();
6858                                 if (tr) {
6859                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6860                                         if (pl) {
6861                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6862
6863                                                 if (result >= 0) {
6864                                                         positions.push_back (result);
6865                                                 }
6866                                         }
6867                                 }
6868                         }
6869                 }
6870
6871         } else {
6872
6873                 if (rs.empty()) {
6874                         return;
6875                 }
6876
6877                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6878                         (*r)->region()->get_transients (positions);
6879                 }
6880         }
6881
6882         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6883
6884         if (forward) {
6885                 AnalysisFeatureList::iterator x;
6886
6887                 for (x = positions.begin(); x != positions.end(); ++x) {
6888                         if ((*x) > pos) {
6889                                 break;
6890                         }
6891                 }
6892
6893                 if (x != positions.end ()) {
6894                         _session->request_locate (*x);
6895                 }
6896
6897         } else {
6898                 AnalysisFeatureList::reverse_iterator x;
6899
6900                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6901                         if ((*x) < pos) {
6902                                 break;
6903                         }
6904                 }
6905
6906                 if (x != positions.rend ()) {
6907                         _session->request_locate (*x);
6908                 }
6909         }
6910 }
6911
6912 void
6913 Editor::playhead_forward_to_grid ()
6914 {
6915         if (!_session) {
6916                 return;
6917         }
6918         
6919         framepos_t pos = playhead_cursor->current_frame ();
6920         if (pos < max_framepos - 1) {
6921                 pos += 2;
6922                 snap_to_internal (pos, RoundUpAlways, false);
6923                 _session->request_locate (pos);
6924         }
6925 }
6926
6927
6928 void
6929 Editor::playhead_backward_to_grid ()
6930 {
6931         if (!_session) {
6932                 return;
6933         }
6934         
6935         framepos_t pos = playhead_cursor->current_frame ();
6936         if (pos > 2) {
6937                 pos -= 2;
6938                 snap_to_internal (pos, RoundDownAlways, false);
6939                 _session->request_locate (pos);
6940         }
6941 }
6942
6943 void
6944 Editor::set_track_height (Height h)
6945 {
6946         TrackSelection& ts (selection->tracks);
6947
6948         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6949                 (*x)->set_height_enum (h);
6950         }
6951 }
6952
6953 void
6954 Editor::toggle_tracks_active ()
6955 {
6956         TrackSelection& ts (selection->tracks);
6957         bool first = true;
6958         bool target = false;
6959
6960         if (ts.empty()) {
6961                 return;
6962         }
6963
6964         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6965                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6966
6967                 if (rtv) {
6968                         if (first) {
6969                                 target = !rtv->_route->active();
6970                                 first = false;
6971                         }
6972                         rtv->_route->set_active (target, this);
6973                 }
6974         }
6975 }
6976
6977 void
6978 Editor::remove_tracks ()
6979 {
6980         /* this will delete GUI objects that may be the subject of an event
6981            handler in which this method is called. Defer actual deletion to the
6982            next idle callback, when all event handling is finished.
6983         */
6984         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
6985 }
6986
6987 bool
6988 Editor::idle_remove_tracks ()
6989 {
6990         _remove_tracks ();
6991         return false; /* do not call again */
6992 }
6993
6994 void
6995 Editor::_remove_tracks ()
6996 {
6997         TrackSelection& ts (selection->tracks);
6998
6999         if (ts.empty()) {
7000                 return;
7001         }
7002
7003         vector<string> choices;
7004         string prompt;
7005         int ntracks = 0;
7006         int nbusses = 0;
7007         const char* trackstr;
7008         const char* busstr;
7009         vector<boost::shared_ptr<Route> > routes;
7010         bool special_bus = false;
7011
7012         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7013                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7014                 if (!rtv) {
7015                         continue;
7016                 }
7017                 if (rtv->is_track()) {
7018                         ntracks++;
7019                 } else {
7020                         nbusses++;
7021                 }
7022                 routes.push_back (rtv->_route);
7023
7024                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7025                         special_bus = true;
7026                 }
7027         }
7028
7029         if (special_bus && !Config->get_allow_special_bus_removal()) {
7030                 MessageDialog msg (_("That would be bad news ...."),
7031                                    false,
7032                                    Gtk::MESSAGE_INFO,
7033                                    Gtk::BUTTONS_OK);
7034                 msg.set_secondary_text (string_compose (_(
7035                                                                 "Removing the master or monitor bus is such a bad idea\n\
7036 that %1 is not going to allow it.\n\
7037 \n\
7038 If you really want to do this sort of thing\n\
7039 edit your ardour.rc file to set the\n\
7040 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7041
7042                 msg.present ();
7043                 msg.run ();
7044                 return;
7045         }
7046
7047         if (ntracks + nbusses == 0) {
7048                 return;
7049         }
7050
7051         trackstr = P_("track", "tracks", ntracks);
7052         busstr = P_("bus", "busses", nbusses);
7053         
7054         if (ntracks) {
7055                 if (nbusses) {
7056                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7057                                                     "(You may also lose the playlists associated with the %2)\n\n"
7058                                                     "This action cannot be undone, and the session file will be overwritten!"),
7059                                                   ntracks, trackstr, nbusses, busstr);
7060                 } else {
7061                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7062                                                     "(You may also lose the playlists associated with the %2)\n\n"
7063                                                     "This action cannot be undone, and the session file will be overwritten!"),
7064                                                   ntracks, trackstr);
7065                 }
7066         } else if (nbusses) {
7067                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7068                                             "This action cannot be undone, and the session file will be overwritten"),
7069                                           nbusses, busstr);
7070         }
7071
7072         choices.push_back (_("No, do nothing."));
7073         if (ntracks + nbusses > 1) {
7074                 choices.push_back (_("Yes, remove them."));
7075         } else {
7076                 choices.push_back (_("Yes, remove it."));
7077         }
7078
7079         string title;
7080         if (ntracks) {
7081                 title = string_compose (_("Remove %1"), trackstr);
7082         } else {
7083                 title = string_compose (_("Remove %1"), busstr);
7084         }
7085
7086         Choice prompter (title, prompt, choices);
7087
7088         if (prompter.run () != 1) {
7089                 return;
7090         }
7091
7092         {
7093                 Session::StateProtector sp (_session);
7094                 DisplaySuspender ds;
7095                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7096                         _session->remove_route (*x);
7097                 }
7098         }
7099 }
7100
7101 void
7102 Editor::do_insert_time ()
7103 {
7104         if (selection->tracks.empty()) {
7105                 return;
7106         }
7107
7108         InsertTimeDialog d (*this);
7109         int response = d.run ();
7110
7111         if (response != RESPONSE_OK) {
7112                 return;
7113         }
7114
7115         if (d.distance() == 0) {
7116                 return;
7117         }
7118
7119         InsertTimeOption opt = d.intersected_region_action ();
7120
7121         insert_time (
7122                 get_preferred_edit_position(),
7123                 d.distance(),
7124                 opt,
7125                 d.all_playlists(),
7126                 d.move_glued(),
7127                 d.move_markers(),
7128                 d.move_glued_markers(),
7129                 d.move_locked_markers(),
7130                 d.move_tempos()
7131                 );
7132 }
7133
7134 void
7135 Editor::insert_time (
7136         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7137         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7138         )
7139 {
7140
7141         if (Config->get_edit_mode() == Lock) {
7142                 return;
7143         }
7144         bool in_command = false;
7145
7146         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7147
7148         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7149
7150                 /* regions */
7151
7152                 /* don't operate on any playlist more than once, which could
7153                  * happen if "all playlists" is enabled, but there is more
7154                  * than 1 track using playlists "from" a given track.
7155                  */
7156
7157                 set<boost::shared_ptr<Playlist> > pl;
7158
7159                 if (all_playlists) {
7160                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7161                         if (rtav) {
7162                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7163                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7164                                         pl.insert (*p);
7165                                 }
7166                         }
7167                 } else {
7168                         if ((*x)->playlist ()) {
7169                                 pl.insert ((*x)->playlist ());
7170                         }
7171                 }
7172                 
7173                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7174
7175                         (*i)->clear_changes ();
7176                         (*i)->clear_owned_changes ();
7177
7178                         if (opt == SplitIntersected) {
7179                                 (*i)->split (pos);
7180                         }
7181
7182                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7183
7184                         if (!in_command) {
7185                                 begin_reversible_command (_("insert time"));
7186                                 in_command = true;
7187                         }
7188                         vector<Command*> cmds;
7189                         (*i)->rdiff (cmds);
7190                         _session->add_commands (cmds);
7191
7192                         _session->add_command (new StatefulDiffCommand (*i));
7193                 }
7194
7195                 /* automation */
7196                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7197                 if (rtav) {
7198                         if (!in_command) {
7199                                 begin_reversible_command (_("insert time"));
7200                                 in_command = true;
7201                         }
7202                         rtav->route ()->shift (pos, frames);
7203                 }
7204         }
7205
7206         /* markers */
7207         if (markers_too) {
7208                 bool moved = false;
7209                 XMLNode& before (_session->locations()->get_state());
7210                 Locations::LocationList copy (_session->locations()->list());
7211
7212                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7213
7214                         Locations::LocationList::const_iterator tmp;
7215
7216                         bool const was_locked = (*i)->locked ();
7217                         if (locked_markers_too) {
7218                                 (*i)->unlock ();
7219                         }
7220
7221                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7222
7223                                 if ((*i)->start() >= pos) {
7224                                         (*i)->set_start ((*i)->start() + frames);
7225                                         if (!(*i)->is_mark()) {
7226                                                 (*i)->set_end ((*i)->end() + frames);
7227                                         }
7228                                         moved = true;
7229                                 }
7230
7231                         }
7232
7233                         if (was_locked) {
7234                                 (*i)->lock ();
7235                         }
7236                 }
7237
7238                 if (moved) {
7239                         if (!in_command) {
7240                                 begin_reversible_command (_("insert time"));
7241                                 in_command = true;
7242                         }
7243                         XMLNode& after (_session->locations()->get_state());
7244                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7245                 }
7246         }
7247
7248         if (tempo_too) {
7249                 if (!in_command) {
7250                         begin_reversible_command (_("insert time"));
7251                         in_command = true;
7252                 }
7253                 XMLNode& before (_session->tempo_map().get_state());
7254                 _session->tempo_map().insert_time (pos, frames);
7255                 XMLNode& after (_session->tempo_map().get_state());
7256                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7257         }
7258
7259         if (in_command) {
7260                 commit_reversible_command ();
7261         }
7262 }
7263 void
7264 Editor::do_cut_time ()
7265 {
7266         if (selection->tracks.empty()) {
7267                 return;
7268         }
7269
7270         framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
7271         ArdourDialog d (*this, _("Cut Time"));
7272         VButtonBox button_box;
7273         VBox option_box;
7274
7275         CheckButton glue_button (_("Move Glued Regions"));  glue_button.set_active();
7276         CheckButton marker_button (_("Move Markers"));  marker_button.set_active();
7277         CheckButton tempo_button (_("Move Tempo & Meters"));  tempo_button.set_active();
7278         AudioClock clock ("cutTimeClock", true, "", true, false, true, false);
7279         HBox clock_box;
7280
7281         clock.set (0);
7282         clock.set_session (_session);
7283         clock.set_bbt_reference (pos);
7284
7285         clock_box.pack_start (clock, false, true);
7286
7287         option_box.set_spacing (6);
7288         option_box.pack_start (button_box, false, false);
7289         option_box.pack_start (glue_button, false, false);
7290         option_box.pack_start (marker_button, false, false);
7291         option_box.pack_start (tempo_button, false, false);
7292
7293         d.get_vbox()->set_border_width (12);
7294         d.get_vbox()->pack_start (clock_box, false, false);
7295         d.get_vbox()->pack_start (option_box, false, false);
7296         
7297         option_box.show ();
7298         button_box.show ();
7299         glue_button.show ();
7300         clock.show_all();
7301         clock_box.show ();
7302         marker_button.show ();
7303         tempo_button.show ();
7304
7305         d.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
7306         d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
7307         d.show ();
7308
7309         int response = d.run ();
7310
7311         if (response != RESPONSE_OK) {
7312                 return;
7313         }
7314         
7315         framecnt_t distance = clock.current_duration (pos);
7316
7317         if (distance == 0) {
7318                 return;
7319         }
7320
7321         cut_time (pos, distance, SplitIntersected, glue_button.get_active(), marker_button.get_active(), tempo_button.get_active());
7322 }
7323
7324 void
7325 Editor::cut_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt, 
7326                      bool ignore_music_glue, bool markers_too, bool tempo_too)
7327 {
7328         if (Config->get_edit_mode() == Lock) {
7329                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7330                 return;
7331         }
7332         bool in_command = false;
7333
7334         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7335                 /* regions */
7336                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7337                 
7338                 if (pl) {
7339
7340                         XMLNode &before = pl->get_state();
7341                         
7342                         std::list<AudioRange> rl;
7343                         AudioRange ar(pos, pos+frames, 0);
7344                         rl.push_back(ar);
7345                         pl->cut (rl);
7346                         pl->shift (pos, -frames, true, ignore_music_glue);
7347                         
7348                         if (!in_command) {
7349                                 begin_reversible_command (_("cut time"));
7350                                 in_command = true;
7351                         }
7352                         XMLNode &after = pl->get_state();
7353                         
7354                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7355                 }
7356                         
7357                 /* automation */
7358                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7359                 if (rtav) {
7360                         if (!in_command) {
7361                                 begin_reversible_command (_("cut time"));
7362                                 in_command = true;
7363                         }
7364                         rtav->route ()->shift (pos, -frames);
7365                 }
7366         }
7367
7368         std::list<Location*> loc_kill_list;
7369         
7370         /* markers */
7371         if (markers_too) {
7372                 bool moved = false;
7373                 XMLNode& before (_session->locations()->get_state());
7374                 Locations::LocationList copy (_session->locations()->list());
7375
7376                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7377                         
7378                         if (!(*i)->is_mark()) {  //range;  have to handle both start and end
7379                                         if ((*i)->end() >= pos
7380                                         && (*i)->end() < pos+frames
7381                                         && (*i)->start() >= pos
7382                                         && (*i)->end() < pos+frames) {  //range is completely enclosed;  kill it
7383                                                 moved = true;
7384                                                 loc_kill_list.push_back(*i);
7385                                         } else {  //ony start or end is included, try to do the right thing
7386                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7387                                                         (*i)->set_end (pos);  //bring the end to the cut
7388                                                         moved = true;
7389                                                 } else if ((*i)->end() >= pos) {
7390                                                         (*i)->set_end ((*i)->end()-frames); //slip the end marker back
7391                                                         moved = true;
7392                                                 }
7393                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7394                                                         (*i)->set_start (pos);  //bring the start marker to the beginning of the cut
7395                                                         moved = true;
7396                                                 } else if ((*i)->start() >= pos) {
7397                                                         (*i)->set_start ((*i)->start() -frames); //slip the end marker back
7398                                                         moved = true;
7399                                                 }
7400                                         }
7401                         } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7402                                 loc_kill_list.push_back(*i);
7403                                 moved = true;
7404                         } else if ((*i)->start() >= pos) {
7405                                 (*i)->set_start ((*i)->start() -frames);
7406                                 moved = true;
7407                         }
7408                 }
7409
7410                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7411                         _session->locations()->remove( *i );
7412                 }
7413         
7414                 if (moved) {
7415                         if (!in_command) {
7416                                 begin_reversible_command (_("cut time"));
7417                                 in_command = true;
7418                         }
7419                         XMLNode& after (_session->locations()->get_state());
7420                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7421                 }
7422         }
7423         
7424         if (tempo_too) {
7425                 XMLNode& before (_session->tempo_map().get_state());
7426
7427                 if (_session->tempo_map().cut_time (pos, frames) ) {
7428                         if (!in_command) {
7429                                 begin_reversible_command (_("cut time"));
7430                                 in_command = true;
7431                         }
7432                         XMLNode& after (_session->tempo_map().get_state());
7433                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7434                 }
7435         }
7436         
7437         if (in_command) {
7438                 commit_reversible_command ();
7439         }
7440 }
7441
7442 void
7443 Editor::fit_selection ()
7444 {
7445         if (!selection->tracks.empty()) {
7446                 fit_tracks (selection->tracks);
7447         } else {
7448                 TrackViewList tvl;
7449
7450                 /* no selected tracks - use tracks with selected regions */
7451
7452                 if (!selection->regions.empty()) {
7453                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7454                                 tvl.push_back (&(*r)->get_time_axis_view ());
7455                         }
7456
7457                         if (!tvl.empty()) {
7458                                 fit_tracks (tvl);
7459                         }
7460                 } else if (internal_editing()) {
7461                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7462                            the entered track
7463                         */
7464                         if (entered_track) {
7465                                 tvl.push_back (entered_track);
7466                                 fit_tracks (tvl);
7467                         }
7468                 }
7469         }
7470
7471 }
7472
7473 void
7474 Editor::fit_tracks (TrackViewList & tracks)
7475 {
7476         if (tracks.empty()) {
7477                 return;
7478         }
7479
7480         uint32_t child_heights = 0;
7481         int visible_tracks = 0;
7482
7483         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7484
7485                 if (!(*t)->marked_for_display()) {
7486                         continue;
7487                 }
7488
7489                 child_heights += (*t)->effective_height() - (*t)->current_height();
7490                 ++visible_tracks;
7491         }
7492
7493         /* compute the per-track height from:
7494
7495            total canvas visible height - 
7496                  height that will be taken by visible children of selected
7497                  tracks - height of the ruler/hscroll area 
7498         */
7499         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7500         double first_y_pos = DBL_MAX;
7501
7502         if (h < TimeAxisView::preset_height (HeightSmall)) {
7503                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
7504                 /* too small to be displayed */
7505                 return;
7506         }
7507
7508         undo_visual_stack.push_back (current_visual_state (true));
7509         PBD::Unwinder<bool> nsv (no_save_visual, true);
7510
7511         /* build a list of all tracks, including children */
7512
7513         TrackViewList all;
7514         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7515                 all.push_back (*i);
7516                 TimeAxisView::Children c = (*i)->get_child_list ();
7517                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7518                         all.push_back (j->get());
7519                 }
7520         }
7521
7522
7523         // find selection range.
7524         // if someone knows how to user TrackViewList::iterator for this
7525         // I'm all ears.
7526         int selected_top = -1;
7527         int selected_bottom = -1;
7528         int i = 0;
7529         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7530                 if ((*t)->marked_for_display ()) {
7531                         if (tracks.contains(*t)) {
7532                                 if (selected_top == -1) {
7533                                         selected_top = i;
7534                                 }
7535                                 selected_bottom = i;
7536                         }
7537                 }
7538         }
7539
7540         i = 0;
7541         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7542                 if ((*t)->marked_for_display ()) {
7543                         if (tracks.contains(*t)) {
7544                                 (*t)->set_height (h);
7545                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7546                         } else {
7547                                 if (i > selected_top && i < selected_bottom) {
7548                                         hide_track_in_display (*t);
7549                                 }
7550                         }
7551                 }
7552         }
7553
7554         /*
7555            set the controls_layout height now, because waiting for its size
7556            request signal handler will cause the vertical adjustment setting to fail
7557         */
7558
7559         controls_layout.property_height () = _full_canvas_height;
7560         vertical_adjustment.set_value (first_y_pos);
7561
7562         redo_visual_stack.push_back (current_visual_state (true));
7563
7564         visible_tracks_selector.set_text (_("Sel"));
7565 }
7566
7567 void
7568 Editor::save_visual_state (uint32_t n)
7569 {
7570         while (visual_states.size() <= n) {
7571                 visual_states.push_back (0);
7572         }
7573
7574         if (visual_states[n] != 0) {
7575                 delete visual_states[n];
7576         }
7577
7578         visual_states[n] = current_visual_state (true);
7579         gdk_beep ();
7580 }
7581
7582 void
7583 Editor::goto_visual_state (uint32_t n)
7584 {
7585         if (visual_states.size() <= n) {
7586                 return;
7587         }
7588
7589         if (visual_states[n] == 0) {
7590                 return;
7591         }
7592
7593         use_visual_state (*visual_states[n]);
7594 }
7595
7596 void
7597 Editor::start_visual_state_op (uint32_t n)
7598 {
7599         save_visual_state (n);
7600         
7601         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7602         char buf[32];
7603         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7604         pup->set_text (buf);
7605         pup->touch();
7606 }
7607
7608 void
7609 Editor::cancel_visual_state_op (uint32_t n)
7610 {
7611         goto_visual_state (n);
7612 }
7613
7614 void
7615 Editor::toggle_region_mute ()
7616 {
7617         if (_ignore_region_action) {
7618                 return;
7619         }
7620
7621         RegionSelection rs = get_regions_from_selection_and_entered ();
7622
7623         if (rs.empty ()) {
7624                 return;
7625         }
7626
7627         if (rs.size() > 1) {
7628                 begin_reversible_command (_("mute regions"));
7629         } else {
7630                 begin_reversible_command (_("mute region"));
7631         }
7632
7633         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7634
7635                 (*i)->region()->playlist()->clear_changes ();
7636                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7637                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7638
7639         }
7640
7641         commit_reversible_command ();
7642 }
7643
7644 void
7645 Editor::combine_regions ()
7646 {
7647         /* foreach track with selected regions, take all selected regions
7648            and join them into a new region containing the subregions (as a
7649            playlist)
7650         */
7651
7652         typedef set<RouteTimeAxisView*> RTVS;
7653         RTVS tracks;
7654
7655         if (selection->regions.empty()) {
7656                 return;
7657         }
7658
7659         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7660                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7661
7662                 if (rtv) {
7663                         tracks.insert (rtv);
7664                 }
7665         }
7666
7667         begin_reversible_command (_("combine regions"));
7668
7669         vector<RegionView*> new_selection;
7670
7671         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7672                 RegionView* rv;
7673
7674                 if ((rv = (*i)->combine_regions ()) != 0) {
7675                         new_selection.push_back (rv);
7676                 }
7677         }
7678
7679         selection->clear_regions ();
7680         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7681                 selection->add (*i);
7682         }
7683
7684         commit_reversible_command ();
7685 }
7686
7687 void
7688 Editor::uncombine_regions ()
7689 {
7690         typedef set<RouteTimeAxisView*> RTVS;
7691         RTVS tracks;
7692
7693         if (selection->regions.empty()) {
7694                 return;
7695         }
7696
7697         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7698                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7699
7700                 if (rtv) {
7701                         tracks.insert (rtv);
7702                 }
7703         }
7704
7705         begin_reversible_command (_("uncombine regions"));
7706
7707         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7708                 (*i)->uncombine_regions ();
7709         }
7710
7711         commit_reversible_command ();
7712 }
7713
7714 void
7715 Editor::toggle_midi_input_active (bool flip_others)
7716 {
7717         bool onoff = false;
7718         boost::shared_ptr<RouteList> rl (new RouteList);
7719
7720         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7721                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7722
7723                 if (!rtav) {
7724                         continue;
7725                 }
7726
7727                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7728
7729                 if (mt) {
7730                         rl->push_back (rtav->route());
7731                         onoff = !mt->input_active();
7732                 }
7733         }
7734         
7735         _session->set_exclusive_input_active (rl, onoff, flip_others);
7736 }
7737
7738 void
7739 Editor::lock ()
7740 {
7741         if (!lock_dialog) {
7742                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
7743
7744                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
7745                 lock_dialog->get_vbox()->pack_start (*padlock);
7746
7747                 ArdourButton* b = manage (new ArdourButton);
7748                 b->set_name ("lock button");
7749                 b->set_text (_("Click to unlock"));
7750                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
7751                 lock_dialog->get_vbox()->pack_start (*b);
7752                 
7753                 lock_dialog->get_vbox()->show_all ();
7754                 lock_dialog->set_size_request (200, 200);
7755         }
7756         
7757 #ifdef __APPLE__
7758         /* The global menu bar continues to be accessible to applications
7759            with modal dialogs, which means that we need to desensitize
7760            all items in the menu bar. Since those items are really just
7761            proxies for actions, that means disabling all actions.
7762         */
7763         ActionManager::disable_all_actions ();
7764 #endif
7765         lock_dialog->present ();
7766 }
7767
7768 void
7769 Editor::unlock ()
7770 {
7771         lock_dialog->hide ();
7772         
7773 #ifdef __APPLE__
7774         ActionManager::pop_action_state ();
7775 #endif  
7776
7777         if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
7778                 start_lock_event_timing ();
7779         }
7780 }
7781
7782 void
7783 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7784 {
7785         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
7786 }
7787
7788 void
7789 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
7790 {
7791         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
7792         Gtkmm2ext::UI::instance()->flush_pending ();
7793 }
7794
7795 void
7796 Editor::bring_all_sources_into_session ()
7797 {
7798         if (!_session) {
7799                 return;
7800         }
7801
7802         Gtk::Label msg;
7803         ArdourDialog w (_("Moving embedded files into session folder"));
7804         w.get_vbox()->pack_start (msg);
7805         w.present ();
7806         
7807         /* flush all pending GUI events because we're about to start copying
7808          * files
7809          */
7810         
7811         Gtkmm2ext::UI::instance()->flush_pending ();
7812
7813         cerr << " Do it\n";
7814
7815         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
7816 }