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