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