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