add new regions created by split to selection after operation is complete
[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                 set_mouse_mode (MouseObject);
2793
2794                 commit_reversible_command ();
2795         }
2796 }
2797
2798 struct PlaylistState {
2799     boost::shared_ptr<Playlist> playlist;
2800     XMLNode*  before;
2801 };
2802
2803 /** Take tracks from get_tracks_for_range_action and cut any regions
2804  *  on those tracks so that the tracks are empty over the time
2805  *  selection.
2806  */
2807 void
2808 Editor::separate_region_from_selection ()
2809 {
2810         /* preferentially use *all* ranges in the time selection if we're in range mode
2811            to allow discontiguous operation, since get_edit_op_range() currently
2812            returns a single range.
2813         */
2814
2815         if (!selection->time.empty()) {
2816
2817                 separate_regions_between (selection->time);
2818
2819         } else {
2820
2821                 framepos_t start;
2822                 framepos_t end;
2823
2824                 if (get_edit_op_range (start, end)) {
2825
2826                         AudioRange ar (start, end, 1);
2827                         TimeSelection ts;
2828                         ts.push_back (ar);
2829
2830                         separate_regions_between (ts);
2831                 }
2832         }
2833 }
2834
2835 void
2836 Editor::separate_region_from_punch ()
2837 {
2838         Location* loc  = _session->locations()->auto_punch_location();
2839         if (loc) {
2840                 separate_regions_using_location (*loc);
2841         }
2842 }
2843
2844 void
2845 Editor::separate_region_from_loop ()
2846 {
2847         Location* loc  = _session->locations()->auto_loop_location();
2848         if (loc) {
2849                 separate_regions_using_location (*loc);
2850         }
2851 }
2852
2853 void
2854 Editor::separate_regions_using_location (Location& loc)
2855 {
2856         if (loc.is_mark()) {
2857                 return;
2858         }
2859
2860         AudioRange ar (loc.start(), loc.end(), 1);
2861         TimeSelection ts;
2862
2863         ts.push_back (ar);
2864
2865         separate_regions_between (ts);
2866 }
2867
2868 /** Separate regions under the selected region */
2869 void
2870 Editor::separate_under_selected_regions ()
2871 {
2872         vector<PlaylistState> playlists;
2873
2874         RegionSelection rs;
2875
2876         rs = get_regions_from_selection_and_entered();
2877
2878         if (!_session || rs.empty()) {
2879                 return;
2880         }
2881
2882         begin_reversible_command (_("separate region under"));
2883
2884         list<boost::shared_ptr<Region> > regions_to_remove;
2885
2886         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2887                 // we can't just remove the region(s) in this loop because
2888                 // this removes them from the RegionSelection, and they thus
2889                 // disappear from underneath the iterator, and the ++i above
2890                 // SEGVs in a puzzling fashion.
2891
2892                 // so, first iterate over the regions to be removed from rs and
2893                 // add them to the regions_to_remove list, and then
2894                 // iterate over the list to actually remove them.
2895
2896                 regions_to_remove.push_back ((*i)->region());
2897         }
2898
2899         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2900
2901                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2902
2903                 if (!playlist) {
2904                         // is this check necessary?
2905                         continue;
2906                 }
2907
2908                 vector<PlaylistState>::iterator i;
2909
2910                 //only take state if this is a new playlist.
2911                 for (i = playlists.begin(); i != playlists.end(); ++i) {
2912                         if ((*i).playlist == playlist) {
2913                                 break;
2914                         }
2915                 }
2916
2917                 if (i == playlists.end()) {
2918
2919                         PlaylistState before;
2920                         before.playlist = playlist;
2921                         before.before = &playlist->get_state();
2922
2923                         playlist->freeze ();
2924                         playlists.push_back(before);
2925                 }
2926
2927                 //Partition on the region bounds
2928                 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2929
2930                 //Re-add region that was just removed due to the partition operation
2931                 playlist->add_region( (*rl), (*rl)->first_frame() );
2932         }
2933
2934         vector<PlaylistState>::iterator pl;
2935
2936         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2937                 (*pl).playlist->thaw ();
2938                 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2939         }
2940
2941         commit_reversible_command ();
2942 }
2943
2944 void
2945 Editor::crop_region_to_selection ()
2946 {
2947         if (!selection->time.empty()) {
2948
2949                 crop_region_to (selection->time.start(), selection->time.end_frame());
2950
2951         } else {
2952
2953                 framepos_t start;
2954                 framepos_t end;
2955
2956                 if (get_edit_op_range (start, end)) {
2957                         crop_region_to (start, end);
2958                 }
2959         }
2960
2961 }
2962
2963 void
2964 Editor::crop_region_to (framepos_t start, framepos_t end)
2965 {
2966         vector<boost::shared_ptr<Playlist> > playlists;
2967         boost::shared_ptr<Playlist> playlist;
2968         TrackViewList ts;
2969
2970         if (selection->tracks.empty()) {
2971                 ts = track_views.filter_to_unique_playlists();
2972         } else {
2973                 ts = selection->tracks.filter_to_unique_playlists ();
2974         }
2975
2976         sort_track_selection (ts);
2977
2978         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2979
2980                 RouteTimeAxisView* rtv;
2981
2982                 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2983
2984                         boost::shared_ptr<Track> t = rtv->track();
2985
2986                         if (t != 0 && ! t->destructive()) {
2987
2988                                 if ((playlist = rtv->playlist()) != 0) {
2989                                         playlists.push_back (playlist);
2990                                 }
2991                         }
2992                 }
2993         }
2994
2995         if (playlists.empty()) {
2996                 return;
2997         }
2998
2999         framepos_t the_start;
3000         framepos_t the_end;
3001         framepos_t cnt;
3002
3003         begin_reversible_command (_("trim to selection"));
3004
3005         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3006
3007                 boost::shared_ptr<Region> region;
3008
3009                 the_start = start;
3010
3011                 if ((region = (*i)->top_region_at(the_start)) == 0) {
3012                         continue;
3013                 }
3014
3015                 /* now adjust lengths to that we do the right thing
3016                    if the selection extends beyond the region
3017                 */
3018
3019                 the_start = max (the_start, (framepos_t) region->position());
3020                 if (max_framepos - the_start < region->length()) {
3021                         the_end = the_start + region->length() - 1;
3022                 } else {
3023                         the_end = max_framepos;
3024                 }
3025                 the_end = min (end, the_end);
3026                 cnt = the_end - the_start + 1;
3027
3028                 region->clear_changes ();
3029                 region->trim_to (the_start, cnt);
3030                 _session->add_command (new StatefulDiffCommand (region));
3031         }
3032
3033         commit_reversible_command ();
3034 }
3035
3036 void
3037 Editor::region_fill_track ()
3038 {
3039         RegionSelection rs = get_regions_from_selection_and_entered ();
3040
3041         if (!_session || rs.empty()) {
3042                 return;
3043         }
3044
3045         framepos_t const end = _session->current_end_frame ();
3046
3047         begin_reversible_command (Operations::region_fill);
3048
3049         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3050
3051                 boost::shared_ptr<Region> region ((*i)->region());
3052
3053                 boost::shared_ptr<Playlist> pl = region->playlist();
3054
3055                 if (end <= region->last_frame()) {
3056                         return;
3057                 }
3058
3059                 double times = (double) (end - region->last_frame()) / (double) region->length();
3060
3061                 if (times == 0) {
3062                         return;
3063                 }
3064
3065                 pl->clear_changes ();
3066                 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
3067                 _session->add_command (new StatefulDiffCommand (pl));
3068         }
3069
3070         commit_reversible_command ();
3071 }
3072
3073 void
3074 Editor::region_fill_selection ()
3075 {
3076         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3077                 return;
3078         }
3079
3080         if (selection->time.empty()) {
3081                 return;
3082         }
3083
3084         boost::shared_ptr<Region> region = _regions->get_single_selection ();
3085         if (region == 0) {
3086                 return;
3087         }
3088
3089         framepos_t start = selection->time[clicked_selection].start;
3090         framepos_t end = selection->time[clicked_selection].end;
3091
3092         boost::shared_ptr<Playlist> playlist;
3093
3094         if (selection->tracks.empty()) {
3095                 return;
3096         }
3097
3098         framepos_t selection_length = end - start;
3099         float times = (float)selection_length / region->length();
3100
3101         begin_reversible_command (Operations::fill_selection);
3102
3103         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3104
3105         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
3106
3107                 if ((playlist = (*i)->playlist()) == 0) {
3108                         continue;
3109                 }
3110
3111                 playlist->clear_changes ();
3112                 playlist->add_region (RegionFactory::create (region, true), start, times);
3113                 _session->add_command (new StatefulDiffCommand (playlist));
3114         }
3115
3116         commit_reversible_command ();
3117 }
3118
3119 void
3120 Editor::set_region_sync_position ()
3121 {
3122         set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3123 }
3124
3125 void
3126 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3127 {
3128         bool in_command = false;
3129
3130         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3131
3132                 if (!(*r)->region()->covers (where)) {
3133                         continue;
3134                 }
3135
3136                 boost::shared_ptr<Region> region ((*r)->region());
3137
3138                 if (!in_command) {
3139                         begin_reversible_command (_("set sync point"));
3140                         in_command = true;
3141                 }
3142
3143                 region->clear_changes ();
3144                 region->set_sync_position (where);
3145                 _session->add_command(new StatefulDiffCommand (region));
3146         }
3147
3148         if (in_command) {
3149                 commit_reversible_command ();
3150         }
3151 }
3152
3153 /** Remove the sync positions of the selection */
3154 void
3155 Editor::remove_region_sync ()
3156 {
3157         RegionSelection rs = get_regions_from_selection_and_entered ();
3158
3159         if (rs.empty()) {
3160                 return;
3161         }
3162
3163         begin_reversible_command (_("remove region sync"));
3164
3165         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3166
3167                 (*i)->region()->clear_changes ();
3168                 (*i)->region()->clear_sync_position ();
3169                 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3170         }
3171
3172         commit_reversible_command ();
3173 }
3174
3175 void
3176 Editor::naturalize_region ()
3177 {
3178         RegionSelection rs = get_regions_from_selection_and_entered ();
3179
3180         if (rs.empty()) {
3181                 return;
3182         }
3183
3184         if (rs.size() > 1) {
3185                 begin_reversible_command (_("move regions to original position"));
3186         } else {
3187                 begin_reversible_command (_("move region to original position"));
3188         }
3189
3190         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3191                 (*i)->region()->clear_changes ();
3192                 (*i)->region()->move_to_natural_position ();
3193                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3194         }
3195
3196         commit_reversible_command ();
3197 }
3198
3199 void
3200 Editor::align_regions (RegionPoint what)
3201 {
3202         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3203
3204         if (rs.empty()) {
3205                 return;
3206         }
3207
3208         begin_reversible_command (_("align selection"));
3209
3210         framepos_t const position = get_preferred_edit_position ();
3211
3212         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3213                 align_region_internal ((*i)->region(), what, position);
3214         }
3215
3216         commit_reversible_command ();
3217 }
3218
3219 struct RegionSortByTime {
3220     bool operator() (const RegionView* a, const RegionView* b) {
3221             return a->region()->position() < b->region()->position();
3222     }
3223 };
3224
3225 void
3226 Editor::align_regions_relative (RegionPoint point)
3227 {
3228         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3229
3230         if (rs.empty()) {
3231                 return;
3232         }
3233
3234         framepos_t const position = get_preferred_edit_position ();
3235
3236         framepos_t distance = 0;
3237         framepos_t pos = 0;
3238         int dir = 1;
3239
3240         list<RegionView*> sorted;
3241         rs.by_position (sorted);
3242
3243         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3244
3245         switch (point) {
3246         case Start:
3247                 pos = position;
3248                 if (position > r->position()) {
3249                         distance = position - r->position();
3250                 } else {
3251                         distance = r->position() - position;
3252                         dir = -1;
3253                 }
3254                 break;
3255
3256         case End:
3257                 if (position > r->last_frame()) {
3258                         distance = position - r->last_frame();
3259                         pos = r->position() + distance;
3260                 } else {
3261                         distance = r->last_frame() - position;
3262                         pos = r->position() - distance;
3263                         dir = -1;
3264                 }
3265                 break;
3266
3267         case SyncPoint:
3268                 pos = r->adjust_to_sync (position);
3269                 if (pos > r->position()) {
3270                         distance = pos - r->position();
3271                 } else {
3272                         distance = r->position() - pos;
3273                         dir = -1;
3274                 }
3275                 break;
3276         }
3277
3278         if (pos == r->position()) {
3279                 return;
3280         }
3281
3282         begin_reversible_command (_("align selection (relative)"));
3283
3284         /* move first one specially */
3285
3286         r->clear_changes ();
3287         r->set_position (pos);
3288         _session->add_command(new StatefulDiffCommand (r));
3289
3290         /* move rest by the same amount */
3291
3292         sorted.pop_front();
3293
3294         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3295
3296                 boost::shared_ptr<Region> region ((*i)->region());
3297
3298                 region->clear_changes ();
3299
3300                 if (dir > 0) {
3301                         region->set_position (region->position() + distance);
3302                 } else {
3303                         region->set_position (region->position() - distance);
3304                 }
3305
3306                 _session->add_command(new StatefulDiffCommand (region));
3307
3308         }
3309
3310         commit_reversible_command ();
3311 }
3312
3313 void
3314 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3315 {
3316         begin_reversible_command (_("align region"));
3317         align_region_internal (region, point, position);
3318         commit_reversible_command ();
3319 }
3320
3321 void
3322 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3323 {
3324         region->clear_changes ();
3325
3326         switch (point) {
3327         case SyncPoint:
3328                 region->set_position (region->adjust_to_sync (position));
3329                 break;
3330
3331         case End:
3332                 if (position > region->length()) {
3333                         region->set_position (position - region->length());
3334                 }
3335                 break;
3336
3337         case Start:
3338                 region->set_position (position);
3339                 break;
3340         }
3341
3342         _session->add_command(new StatefulDiffCommand (region));
3343 }
3344
3345 void
3346 Editor::trim_region_front ()
3347 {
3348         trim_region (true);
3349 }
3350
3351 void
3352 Editor::trim_region_back ()
3353 {
3354         trim_region (false);
3355 }
3356
3357 void
3358 Editor::trim_region (bool front)
3359 {
3360         framepos_t where = get_preferred_edit_position();
3361         RegionSelection rs = get_regions_from_selection_and_edit_point ();
3362
3363         if (rs.empty()) {
3364                 return;
3365         }
3366
3367         begin_reversible_command (front ? _("trim front") : _("trim back"));
3368
3369         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3370                 if (!(*i)->region()->locked()) {
3371
3372                         (*i)->region()->clear_changes ();
3373
3374                         if (front) {
3375                                 (*i)->region()->trim_front (where);
3376                                 maybe_locate_with_edit_preroll ( where );
3377                         } else {
3378                                 (*i)->region()->trim_end (where);
3379                                 maybe_locate_with_edit_preroll ( where );
3380                         }
3381
3382                         _session->add_command (new StatefulDiffCommand ((*i)->region()));
3383                 }
3384         }
3385
3386         commit_reversible_command ();
3387 }
3388
3389 /** Trim the end of the selected regions to the position of the edit cursor */
3390 void
3391 Editor::trim_region_to_loop ()
3392 {
3393         Location* loc = _session->locations()->auto_loop_location();
3394         if (!loc) {
3395                 return;
3396         }
3397         trim_region_to_location (*loc, _("trim to loop"));
3398 }
3399
3400 void
3401 Editor::trim_region_to_punch ()
3402 {
3403         Location* loc = _session->locations()->auto_punch_location();
3404         if (!loc) {
3405                 return;
3406         }
3407         trim_region_to_location (*loc, _("trim to punch"));
3408 }
3409
3410 void
3411 Editor::trim_region_to_location (const Location& loc, const char* str)
3412 {
3413         RegionSelection rs = get_regions_from_selection_and_entered ();
3414
3415         begin_reversible_command (str);
3416
3417         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3418                 RegionView* rv = (*x);
3419
3420                 /* require region to span proposed trim */
3421                 switch (rv->region()->coverage (loc.start(), loc.end())) {
3422                 case Evoral::OverlapInternal:
3423                         break;
3424                 default:
3425                         continue;
3426                 }
3427
3428                 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3429                 if (!tav) {
3430                         return;
3431                 }
3432
3433                 float speed = 1.0;
3434                 framepos_t start;
3435                 framepos_t end;
3436
3437                 if (tav->track() != 0) {
3438                         speed = tav->track()->speed();
3439                 }
3440
3441                 start = session_frame_to_track_frame (loc.start(), speed);
3442                 end = session_frame_to_track_frame (loc.end(), speed);
3443
3444                 rv->region()->clear_changes ();
3445                 rv->region()->trim_to (start, (end - start));
3446                 _session->add_command(new StatefulDiffCommand (rv->region()));
3447         }
3448
3449         commit_reversible_command ();
3450 }
3451
3452 void
3453 Editor::trim_region_to_previous_region_end ()
3454 {
3455         return trim_to_region(false);
3456 }
3457
3458 void
3459 Editor::trim_region_to_next_region_start ()
3460 {
3461         return trim_to_region(true);
3462 }
3463
3464 void
3465 Editor::trim_to_region(bool forward)
3466 {
3467         RegionSelection rs = get_regions_from_selection_and_entered ();
3468
3469         begin_reversible_command (_("trim to region"));
3470
3471         boost::shared_ptr<Region> next_region;
3472
3473         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3474
3475                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3476
3477                 if (!arv) {
3478                         continue;
3479                 }
3480
3481                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3482
3483                 if (!atav) {
3484                         return;
3485                 }
3486
3487                 float speed = 1.0;
3488
3489                 if (atav->track() != 0) {
3490                         speed = atav->track()->speed();
3491                 }
3492
3493
3494                 boost::shared_ptr<Region> region = arv->region();
3495                 boost::shared_ptr<Playlist> playlist (region->playlist());
3496
3497                 region->clear_changes ();
3498
3499                 if (forward) {
3500
3501                     next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3502
3503                     if (!next_region) {
3504                         continue;
3505                     }
3506
3507                     region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3508                     arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3509                 }
3510                 else {
3511
3512                     next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3513
3514                     if(!next_region){
3515                         continue;
3516                     }
3517
3518                     region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3519
3520                     arv->region_changed (ARDOUR::bounds_change);
3521                 }
3522
3523                 _session->add_command(new StatefulDiffCommand (region));
3524         }
3525
3526         commit_reversible_command ();
3527 }
3528
3529 void
3530 Editor::unfreeze_route ()
3531 {
3532         if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3533                 return;
3534         }
3535
3536         clicked_routeview->track()->unfreeze ();
3537 }
3538
3539 void*
3540 Editor::_freeze_thread (void* arg)
3541 {
3542         return static_cast<Editor*>(arg)->freeze_thread ();
3543 }
3544
3545 void*
3546 Editor::freeze_thread ()
3547 {
3548         /* create event pool because we may need to talk to the session */
3549         SessionEvent::create_per_thread_pool ("freeze events", 64);
3550         /* create per-thread buffers for process() tree to use */
3551         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3552         current_interthread_info->done = true;
3553         return 0;
3554 }
3555
3556 void
3557 Editor::freeze_route ()
3558 {
3559         if (!_session) {
3560                 return;
3561         }
3562
3563         /* stop transport before we start. this is important */
3564
3565         _session->request_transport_speed (0.0);
3566         
3567         /* wait for just a little while, because the above call is asynchronous */
3568
3569         Glib::usleep (250000);
3570
3571         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3572                 return;
3573         }
3574
3575         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3576                 MessageDialog d (
3577                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3578                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3579                         );
3580                 d.set_title (_("Cannot freeze"));
3581                 d.run ();
3582                 return;
3583         }
3584
3585         if (clicked_routeview->track()->has_external_redirects()) {
3586                 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"
3587                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3588                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3589
3590                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3591                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3592                 d.set_title (_("Freeze Limits"));
3593
3594                 int response = d.run ();
3595
3596                 switch (response) {
3597                 case Gtk::RESPONSE_CANCEL:
3598                         return;
3599                 default:
3600                         break;
3601                 }
3602         }
3603
3604         InterThreadInfo itt;
3605         current_interthread_info = &itt;
3606
3607         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3608
3609         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3610
3611         set_canvas_cursor (_cursors->wait);
3612
3613         while (!itt.done && !itt.cancel) {
3614                 gtk_main_iteration ();
3615         }
3616
3617         current_interthread_info = 0;
3618         set_canvas_cursor (current_canvas_cursor);
3619 }
3620
3621 void
3622 Editor::bounce_range_selection (bool replace, bool enable_processing)
3623 {
3624         if (selection->time.empty()) {
3625                 return;
3626         }
3627
3628         TrackSelection views = selection->tracks;
3629
3630         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3631
3632                 if (enable_processing) {
3633
3634                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3635
3636                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3637                                 MessageDialog d (
3638                                         _("You can't perform this operation because the processing of the signal "
3639                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3640                                           "You can do this without processing, which is a different operation.")
3641                                         );
3642                                 d.set_title (_("Cannot bounce"));
3643                                 d.run ();
3644                                 return;
3645                         }
3646                 }
3647         }
3648
3649         framepos_t start = selection->time[clicked_selection].start;
3650         framepos_t end = selection->time[clicked_selection].end;
3651         framepos_t cnt = end - start + 1;
3652
3653         begin_reversible_command (_("bounce range"));
3654
3655         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3656
3657                 RouteTimeAxisView* rtv;
3658
3659                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3660                         continue;
3661                 }
3662
3663                 boost::shared_ptr<Playlist> playlist;
3664
3665                 if ((playlist = rtv->playlist()) == 0) {
3666                         return;
3667                 }
3668
3669                 InterThreadInfo itt;
3670
3671                 playlist->clear_changes ();
3672                 playlist->clear_owned_changes ();
3673
3674                 boost::shared_ptr<Region> r;
3675
3676                 if (enable_processing) {
3677                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3678                 } else {
3679                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3680                 }
3681
3682                 if (!r) {
3683                         continue;
3684                 }
3685
3686                 if (replace) {
3687                         list<AudioRange> ranges;
3688                         ranges.push_back (AudioRange (start, start+cnt, 0));
3689                         playlist->cut (ranges); // discard result
3690                         playlist->add_region (r, start);
3691                 }
3692
3693                 vector<Command*> cmds;
3694                 playlist->rdiff (cmds);
3695                 _session->add_commands (cmds);
3696
3697                 _session->add_command (new StatefulDiffCommand (playlist));
3698         }
3699
3700         commit_reversible_command ();
3701 }
3702
3703 /** Delete selected regions, automation points or a time range */
3704 void
3705 Editor::delete_ ()
3706 {
3707         cut_copy (Delete);
3708 }
3709
3710 /** Cut selected regions, automation points or a time range */
3711 void
3712 Editor::cut ()
3713 {
3714         cut_copy (Cut);
3715 }
3716
3717 /** Copy selected regions, automation points or a time range */
3718 void
3719 Editor::copy ()
3720 {
3721         cut_copy (Copy);
3722 }
3723
3724
3725 /** @return true if a Cut, Copy or Clear is possible */
3726 bool
3727 Editor::can_cut_copy () const
3728 {
3729         switch (effective_mouse_mode()) {
3730
3731         case MouseObject:
3732                 if (!selection->regions.empty() || !selection->points.empty()) {
3733                         return true;
3734                 }
3735                 break;
3736
3737         case MouseRange:
3738                 if (!selection->time.empty()) {
3739                         return true;
3740                 }
3741                 break;
3742
3743         default:
3744                 break;
3745         }
3746
3747         return false;
3748 }
3749
3750
3751 /** Cut, copy or clear selected regions, automation points or a time range.
3752  * @param op Operation (Delete, Cut, Copy or Clear)
3753  */
3754 void
3755 Editor::cut_copy (CutCopyOp op)
3756 {
3757         /* only cancel selection if cut/copy is successful.*/
3758
3759         string opname;
3760
3761         switch (op) {
3762         case Delete:
3763                 opname = _("delete");
3764                 break;
3765         case Cut:
3766                 opname = _("cut");
3767                 break;
3768         case Copy:
3769                 opname = _("copy");
3770                 break;
3771         case Clear:
3772                 opname = _("clear");
3773                 break;
3774         }
3775
3776         /* if we're deleting something, and the mouse is still pressed,
3777            the thing we started a drag for will be gone when we release
3778            the mouse button(s). avoid this. see part 2 at the end of
3779            this function.
3780         */
3781
3782         if (op == Delete || op == Cut || op == Clear) {
3783                 if (_drags->active ()) {
3784                         _drags->abort ();
3785                 }
3786         }
3787
3788         if ( op != Delete )  //"Delete" doesn't change copy/paste buf
3789                 cut_buffer->clear ();
3790
3791         if (entered_marker) {
3792
3793                 /* cut/delete op while pointing at a marker */
3794
3795                 bool ignored;
3796                 Location* loc = find_location_from_marker (entered_marker, ignored);
3797
3798                 if (_session && loc) {
3799                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3800                 }
3801
3802                 _drags->abort ();
3803                 return;
3804         }
3805
3806         if (internal_editing()) {
3807
3808                 switch (effective_mouse_mode()) {
3809                 case MouseObject:
3810                 case MouseRange:
3811                         begin_reversible_command (opname + ' ' + X_("MIDI"));
3812                         cut_copy_midi (op);
3813                         commit_reversible_command ();
3814                         break;
3815                 default:
3816                         break;
3817                 }
3818
3819                 return;
3820         }
3821
3822         bool did_edit = false;
3823
3824         switch (effective_mouse_mode()) {
3825         case MouseGain:
3826                 if (!selection->points.empty()) {
3827                         begin_reversible_command (opname + _(" points"));
3828                         did_edit = true;
3829                         cut_copy_points (op);
3830                         if (op == Cut || op == Delete) {
3831                                 selection->clear_points ();
3832                         }
3833                 }
3834                 break;
3835                 
3836         case MouseObject: 
3837
3838                 if (!selection->regions.empty() || !selection->points.empty()) {
3839
3840                         string thing_name;
3841
3842                         if (selection->regions.empty()) {
3843                                 thing_name = _("points");
3844                         } else if (selection->points.empty()) {
3845                                 thing_name = _("regions");
3846                         } else {
3847                                 thing_name = _("objects");
3848                         }
3849                 
3850                         begin_reversible_command (opname + ' ' + thing_name);
3851                         did_edit = true;
3852
3853                         if (!selection->regions.empty()) {
3854                                 cut_copy_regions (op, selection->regions);
3855                                 
3856                                 if (op == Cut || op == Delete) {
3857                                         selection->clear_regions ();
3858                                 }
3859                         }
3860                         
3861                         if (!selection->points.empty()) {
3862                                 cut_copy_points (op);
3863                                 
3864                                 if (op == Cut || op == Delete) {
3865                                         selection->clear_points ();
3866                                 }
3867                         }
3868                 } 
3869                 break;
3870                         
3871         case MouseRange:
3872                 if (selection->time.empty()) {
3873                         framepos_t start, end;
3874                         /* no time selection, see if we can get an edit range
3875                            and use that.
3876                         */
3877                         if (get_edit_op_range (start, end)) {
3878                                 selection->set (start, end);
3879                         }
3880                 }
3881                 if (!selection->time.empty()) {
3882                         begin_reversible_command (opname + _(" range"));
3883
3884                         did_edit = true;
3885                         cut_copy_ranges (op);
3886                         
3887                         if (op == Cut || op == Delete) {
3888                                 selection->clear_time ();
3889                         }
3890                 }
3891                 break;
3892                 
3893         default:
3894                 break;
3895         }
3896         
3897         if (did_edit) {
3898                 commit_reversible_command ();   
3899         }
3900         
3901         if (op == Delete || op == Cut || op == Clear) {
3902                 _drags->abort ();
3903         }
3904 }
3905
3906 struct AutomationRecord {
3907         AutomationRecord () : state (0) {}
3908         AutomationRecord (XMLNode* s) : state (s) {}
3909         
3910         XMLNode* state; ///< state before any operation
3911         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3912 };
3913
3914 /** Cut, copy or clear selected automation points.
3915  *  @param op Operation (Cut, Copy or Clear)
3916  */
3917 void
3918 Editor::cut_copy_points (CutCopyOp op)
3919 {
3920         if (selection->points.empty ()) {
3921                 return;
3922         }
3923
3924         /* XXX: not ideal, as there may be more than one track involved in the point selection */
3925         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3926
3927         /* Keep a record of the AutomationLists that we end up using in this operation */
3928         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3929         Lists lists;
3930
3931         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3932         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3933                 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3934                 if (lists.find (al) == lists.end ()) {
3935                         /* We haven't seen this list yet, so make a record for it.  This includes
3936                            taking a copy of its current state, in case this is needed for undo later.
3937                         */
3938                         lists[al] = AutomationRecord (&al->get_state ());
3939                 }
3940         }
3941
3942         if (op == Cut || op == Copy) {
3943                 /* This operation will involve putting things in the cut buffer, so create an empty
3944                    ControlList for each of our source lists to put the cut buffer data in.
3945                 */
3946                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3947                         i->second.copy = i->first->create (i->first->parameter ());
3948                 }
3949
3950                 /* Add all selected points to the relevant copy ControlLists */
3951                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3952                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3953                         AutomationList::const_iterator j = (*i)->model ();
3954                         lists[al].copy->add ((*j)->when, (*j)->value);
3955                 }
3956
3957                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3958                         /* Correct this copy list so that it starts at time 0 */
3959                         double const start = i->second.copy->front()->when;
3960                         for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3961                                 (*j)->when -= start;
3962                         }
3963
3964                         /* And add it to the cut buffer */
3965                         cut_buffer->add (i->second.copy);
3966                 }
3967         }
3968                 
3969         if (op == Delete || op == Cut) {
3970                 /* This operation needs to remove things from the main AutomationList, so do that now */
3971                 
3972                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3973                         i->first->freeze ();
3974                 }
3975
3976                 /* Remove each selected point from its AutomationList */
3977                 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3978                         boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3979                         al->erase ((*i)->model ());
3980                 }
3981
3982                 /* Thaw the lists and add undo records for them */
3983                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3984                         boost::shared_ptr<AutomationList> al = i->first;
3985                         al->thaw ();
3986                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3987                 }
3988         }
3989 }
3990
3991 /** Cut, copy or clear selected automation points.
3992  * @param op Operation (Cut, Copy or Clear)
3993  */
3994 void
3995 Editor::cut_copy_midi (CutCopyOp op)
3996 {
3997         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3998                 MidiRegionView* mrv = *i;
3999                 mrv->cut_copy_clear (op);
4000         }
4001 }
4002
4003
4004
4005 struct lt_playlist {
4006     bool operator () (const PlaylistState& a, const PlaylistState& b) {
4007             return a.playlist < b.playlist;
4008     }
4009 };
4010
4011 struct PlaylistMapping {
4012     TimeAxisView* tv;
4013     boost::shared_ptr<Playlist> pl;
4014
4015     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4016 };
4017
4018 /** Remove `clicked_regionview' */
4019 void
4020 Editor::remove_clicked_region ()
4021 {
4022         if (clicked_routeview == 0 || clicked_regionview == 0) {
4023                 return;
4024         }
4025
4026         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4027
4028         playlist->clear_changes ();
4029         playlist->clear_owned_changes ();
4030         playlist->remove_region (clicked_regionview->region());
4031         if (Config->get_edit_mode() == Ripple)
4032                 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4033
4034         /* We might have removed regions, which alters other regions' layering_index,
4035            so we need to do a recursive diff here.
4036         */
4037         vector<Command*> cmds;
4038         playlist->rdiff (cmds);
4039         _session->add_commands (cmds);
4040         
4041         _session->add_command(new StatefulDiffCommand (playlist));
4042         commit_reversible_command ();
4043 }
4044
4045
4046 /** Remove the selected regions */
4047 void
4048 Editor::remove_selected_regions ()
4049 {
4050         RegionSelection rs = get_regions_from_selection_and_entered ();
4051
4052         if (!_session || rs.empty()) {
4053                 return;
4054         }
4055
4056         begin_reversible_command (_("remove region"));
4057
4058         list<boost::shared_ptr<Region> > regions_to_remove;
4059
4060         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4061                 // we can't just remove the region(s) in this loop because
4062                 // this removes them from the RegionSelection, and they thus
4063                 // disappear from underneath the iterator, and the ++i above
4064                 // SEGVs in a puzzling fashion.
4065
4066                 // so, first iterate over the regions to be removed from rs and
4067                 // add them to the regions_to_remove list, and then
4068                 // iterate over the list to actually remove them.
4069
4070                 regions_to_remove.push_back ((*i)->region());
4071         }
4072
4073         vector<boost::shared_ptr<Playlist> > playlists;
4074
4075         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4076
4077                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4078
4079                 if (!playlist) {
4080                         // is this check necessary?
4081                         continue;
4082                 }
4083
4084                 /* get_regions_from_selection_and_entered() guarantees that
4085                    the playlists involved are unique, so there is no need
4086                    to check here.
4087                 */
4088
4089                 playlists.push_back (playlist);
4090
4091                 playlist->clear_changes ();
4092                 playlist->clear_owned_changes ();
4093                 playlist->freeze ();
4094                 playlist->remove_region (*rl);
4095                 if (Config->get_edit_mode() == Ripple)
4096                         playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4097
4098         }
4099
4100         vector<boost::shared_ptr<Playlist> >::iterator pl;
4101
4102         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4103                 (*pl)->thaw ();
4104
4105                 /* We might have removed regions, which alters other regions' layering_index,
4106                    so we need to do a recursive diff here.
4107                 */
4108                 vector<Command*> cmds;
4109                 (*pl)->rdiff (cmds);
4110                 _session->add_commands (cmds);
4111                 
4112                 _session->add_command(new StatefulDiffCommand (*pl));
4113         }
4114
4115         commit_reversible_command ();
4116 }
4117
4118 /** Cut, copy or clear selected regions.
4119  * @param op Operation (Cut, Copy or Clear)
4120  */
4121 void
4122 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4123 {
4124         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4125            a map when we want ordered access to both elements. i think.
4126         */
4127
4128         vector<PlaylistMapping> pmap;
4129
4130         framepos_t first_position = max_framepos;
4131
4132         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4133         FreezeList freezelist;
4134
4135         /* get ordering correct before we cut/copy */
4136
4137         rs.sort_by_position_and_track ();
4138
4139         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4140
4141                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4142
4143                 if (op == Cut || op == Clear || op == Delete) {
4144                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4145
4146                         if (pl) {
4147                                 FreezeList::iterator fl;
4148
4149                                 // only take state if this is a new playlist.
4150                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4151                                         if ((*fl) == pl) {
4152                                                 break;
4153                                         }
4154                                 }
4155
4156                                 if (fl == freezelist.end()) {
4157                                         pl->clear_changes();
4158                                         pl->clear_owned_changes ();
4159                                         pl->freeze ();
4160                                         freezelist.insert (pl);
4161                                 }
4162                         }
4163                 }
4164
4165                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4166                 vector<PlaylistMapping>::iterator z;
4167
4168                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4169                         if ((*z).tv == tv) {
4170                                 break;
4171                         }
4172                 }
4173
4174                 if (z == pmap.end()) {
4175                         pmap.push_back (PlaylistMapping (tv));
4176                 }
4177         }
4178
4179         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4180
4181                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4182
4183                 if (!pl) {
4184                         /* region not yet associated with a playlist (e.g. unfinished
4185                            capture pass.
4186                         */
4187                         ++x;
4188                         continue;
4189                 }
4190
4191                 TimeAxisView& tv = (*x)->get_time_axis_view();
4192                 boost::shared_ptr<Playlist> npl;
4193                 RegionSelection::iterator tmp;
4194
4195                 tmp = x;
4196                 ++tmp;
4197
4198                 if (op != Delete) {
4199
4200                         vector<PlaylistMapping>::iterator z;
4201                         
4202                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4203                                 if ((*z).tv == &tv) {
4204                                         break;
4205                                 }
4206                         }
4207                         
4208                         assert (z != pmap.end());
4209                         
4210                         if (!(*z).pl) {
4211                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4212                                 npl->freeze();
4213                                 (*z).pl = npl;
4214                         } else {
4215                                 npl = (*z).pl;
4216                         }
4217                 }
4218
4219                 boost::shared_ptr<Region> r = (*x)->region();
4220                 boost::shared_ptr<Region> _xx;
4221
4222                 assert (r != 0);
4223
4224                 switch (op) {
4225                 case Delete:
4226                         pl->remove_region (r);
4227                         if (Config->get_edit_mode() == Ripple)
4228                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4229                         break;
4230                         
4231                 case Cut:
4232                         _xx = RegionFactory::create (r);
4233                         npl->add_region (_xx, r->position() - first_position);
4234                         pl->remove_region (r);
4235                         if (Config->get_edit_mode() == Ripple)
4236                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4237                         break;
4238
4239                 case Copy:
4240                         /* copy region before adding, so we're not putting same object into two different playlists */
4241                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4242                         break;
4243
4244                 case Clear:
4245                         pl->remove_region (r);
4246                         if (Config->get_edit_mode() == Ripple)
4247                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4248                         break;
4249                 }
4250
4251                 x = tmp;
4252         }
4253
4254         if (op != Delete) {
4255
4256                 list<boost::shared_ptr<Playlist> > foo;
4257                 
4258                 /* the pmap is in the same order as the tracks in which selected regions occured */
4259                 
4260                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4261                         if ((*i).pl) {
4262                                 (*i).pl->thaw();
4263                                 foo.push_back ((*i).pl);
4264                         }
4265                 }
4266                 
4267                 if (!foo.empty()) {
4268                         cut_buffer->set (foo);
4269                 }
4270                 
4271                 if (pmap.empty()) {
4272                         _last_cut_copy_source_track = 0;
4273                 } else {
4274                         _last_cut_copy_source_track = pmap.front().tv;
4275                 }
4276         }
4277
4278         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4279                 (*pl)->thaw ();
4280
4281                 /* We might have removed regions, which alters other regions' layering_index,
4282                    so we need to do a recursive diff here.
4283                 */
4284                 vector<Command*> cmds;
4285                 (*pl)->rdiff (cmds);
4286                 _session->add_commands (cmds);
4287                 
4288                 _session->add_command (new StatefulDiffCommand (*pl));
4289         }
4290 }
4291
4292 void
4293 Editor::cut_copy_ranges (CutCopyOp op)
4294 {
4295         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4296
4297         /* Sort the track selection now, so that it if is used, the playlists
4298            selected by the calls below to cut_copy_clear are in the order that
4299            their tracks appear in the editor.  This makes things like paste
4300            of ranges work properly.
4301         */
4302
4303         sort_track_selection (ts);
4304
4305         if (ts.empty()) {
4306                 if (!entered_track) {
4307                         return;
4308                 }
4309                 ts.push_back (entered_track);
4310         } 
4311
4312         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4313                 (*i)->cut_copy_clear (*selection, op);
4314         }
4315 }
4316
4317 void
4318 Editor::paste (float times, bool from_context)
4319 {
4320         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4321
4322         paste_internal (get_preferred_edit_position (false, from_context), times);
4323 }
4324
4325 void
4326 Editor::mouse_paste ()
4327 {
4328         framepos_t where;
4329         bool ignored;
4330
4331         if (!mouse_frame (where, ignored)) {
4332                 return;
4333         }
4334
4335         snap_to (where);
4336         paste_internal (where, 1);
4337 }
4338
4339 void
4340 Editor::paste_internal (framepos_t position, float times)
4341 {
4342         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4343
4344         if (internal_editing()) {
4345                 if (cut_buffer->midi_notes.empty()) {
4346                         return;
4347                 }
4348         } else {
4349                 if (cut_buffer->empty()) {
4350                         return;
4351                 }
4352         }
4353
4354         if (position == max_framepos) {
4355                 position = get_preferred_edit_position();
4356                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4357         }
4358
4359         TrackViewList ts;
4360         TrackViewList::iterator i;
4361         size_t nth;
4362
4363         /* get everything in the correct order */
4364
4365         if (_edit_point == Editing::EditAtMouse && entered_track) {
4366                 /* With the mouse edit point, paste onto the track under the mouse */
4367                 ts.push_back (entered_track);
4368         } else if (!selection->tracks.empty()) {
4369                 /* Otherwise, if there are some selected tracks, paste to them */
4370                 ts = selection->tracks.filter_to_unique_playlists ();
4371                 sort_track_selection (ts);
4372         } else if (_last_cut_copy_source_track) {
4373                 /* Otherwise paste to the track that the cut/copy came from;
4374                    see discussion in mantis #3333.
4375                 */
4376                 ts.push_back (_last_cut_copy_source_track);
4377         }
4378
4379         if (internal_editing ()) {
4380
4381                 /* undo/redo is handled by individual tracks/regions */
4382
4383                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4384
4385                         RegionSelection rs;
4386                         RegionSelection::iterator r;
4387                         MidiNoteSelection::iterator cb;
4388
4389                         get_regions_at (rs, position, ts);
4390
4391                         for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4392                              cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4393                                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4394                                 if (mrv) {
4395                                         mrv->paste (position, times, **cb);
4396                                         ++cb;
4397                                 }
4398                         }
4399                 }
4400
4401         } else {
4402
4403                 /* we do redo (do you do voodoo?) */
4404
4405                 begin_reversible_command (Operations::paste);
4406
4407                 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4408                         (*i)->paste (position, times, *cut_buffer, nth);
4409                 }
4410
4411                 commit_reversible_command ();
4412         }
4413 }
4414
4415 void
4416 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4417 {
4418         boost::shared_ptr<Playlist> playlist;
4419         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4420         RegionSelection foo;
4421
4422         framepos_t const start_frame = regions.start ();
4423         framepos_t const end_frame = regions.end_frame ();
4424
4425         begin_reversible_command (Operations::duplicate_region);
4426
4427         selection->clear_regions ();
4428
4429         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4430
4431                 boost::shared_ptr<Region> r ((*i)->region());
4432
4433                 TimeAxisView& tv = (*i)->get_time_axis_view();
4434                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4435                 latest_regionviews.clear ();
4436                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4437
4438                 playlist = (*i)->region()->playlist();
4439                 playlist->clear_changes ();
4440                 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4441                 _session->add_command(new StatefulDiffCommand (playlist));
4442
4443                 c.disconnect ();
4444
4445                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4446         }
4447
4448         commit_reversible_command ();
4449
4450         if (!foo.empty()) {
4451                 selection->set (foo);
4452         }
4453 }
4454
4455 void
4456 Editor::duplicate_selection (float times)
4457 {
4458         if (selection->time.empty() || selection->tracks.empty()) {
4459                 return;
4460         }
4461
4462         boost::shared_ptr<Playlist> playlist;
4463         vector<boost::shared_ptr<Region> > new_regions;
4464         vector<boost::shared_ptr<Region> >::iterator ri;
4465
4466         create_region_from_selection (new_regions);
4467
4468         if (new_regions.empty()) {
4469                 return;
4470         }
4471
4472         begin_reversible_command (_("duplicate selection"));
4473
4474         ri = new_regions.begin();
4475
4476         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4477
4478         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4479                 if ((playlist = (*i)->playlist()) == 0) {
4480                         continue;
4481                 }
4482                 playlist->clear_changes ();
4483                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4484                 _session->add_command (new StatefulDiffCommand (playlist));
4485
4486                 ++ri;
4487                 if (ri == new_regions.end()) {
4488                         --ri;
4489                 }
4490         }
4491
4492         commit_reversible_command ();
4493 }
4494
4495 /** Reset all selected points to the relevant default value */
4496 void
4497 Editor::reset_point_selection ()
4498 {
4499         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4500                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4501                 (*j)->value = (*i)->line().the_list()->default_value ();
4502         }
4503 }
4504
4505 void
4506 Editor::center_playhead ()
4507 {
4508         float const page = _visible_canvas_width * samples_per_pixel;
4509         center_screen_internal (playhead_cursor->current_frame (), page);
4510 }
4511
4512 void
4513 Editor::center_edit_point ()
4514 {
4515         float const page = _visible_canvas_width * samples_per_pixel;
4516         center_screen_internal (get_preferred_edit_position(), page);
4517 }
4518
4519 /** Caller must begin and commit a reversible command */
4520 void
4521 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4522 {
4523         playlist->clear_changes ();
4524         playlist->clear ();
4525         _session->add_command (new StatefulDiffCommand (playlist));
4526 }
4527
4528 void
4529 Editor::nudge_track (bool use_edit, bool forwards)
4530 {
4531         boost::shared_ptr<Playlist> playlist;
4532         framepos_t distance;
4533         framepos_t next_distance;
4534         framepos_t start;
4535
4536         if (use_edit) {
4537                 start = get_preferred_edit_position();
4538         } else {
4539                 start = 0;
4540         }
4541
4542         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4543                 return;
4544         }
4545
4546         if (selection->tracks.empty()) {
4547                 return;
4548         }
4549
4550         begin_reversible_command (_("nudge track"));
4551
4552         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4553
4554         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4555
4556                 if ((playlist = (*i)->playlist()) == 0) {
4557                         continue;
4558                 }
4559
4560                 playlist->clear_changes ();
4561                 playlist->clear_owned_changes ();
4562
4563                 playlist->nudge_after (start, distance, forwards);
4564
4565                 vector<Command*> cmds;
4566
4567                 playlist->rdiff (cmds);
4568                 _session->add_commands (cmds);
4569
4570                 _session->add_command (new StatefulDiffCommand (playlist));
4571         }
4572
4573         commit_reversible_command ();
4574 }
4575
4576 void
4577 Editor::remove_last_capture ()
4578 {
4579         vector<string> choices;
4580         string prompt;
4581
4582         if (!_session) {
4583                 return;
4584         }
4585
4586         if (Config->get_verify_remove_last_capture()) {
4587                 prompt  = _("Do you really want to destroy the last capture?"
4588                             "\n(This is destructive and cannot be undone)");
4589
4590                 choices.push_back (_("No, do nothing."));
4591                 choices.push_back (_("Yes, destroy it."));
4592
4593                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4594
4595                 if (prompter.run () == 1) {
4596                         _session->remove_last_capture ();
4597                         _regions->redisplay ();
4598                 }
4599
4600         } else {
4601                 _session->remove_last_capture();
4602                 _regions->redisplay ();
4603         }
4604 }
4605
4606 void
4607 Editor::normalize_region ()
4608 {
4609         if (!_session) {
4610                 return;
4611         }
4612
4613         RegionSelection rs = get_regions_from_selection_and_entered ();
4614
4615         if (rs.empty()) {
4616                 return;
4617         }
4618
4619         NormalizeDialog dialog (rs.size() > 1);
4620
4621         if (dialog.run () == RESPONSE_CANCEL) {
4622                 return;
4623         }
4624
4625         set_canvas_cursor (_cursors->wait);
4626         gdk_flush ();
4627
4628         /* XXX: should really only count audio regions here */
4629         int const regions = rs.size ();
4630
4631         /* Make a list of the selected audio regions' maximum amplitudes, and also
4632            obtain the maximum amplitude of them all.
4633         */
4634         list<double> max_amps;
4635         double max_amp = 0;
4636         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4637                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4638                 if (arv) {
4639                         dialog.descend (1.0 / regions);
4640                         double const a = arv->audio_region()->maximum_amplitude (&dialog);
4641
4642                         if (a == -1) {
4643                                 /* the user cancelled the operation */
4644                                 set_canvas_cursor (current_canvas_cursor);
4645                                 return;
4646                         }
4647
4648                         max_amps.push_back (a);
4649                         max_amp = max (max_amp, a);
4650                         dialog.ascend ();
4651                 }
4652         }
4653
4654         begin_reversible_command (_("normalize"));
4655
4656         list<double>::const_iterator a = max_amps.begin ();
4657
4658         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4659                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4660                 if (!arv) {
4661                         continue;
4662                 }
4663
4664                 arv->region()->clear_changes ();
4665
4666                 double const amp = dialog.normalize_individually() ? *a : max_amp;
4667
4668                 arv->audio_region()->normalize (amp, dialog.target ());
4669                 _session->add_command (new StatefulDiffCommand (arv->region()));
4670
4671                 ++a;
4672         }
4673
4674         commit_reversible_command ();
4675         set_canvas_cursor (current_canvas_cursor);
4676 }
4677
4678
4679 void
4680 Editor::reset_region_scale_amplitude ()
4681 {
4682         if (!_session) {
4683                 return;
4684         }
4685
4686         RegionSelection rs = get_regions_from_selection_and_entered ();
4687
4688         if (rs.empty()) {
4689                 return;
4690         }
4691
4692         begin_reversible_command ("reset gain");
4693
4694         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4695                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4696                 if (!arv)
4697                         continue;
4698                 arv->region()->clear_changes ();
4699                 arv->audio_region()->set_scale_amplitude (1.0f);
4700                 _session->add_command (new StatefulDiffCommand (arv->region()));
4701         }
4702
4703         commit_reversible_command ();
4704 }
4705
4706 void
4707 Editor::adjust_region_gain (bool up)
4708 {
4709         RegionSelection rs = get_regions_from_selection_and_entered ();
4710
4711         if (!_session || rs.empty()) {
4712                 return;
4713         }
4714
4715         begin_reversible_command ("adjust region gain");
4716
4717         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4718                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4719                 if (!arv) {
4720                         continue;
4721                 }
4722
4723                 arv->region()->clear_changes ();
4724
4725                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4726
4727                 if (up) {
4728                         dB += 1;
4729                 } else {
4730                         dB -= 1;
4731                 }
4732
4733                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4734                 _session->add_command (new StatefulDiffCommand (arv->region()));
4735         }
4736
4737         commit_reversible_command ();
4738 }
4739
4740
4741 void
4742 Editor::reverse_region ()
4743 {
4744         if (!_session) {
4745                 return;
4746         }
4747
4748         Reverse rev (*_session);
4749         apply_filter (rev, _("reverse regions"));
4750 }
4751
4752 void
4753 Editor::strip_region_silence ()
4754 {
4755         if (!_session) {
4756                 return;
4757         }
4758
4759         RegionSelection rs = get_regions_from_selection_and_entered ();
4760
4761         if (rs.empty()) {
4762                 return;
4763         }
4764
4765         std::list<RegionView*> audio_only;
4766
4767         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4768                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4769                 if (arv) {
4770                         audio_only.push_back (arv);
4771                 }
4772         }
4773
4774         StripSilenceDialog d (_session, audio_only);
4775         int const r = d.run ();
4776
4777         d.drop_rects ();
4778
4779         if (r == Gtk::RESPONSE_OK) {
4780                 ARDOUR::AudioIntervalMap silences;
4781                 d.silences (silences);
4782                 StripSilence s (*_session, silences, d.fade_length());
4783                 apply_filter (s, _("strip silence"), &d);
4784         }
4785 }
4786
4787 Command*
4788 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4789 {
4790         Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4791         mrv.selection_as_notelist (selected, true);
4792
4793         vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4794         v.push_back (selected);
4795
4796         framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
4797         double     pos_beats  = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4798
4799         return op (mrv.midi_region()->model(), pos_beats, v);
4800 }
4801
4802 void
4803 Editor::apply_midi_note_edit_op (MidiOperator& op)
4804 {
4805         Command* cmd;
4806
4807         RegionSelection rs = get_regions_from_selection_and_entered ();
4808
4809         if (rs.empty()) {
4810                 return;
4811         }
4812
4813         begin_reversible_command (op.name ());
4814
4815         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4816                 RegionSelection::iterator tmp = r;
4817                 ++tmp;
4818
4819                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4820
4821                 if (mrv) {
4822                         cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4823                         if (cmd) {
4824                                 (*cmd)();
4825                                 _session->add_command (cmd);
4826                         }
4827                 }
4828
4829                 r = tmp;
4830         }
4831
4832         commit_reversible_command ();
4833 }
4834
4835 void
4836 Editor::fork_region ()
4837 {
4838         RegionSelection rs = get_regions_from_selection_and_entered ();
4839
4840         if (rs.empty()) {
4841                 return;
4842         }
4843
4844         begin_reversible_command (_("Fork Region(s)"));
4845
4846         set_canvas_cursor (_cursors->wait);
4847         gdk_flush ();
4848
4849         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4850                 RegionSelection::iterator tmp = r;
4851                 ++tmp;
4852
4853                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4854
4855                 if (mrv) {
4856                         try {
4857                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4858                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
4859                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
4860                                 
4861                                 playlist->clear_changes ();
4862                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4863                                 _session->add_command(new StatefulDiffCommand (playlist));
4864                         } catch (...) {
4865                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
4866                         }
4867                 }
4868
4869                 r = tmp;
4870         }
4871
4872         commit_reversible_command ();
4873
4874         set_canvas_cursor (current_canvas_cursor);
4875 }
4876
4877 void
4878 Editor::quantize_region ()
4879 {
4880         int selected_midi_region_cnt = 0;
4881
4882         if (!_session) {
4883                 return;
4884         }
4885
4886         RegionSelection rs = get_regions_from_selection_and_entered ();
4887
4888         if (rs.empty()) {
4889                 return;
4890         }
4891
4892         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4893                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4894                 if (mrv) {
4895                         selected_midi_region_cnt++;
4896                 }
4897         }
4898
4899         if (selected_midi_region_cnt == 0) {
4900                 return;
4901         }
4902
4903         QuantizeDialog* qd = new QuantizeDialog (*this);
4904
4905         qd->present ();
4906         const int r = qd->run ();
4907         qd->hide ();
4908
4909         if (r == Gtk::RESPONSE_OK) {
4910                 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4911                                 qd->start_grid_size(), qd->end_grid_size(),
4912                                 qd->strength(), qd->swing(), qd->threshold());
4913
4914                 apply_midi_note_edit_op (quant);
4915         }
4916 }
4917
4918 void
4919 Editor::insert_patch_change (bool from_context)
4920 {
4921         RegionSelection rs = get_regions_from_selection_and_entered ();
4922
4923         if (rs.empty ()) {
4924                 return;
4925         }
4926
4927         const framepos_t p = get_preferred_edit_position (false, from_context);
4928
4929         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4930            there may be more than one, but the PatchChangeDialog can only offer
4931            one set of patch menus.
4932         */
4933         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4934
4935         Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4936         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4937
4938         if (d.run() == RESPONSE_CANCEL) {
4939                 return;
4940         }
4941
4942         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4943                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4944                 if (mrv) {
4945                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4946                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4947                         }
4948                 }
4949         }
4950 }
4951
4952 void
4953 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4954 {
4955         RegionSelection rs = get_regions_from_selection_and_entered ();
4956
4957         if (rs.empty()) {
4958                 return;
4959         }
4960
4961         begin_reversible_command (command);
4962
4963         set_canvas_cursor (_cursors->wait);
4964         gdk_flush ();
4965
4966         int n = 0;
4967         int const N = rs.size ();
4968
4969         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4970                 RegionSelection::iterator tmp = r;
4971                 ++tmp;
4972
4973                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4974                 if (arv) {
4975                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4976
4977                         if (progress) {
4978                                 progress->descend (1.0 / N);
4979                         }
4980
4981                         if (arv->audio_region()->apply (filter, progress) == 0) {
4982
4983                                 playlist->clear_changes ();
4984                                 playlist->clear_owned_changes ();
4985
4986                                 if (filter.results.empty ()) {
4987
4988                                         /* no regions returned; remove the old one */
4989                                         playlist->remove_region (arv->region ());
4990
4991                                 } else {
4992
4993                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4994
4995                                         /* first region replaces the old one */
4996                                         playlist->replace_region (arv->region(), *res, (*res)->position());
4997                                         ++res;
4998
4999                                         /* add the rest */
5000                                         while (res != filter.results.end()) {
5001                                                 playlist->add_region (*res, (*res)->position());
5002                                                 ++res;
5003                                         }
5004
5005                                 }
5006
5007                                 /* We might have removed regions, which alters other regions' layering_index,
5008                                    so we need to do a recursive diff here.
5009                                 */
5010                                 vector<Command*> cmds;
5011                                 playlist->rdiff (cmds);
5012                                 _session->add_commands (cmds);
5013                                 
5014                                 _session->add_command(new StatefulDiffCommand (playlist));
5015                         } else {
5016                                 goto out;
5017                         }
5018
5019                         if (progress) {
5020                                 progress->ascend ();
5021                         }
5022                 }
5023
5024                 r = tmp;
5025                 ++n;
5026         }
5027
5028         commit_reversible_command ();
5029
5030   out:
5031         set_canvas_cursor (current_canvas_cursor);
5032 }
5033
5034 void
5035 Editor::external_edit_region ()
5036 {
5037         /* more to come */
5038 }
5039
5040 void
5041 Editor::reset_region_gain_envelopes ()
5042 {
5043         RegionSelection rs = get_regions_from_selection_and_entered ();
5044
5045         if (!_session || rs.empty()) {
5046                 return;
5047         }
5048
5049         _session->begin_reversible_command (_("reset region gain"));
5050
5051         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5052                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5053                 if (arv) {
5054                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5055                         XMLNode& before (alist->get_state());
5056
5057                         arv->audio_region()->set_default_envelope ();
5058                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5059                 }
5060         }
5061
5062         _session->commit_reversible_command ();
5063 }
5064
5065 void
5066 Editor::set_region_gain_visibility (RegionView* rv)
5067 {
5068         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5069         if (arv) {
5070                 arv->update_envelope_visibility();
5071         }
5072 }
5073
5074 void
5075 Editor::set_gain_envelope_visibility ()
5076 {
5077         if (!_session) {
5078                 return;
5079         }
5080
5081         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5082                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5083                 if (v) {
5084                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5085                 }
5086         }
5087 }
5088
5089 void
5090 Editor::toggle_gain_envelope_active ()
5091 {
5092         if (_ignore_region_action) {
5093                 return;
5094         }
5095
5096         RegionSelection rs = get_regions_from_selection_and_entered ();
5097
5098         if (!_session || rs.empty()) {
5099                 return;
5100         }
5101
5102         _session->begin_reversible_command (_("region gain envelope active"));
5103
5104         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5105                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5106                 if (arv) {
5107                         arv->region()->clear_changes ();
5108                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5109                         _session->add_command (new StatefulDiffCommand (arv->region()));
5110                 }
5111         }
5112
5113         _session->commit_reversible_command ();
5114 }
5115
5116 void
5117 Editor::toggle_region_lock ()
5118 {
5119         if (_ignore_region_action) {
5120                 return;
5121         }
5122
5123         RegionSelection rs = get_regions_from_selection_and_entered ();
5124
5125         if (!_session || rs.empty()) {
5126                 return;
5127         }
5128
5129         _session->begin_reversible_command (_("toggle region lock"));
5130
5131         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5132                 (*i)->region()->clear_changes ();
5133                 (*i)->region()->set_locked (!(*i)->region()->locked());
5134                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5135         }
5136
5137         _session->commit_reversible_command ();
5138 }
5139
5140 void
5141 Editor::toggle_region_video_lock ()
5142 {
5143         if (_ignore_region_action) {
5144                 return;
5145         }
5146
5147         RegionSelection rs = get_regions_from_selection_and_entered ();
5148
5149         if (!_session || rs.empty()) {
5150                 return;
5151         }
5152
5153         _session->begin_reversible_command (_("Toggle Video Lock"));
5154
5155         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5156                 (*i)->region()->clear_changes ();
5157                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5158                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5159         }
5160
5161         _session->commit_reversible_command ();
5162 }
5163
5164 void
5165 Editor::toggle_region_lock_style ()
5166 {
5167         if (_ignore_region_action) {
5168                 return;
5169         }
5170
5171         RegionSelection rs = get_regions_from_selection_and_entered ();
5172
5173         if (!_session || rs.empty()) {
5174                 return;
5175         }
5176
5177         _session->begin_reversible_command (_("region lock style"));
5178
5179         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5180                 (*i)->region()->clear_changes ();
5181                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5182                 (*i)->region()->set_position_lock_style (ns);
5183                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5184         }
5185
5186         _session->commit_reversible_command ();
5187 }
5188
5189 void
5190 Editor::toggle_opaque_region ()
5191 {
5192         if (_ignore_region_action) {
5193                 return;
5194         }
5195
5196         RegionSelection rs = get_regions_from_selection_and_entered ();
5197
5198         if (!_session || rs.empty()) {
5199                 return;
5200         }
5201
5202         _session->begin_reversible_command (_("change region opacity"));
5203
5204         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5205                 (*i)->region()->clear_changes ();
5206                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5207                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5208         }
5209
5210         _session->commit_reversible_command ();
5211 }
5212
5213 void
5214 Editor::toggle_record_enable ()
5215 {
5216         bool new_state = false;
5217         bool first = true;
5218         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5219                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5220                 if (!rtav)
5221                         continue;
5222                 if (!rtav->is_track())
5223                         continue;
5224
5225                 if (first) {
5226                         new_state = !rtav->track()->record_enabled();
5227                         first = false;
5228                 }
5229
5230                 rtav->track()->set_record_enabled (new_state, this);
5231         }
5232 }
5233
5234 void
5235 Editor::toggle_solo ()
5236 {
5237         bool new_state = false;
5238         bool first = true;
5239         boost::shared_ptr<RouteList> rl (new RouteList);
5240
5241         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5242                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5243
5244                 if (!rtav) {
5245                         continue;
5246                 }
5247
5248                 if (first) {
5249                         new_state = !rtav->route()->soloed ();
5250                         first = false;
5251                 }
5252
5253                 rl->push_back (rtav->route());
5254         }
5255
5256         _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5257 }
5258
5259 void
5260 Editor::toggle_mute ()
5261 {
5262         bool new_state = false;
5263         bool first = true;
5264         boost::shared_ptr<RouteList> rl (new RouteList);
5265
5266         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5267                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5268
5269                 if (!rtav) {
5270                         continue;
5271                 }
5272
5273                 if (first) {
5274                         new_state = !rtav->route()->muted();
5275                         first = false;
5276                 }
5277
5278                 rl->push_back (rtav->route());
5279         }
5280
5281         _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5282 }
5283
5284 void
5285 Editor::toggle_solo_isolate ()
5286 {
5287 }
5288
5289 void
5290 Editor::set_fade_length (bool in)
5291 {
5292         RegionSelection rs = get_regions_from_selection_and_entered ();
5293
5294         if (rs.empty()) {
5295                 return;
5296         }
5297
5298         /* we need a region to measure the offset from the start */
5299
5300         RegionView* rv = rs.front ();
5301
5302         framepos_t pos = get_preferred_edit_position();
5303         framepos_t len;
5304         char const * cmd;
5305
5306         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5307                 /* edit point is outside the relevant region */
5308                 return;
5309         }
5310
5311         if (in) {
5312                 if (pos <= rv->region()->position()) {
5313                         /* can't do it */
5314                         return;
5315                 }
5316                 len = pos - rv->region()->position();
5317                 cmd = _("set fade in length");
5318         } else {
5319                 if (pos >= rv->region()->last_frame()) {
5320                         /* can't do it */
5321                         return;
5322                 }
5323                 len = rv->region()->last_frame() - pos;
5324                 cmd = _("set fade out length");
5325         }
5326
5327         begin_reversible_command (cmd);
5328
5329         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5330                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5331
5332                 if (!tmp) {
5333                         return;
5334                 }
5335
5336                 boost::shared_ptr<AutomationList> alist;
5337                 if (in) {
5338                         alist = tmp->audio_region()->fade_in();
5339                 } else {
5340                         alist = tmp->audio_region()->fade_out();
5341                 }
5342
5343                 XMLNode &before = alist->get_state();
5344
5345                 if (in) {
5346                         tmp->audio_region()->set_fade_in_length (len);
5347                         tmp->audio_region()->set_fade_in_active (true);
5348                 } else {
5349                         tmp->audio_region()->set_fade_out_length (len);
5350                         tmp->audio_region()->set_fade_out_active (true);
5351                 }
5352
5353                 XMLNode &after = alist->get_state();
5354                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5355         }
5356
5357         commit_reversible_command ();
5358 }
5359
5360 void
5361 Editor::set_fade_in_shape (FadeShape shape)
5362 {
5363         RegionSelection rs = get_regions_from_selection_and_entered ();
5364
5365         if (rs.empty()) {
5366                 return;
5367         }
5368
5369         begin_reversible_command (_("set fade in shape"));
5370
5371         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5372                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5373
5374                 if (!tmp) {
5375                         return;
5376                 }
5377
5378                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5379                 XMLNode &before = alist->get_state();
5380
5381                 tmp->audio_region()->set_fade_in_shape (shape);
5382
5383                 XMLNode &after = alist->get_state();
5384                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5385         }
5386
5387         commit_reversible_command ();
5388
5389 }
5390
5391 void
5392 Editor::set_fade_out_shape (FadeShape shape)
5393 {
5394         RegionSelection rs = get_regions_from_selection_and_entered ();
5395
5396         if (rs.empty()) {
5397                 return;
5398         }
5399
5400         begin_reversible_command (_("set fade out shape"));
5401
5402         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5403                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5404
5405                 if (!tmp) {
5406                         return;
5407                 }
5408
5409                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5410                 XMLNode &before = alist->get_state();
5411
5412                 tmp->audio_region()->set_fade_out_shape (shape);
5413
5414                 XMLNode &after = alist->get_state();
5415                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5416         }
5417
5418         commit_reversible_command ();
5419 }
5420
5421 void
5422 Editor::set_fade_in_active (bool yn)
5423 {
5424         RegionSelection rs = get_regions_from_selection_and_entered ();
5425
5426         if (rs.empty()) {
5427                 return;
5428         }
5429
5430         begin_reversible_command (_("set fade in active"));
5431
5432         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5433                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5434
5435                 if (!tmp) {
5436                         return;
5437                 }
5438
5439
5440                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5441
5442                 ar->clear_changes ();
5443                 ar->set_fade_in_active (yn);
5444                 _session->add_command (new StatefulDiffCommand (ar));
5445         }
5446
5447         commit_reversible_command ();
5448 }
5449
5450 void
5451 Editor::set_fade_out_active (bool yn)
5452 {
5453         RegionSelection rs = get_regions_from_selection_and_entered ();
5454
5455         if (rs.empty()) {
5456                 return;
5457         }
5458
5459         begin_reversible_command (_("set fade out active"));
5460
5461         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5462                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5463
5464                 if (!tmp) {
5465                         return;
5466                 }
5467
5468                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5469
5470                 ar->clear_changes ();
5471                 ar->set_fade_out_active (yn);
5472                 _session->add_command(new StatefulDiffCommand (ar));
5473         }
5474
5475         commit_reversible_command ();
5476 }
5477
5478 void
5479 Editor::toggle_region_fades (int dir)
5480 {
5481         if (_ignore_region_action) {
5482                 return;
5483         }
5484         
5485         boost::shared_ptr<AudioRegion> ar;
5486         bool yn = false;
5487
5488         RegionSelection rs = get_regions_from_selection_and_entered ();
5489
5490         if (rs.empty()) {
5491                 return;
5492         }
5493
5494         RegionSelection::iterator i;
5495         for (i = rs.begin(); i != rs.end(); ++i) {
5496                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5497                         if (dir == -1) {
5498                                 yn = ar->fade_out_active ();
5499                         } else {
5500                                 yn = ar->fade_in_active ();
5501                         }
5502                         break;
5503                 }
5504         }
5505
5506         if (i == rs.end()) {
5507                 return;
5508         }
5509
5510         /* XXX should this undo-able? */
5511
5512         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5513                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5514                         continue;
5515                 }
5516                 if (dir == 1 || dir == 0) {
5517                         ar->set_fade_in_active (!yn);
5518                 }
5519
5520                 if (dir == -1 || dir == 0) {
5521                         ar->set_fade_out_active (!yn);
5522                 }
5523         }
5524 }
5525
5526
5527 /** Update region fade visibility after its configuration has been changed */
5528 void
5529 Editor::update_region_fade_visibility ()
5530 {
5531         bool _fade_visibility = _session->config.get_show_region_fades ();
5532
5533         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5534                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5535                 if (v) {
5536                         if (_fade_visibility) {
5537                                 v->audio_view()->show_all_fades ();
5538                         } else {
5539                                 v->audio_view()->hide_all_fades ();
5540                         }
5541                 }
5542         }
5543 }
5544
5545 void
5546 Editor::set_edit_point ()
5547 {
5548         framepos_t where;
5549         bool ignored;
5550
5551         if (!mouse_frame (where, ignored)) {
5552                 return;
5553         }
5554
5555         snap_to (where);
5556
5557         if (selection->markers.empty()) {
5558
5559                 mouse_add_new_marker (where);
5560
5561         } else {
5562                 bool ignored;
5563
5564                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5565
5566                 if (loc) {
5567                         loc->move_to (where);
5568                 }
5569         }
5570 }
5571
5572 void
5573 Editor::set_playhead_cursor ()
5574 {
5575         if (entered_marker) {
5576                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5577         } else {
5578                 framepos_t where;
5579                 bool ignored;
5580
5581                 if (!mouse_frame (where, ignored)) {
5582                         return;
5583                 }
5584
5585                 snap_to (where);
5586
5587                 if (_session) {
5588                         _session->request_locate (where, _session->transport_rolling());
5589                 }
5590         }
5591
5592         if ( Config->get_follow_edits() )
5593                 cancel_time_selection();
5594 }
5595
5596 void
5597 Editor::split_region ()
5598 {
5599         if ( !selection->time.empty()) {
5600                 separate_regions_between (selection->time);
5601                 return;
5602         }
5603
5604         RegionSelection rs = get_regions_from_selection_and_edit_point ();
5605
5606         framepos_t where = get_preferred_edit_position ();
5607
5608         if (rs.empty()) {
5609                 return;
5610         }
5611
5612         split_regions_at (where, rs);
5613 }
5614
5615 struct EditorOrderRouteSorter {
5616     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5617             return a->order_key () < b->order_key ();
5618     }
5619 };
5620
5621 void
5622 Editor::select_next_route()
5623 {
5624         if (selection->tracks.empty()) {
5625                 selection->set (track_views.front());
5626                 return;
5627         }
5628
5629         TimeAxisView* current = selection->tracks.front();
5630
5631         RouteUI *rui;
5632         do {
5633                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5634                         if (*i == current) {
5635                                 ++i;
5636                                 if (i != track_views.end()) {
5637                                         current = (*i);
5638                                 } else {
5639                                         current = (*(track_views.begin()));
5640                                         //selection->set (*(track_views.begin()));
5641                                 }
5642                                 break;
5643                         }
5644                 }
5645                 rui = dynamic_cast<RouteUI *>(current);
5646         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5647
5648         selection->set(current);
5649
5650         ensure_time_axis_view_is_visible (*current);
5651 }
5652
5653 void
5654 Editor::select_prev_route()
5655 {
5656         if (selection->tracks.empty()) {
5657                 selection->set (track_views.front());
5658                 return;
5659         }
5660
5661         TimeAxisView* current = selection->tracks.front();
5662
5663         RouteUI *rui;
5664         do {
5665                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5666                         if (*i == current) {
5667                                 ++i;
5668                                 if (i != track_views.rend()) {
5669                                         current = (*i);
5670                                 } else {
5671                                         current = *(track_views.rbegin());
5672                                 }
5673                                 break;
5674                         }
5675                 }
5676                 rui = dynamic_cast<RouteUI *>(current);
5677         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5678
5679         selection->set (current);
5680
5681         ensure_time_axis_view_is_visible (*current);
5682 }
5683
5684 void
5685 Editor::set_loop_from_selection (bool play)
5686 {
5687         if (_session == 0 || selection->time.empty()) {
5688                 return;
5689         }
5690
5691         framepos_t start = selection->time[clicked_selection].start;
5692         framepos_t end = selection->time[clicked_selection].end;
5693
5694         set_loop_range (start, end,  _("set loop range from selection"));
5695
5696         if (play) {
5697                 _session->request_play_loop (true);
5698                 _session->request_locate (start, true);
5699         }
5700 }
5701
5702 void
5703 Editor::set_loop_from_edit_range (bool play)
5704 {
5705         if (_session == 0) {
5706                 return;
5707         }
5708
5709         framepos_t start;
5710         framepos_t end;
5711
5712         if (!get_edit_op_range (start, end)) {
5713                 return;
5714         }
5715
5716         set_loop_range (start, end,  _("set loop range from edit range"));
5717
5718         if (play) {
5719                 _session->request_play_loop (true);
5720                 _session->request_locate (start, true);
5721         }
5722 }
5723
5724 void
5725 Editor::set_loop_from_region (bool play)
5726 {
5727         framepos_t start = max_framepos;
5728         framepos_t end = 0;
5729
5730         RegionSelection rs = get_regions_from_selection_and_entered ();
5731
5732         if (rs.empty()) {
5733                 return;
5734         }
5735
5736         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5737                 if ((*i)->region()->position() < start) {
5738                         start = (*i)->region()->position();
5739                 }
5740                 if ((*i)->region()->last_frame() + 1 > end) {
5741                         end = (*i)->region()->last_frame() + 1;
5742                 }
5743         }
5744
5745         set_loop_range (start, end, _("set loop range from region"));
5746
5747         if (play) {
5748                 _session->request_play_loop (true);
5749                 _session->request_locate (start, true);
5750         }
5751 }
5752
5753 void
5754 Editor::set_punch_from_selection ()
5755 {
5756         if (_session == 0 || selection->time.empty()) {
5757                 return;
5758         }
5759
5760         framepos_t start = selection->time[clicked_selection].start;
5761         framepos_t end = selection->time[clicked_selection].end;
5762
5763         set_punch_range (start, end,  _("set punch range from selection"));
5764 }
5765
5766 void
5767 Editor::set_punch_from_edit_range ()
5768 {
5769         if (_session == 0) {
5770                 return;
5771         }
5772
5773         framepos_t start;
5774         framepos_t end;
5775
5776         if (!get_edit_op_range (start, end)) {
5777                 return;
5778         }
5779
5780         set_punch_range (start, end,  _("set punch range from edit range"));
5781 }
5782
5783 void
5784 Editor::set_punch_from_region ()
5785 {
5786         framepos_t start = max_framepos;
5787         framepos_t end = 0;
5788
5789         RegionSelection rs = get_regions_from_selection_and_entered ();
5790
5791         if (rs.empty()) {
5792                 return;
5793         }
5794
5795         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5796                 if ((*i)->region()->position() < start) {
5797                         start = (*i)->region()->position();
5798                 }
5799                 if ((*i)->region()->last_frame() + 1 > end) {
5800                         end = (*i)->region()->last_frame() + 1;
5801                 }
5802         }
5803
5804         set_punch_range (start, end, _("set punch range from region"));
5805 }
5806
5807 void
5808 Editor::pitch_shift_region ()
5809 {
5810         RegionSelection rs = get_regions_from_selection_and_entered ();
5811
5812         RegionSelection audio_rs;
5813         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5814                 if (dynamic_cast<AudioRegionView*> (*i)) {
5815                         audio_rs.push_back (*i);
5816                 }
5817         }
5818
5819         if (audio_rs.empty()) {
5820                 return;
5821         }
5822
5823         pitch_shift (audio_rs, 1.2);
5824 }
5825
5826 void
5827 Editor::transpose_region ()
5828 {
5829         RegionSelection rs = get_regions_from_selection_and_entered ();
5830
5831         list<MidiRegionView*> midi_region_views;
5832         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5833                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5834                 if (mrv) {
5835                         midi_region_views.push_back (mrv);
5836                 }
5837         }
5838
5839         TransposeDialog d;
5840         int const r = d.run ();
5841         if (r != RESPONSE_ACCEPT) {
5842                 return;
5843         }
5844
5845         for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5846                 (*i)->midi_region()->transpose (d.semitones ());
5847         }
5848 }
5849
5850 void
5851 Editor::set_tempo_from_region ()
5852 {
5853         RegionSelection rs = get_regions_from_selection_and_entered ();
5854
5855         if (!_session || rs.empty()) {
5856                 return;
5857         }
5858
5859         RegionView* rv = rs.front();
5860
5861         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5862 }
5863
5864 void
5865 Editor::use_range_as_bar ()
5866 {
5867         framepos_t start, end;
5868         if (get_edit_op_range (start, end)) {
5869                 define_one_bar (start, end);
5870         }
5871 }
5872
5873 void
5874 Editor::define_one_bar (framepos_t start, framepos_t end)
5875 {
5876         framepos_t length = end - start;
5877
5878         const Meter& m (_session->tempo_map().meter_at (start));
5879
5880         /* length = 1 bar */
5881
5882         /* now we want frames per beat.
5883            we have frames per bar, and beats per bar, so ...
5884         */
5885
5886         /* XXXX METER MATH */
5887
5888         double frames_per_beat = length / m.divisions_per_bar();
5889
5890         /* beats per minute = */
5891
5892         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5893
5894         /* now decide whether to:
5895
5896             (a) set global tempo
5897             (b) add a new tempo marker
5898
5899         */
5900
5901         const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5902
5903         bool do_global = false;
5904
5905         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5906
5907                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5908                    at the start, or create a new marker
5909                 */
5910
5911                 vector<string> options;
5912                 options.push_back (_("Cancel"));
5913                 options.push_back (_("Add new marker"));
5914                 options.push_back (_("Set global tempo"));
5915
5916                 Choice c (
5917                         _("Define one bar"),
5918                         _("Do you want to set the global tempo or add a new tempo marker?"),
5919                         options
5920                         );
5921
5922                 c.set_default_response (2);
5923
5924                 switch (c.run()) {
5925                 case 0:
5926                         return;
5927
5928                 case 2:
5929                         do_global = true;
5930                         break;
5931
5932                 default:
5933                         do_global = false;
5934                 }
5935
5936         } else {
5937
5938                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5939                    if the marker is at the region starter, change it, otherwise add
5940                    a new tempo marker
5941                 */
5942         }
5943
5944         begin_reversible_command (_("set tempo from region"));
5945         XMLNode& before (_session->tempo_map().get_state());
5946
5947         if (do_global) {
5948                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5949         } else if (t.frame() == start) {
5950                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5951         } else {
5952                 Timecode::BBT_Time bbt;
5953                 _session->tempo_map().bbt_time (start, bbt);
5954                 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
5955         }
5956
5957         XMLNode& after (_session->tempo_map().get_state());
5958
5959         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5960         commit_reversible_command ();
5961 }
5962
5963 void
5964 Editor::split_region_at_transients ()
5965 {
5966         AnalysisFeatureList positions;
5967
5968         RegionSelection rs = get_regions_from_selection_and_entered ();
5969
5970         if (!_session || rs.empty()) {
5971                 return;
5972         }
5973
5974         _session->begin_reversible_command (_("split regions"));
5975
5976         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5977
5978                 RegionSelection::iterator tmp;
5979
5980                 tmp = i;
5981                 ++tmp;
5982
5983                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5984
5985                 if (ar && (ar->get_transients (positions) == 0)) {
5986                         split_region_at_points ((*i)->region(), positions, true);
5987                         positions.clear ();
5988                 }
5989
5990                 i = tmp;
5991         }
5992
5993         _session->commit_reversible_command ();
5994
5995 }
5996
5997 void
5998 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5999 {
6000         bool use_rhythmic_rodent = false;
6001
6002         boost::shared_ptr<Playlist> pl = r->playlist();
6003
6004         list<boost::shared_ptr<Region> > new_regions;
6005
6006         if (!pl) {
6007                 return;
6008         }
6009
6010         if (positions.empty()) {
6011                 return;
6012         }
6013
6014
6015         if (positions.size() > 20 && can_ferret) {
6016                 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);
6017                 MessageDialog msg (msgstr,
6018                                    false,
6019                                    Gtk::MESSAGE_INFO,
6020                                    Gtk::BUTTONS_OK_CANCEL);
6021
6022                 if (can_ferret) {
6023                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6024                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6025                 } else {
6026                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6027                 }
6028
6029                 msg.set_title (_("Excessive split?"));
6030                 msg.present ();
6031
6032                 int response = msg.run();
6033                 msg.hide ();
6034
6035                 switch (response) {
6036                 case RESPONSE_OK:
6037                         break;
6038                 case RESPONSE_APPLY:
6039                         use_rhythmic_rodent = true;
6040                         break;
6041                 default:
6042                         return;
6043                 }
6044         }
6045
6046         if (use_rhythmic_rodent) {
6047                 show_rhythm_ferret ();
6048                 return;
6049         }
6050
6051         AnalysisFeatureList::const_iterator x;
6052
6053         pl->clear_changes ();
6054         pl->clear_owned_changes ();
6055
6056         x = positions.begin();
6057
6058         if (x == positions.end()) {
6059                 return;
6060         }
6061
6062         pl->freeze ();
6063         pl->remove_region (r);
6064
6065         framepos_t pos = 0;
6066
6067         while (x != positions.end()) {
6068
6069                 /* deal with positons that are out of scope of present region bounds */
6070                 if (*x <= 0 || *x > r->length()) {
6071                         ++x;
6072                         continue;
6073                 }
6074
6075                 /* file start = original start + how far we from the initial position ?
6076                  */
6077
6078                 framepos_t file_start = r->start() + pos;
6079
6080                 /* length = next position - current position
6081                  */
6082
6083                 framepos_t len = (*x) - pos;
6084
6085                 /* XXX we do we really want to allow even single-sample regions?
6086                    shouldn't we have some kind of lower limit on region size?
6087                 */
6088
6089                 if (len <= 0) {
6090                         break;
6091                 }
6092
6093                 string new_name;
6094
6095                 if (RegionFactory::region_name (new_name, r->name())) {
6096                         break;
6097                 }
6098
6099                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6100
6101                 PropertyList plist;
6102
6103                 plist.add (ARDOUR::Properties::start, file_start);
6104                 plist.add (ARDOUR::Properties::length, len);
6105                 plist.add (ARDOUR::Properties::name, new_name);
6106                 plist.add (ARDOUR::Properties::layer, 0);
6107
6108                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6109                 /* because we set annouce to false, manually add the new region to the
6110                    RegionFactory map
6111                 */
6112                 RegionFactory::map_add (nr);
6113
6114                 pl->add_region (nr, r->position() + pos);
6115
6116                 if (select_new) {
6117                         new_regions.push_front(nr);
6118                 }
6119
6120                 pos += len;
6121                 ++x;
6122         }
6123
6124         string new_name;
6125
6126         RegionFactory::region_name (new_name, r->name());
6127
6128         /* Add the final region */
6129         PropertyList plist;
6130
6131         plist.add (ARDOUR::Properties::start, r->start() + pos);
6132         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6133         plist.add (ARDOUR::Properties::name, new_name);
6134         plist.add (ARDOUR::Properties::layer, 0);
6135
6136         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6137         /* because we set annouce to false, manually add the new region to the
6138            RegionFactory map
6139         */
6140         RegionFactory::map_add (nr);
6141         pl->add_region (nr, r->position() + pos);
6142
6143         if (select_new) {
6144                 new_regions.push_front(nr);
6145         }
6146
6147         pl->thaw ();
6148
6149         /* We might have removed regions, which alters other regions' layering_index,
6150            so we need to do a recursive diff here.
6151         */
6152         vector<Command*> cmds;
6153         pl->rdiff (cmds);
6154         _session->add_commands (cmds);
6155         
6156         _session->add_command (new StatefulDiffCommand (pl));
6157
6158         if (select_new) {
6159
6160                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6161                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6162                 }
6163         }
6164 }
6165
6166 void
6167 Editor::place_transient()
6168 {
6169         if (!_session) {
6170                 return;
6171         }
6172
6173         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6174
6175         if (rs.empty()) {
6176                 return;
6177         }
6178
6179         framepos_t where = get_preferred_edit_position();
6180
6181         _session->begin_reversible_command (_("place transient"));
6182
6183         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6184                 framepos_t position = (*r)->region()->position();
6185                 (*r)->region()->add_transient(where - position);
6186         }
6187
6188         _session->commit_reversible_command ();
6189 }
6190
6191 void
6192 Editor::remove_transient(ArdourCanvas::Item* item)
6193 {
6194         if (!_session) {
6195                 return;
6196         }
6197
6198         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6199         assert (_line);
6200
6201         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6202         _arv->remove_transient (*(float*) _line->get_data ("position"));
6203 }
6204
6205 void
6206 Editor::snap_regions_to_grid ()
6207 {
6208         list <boost::shared_ptr<Playlist > > used_playlists;
6209
6210         RegionSelection rs = get_regions_from_selection_and_entered ();
6211
6212         if (!_session || rs.empty()) {
6213                 return;
6214         }
6215
6216         _session->begin_reversible_command (_("snap regions to grid"));
6217
6218         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6219
6220                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6221
6222                 if (!pl->frozen()) {
6223                         /* we haven't seen this playlist before */
6224
6225                         /* remember used playlists so we can thaw them later */
6226                         used_playlists.push_back(pl);
6227                         pl->freeze();
6228                 }
6229
6230                 framepos_t start_frame = (*r)->region()->first_frame ();
6231                 snap_to (start_frame);
6232                 (*r)->region()->set_position (start_frame);
6233         }
6234
6235         while (used_playlists.size() > 0) {
6236                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6237                 (*i)->thaw();
6238                 used_playlists.pop_front();
6239         }
6240
6241         _session->commit_reversible_command ();
6242 }
6243
6244 void
6245 Editor::close_region_gaps ()
6246 {
6247         list <boost::shared_ptr<Playlist > > used_playlists;
6248
6249         RegionSelection rs = get_regions_from_selection_and_entered ();
6250
6251         if (!_session || rs.empty()) {
6252                 return;
6253         }
6254
6255         Dialog dialog (_("Close Region Gaps"));
6256
6257         Table table (2, 3);
6258         table.set_spacings (12);
6259         table.set_border_width (12);
6260         Label* l = manage (left_aligned_label (_("Crossfade length")));
6261         table.attach (*l, 0, 1, 0, 1);
6262
6263         SpinButton spin_crossfade (1, 0);
6264         spin_crossfade.set_range (0, 15);
6265         spin_crossfade.set_increments (1, 1);
6266         spin_crossfade.set_value (5);
6267         table.attach (spin_crossfade, 1, 2, 0, 1);
6268
6269         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6270
6271         l = manage (left_aligned_label (_("Pull-back length")));
6272         table.attach (*l, 0, 1, 1, 2);
6273
6274         SpinButton spin_pullback (1, 0);
6275         spin_pullback.set_range (0, 100);
6276         spin_pullback.set_increments (1, 1);
6277         spin_pullback.set_value(30);
6278         table.attach (spin_pullback, 1, 2, 1, 2);
6279
6280         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6281
6282         dialog.get_vbox()->pack_start (table);
6283         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6284         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6285         dialog.show_all ();
6286
6287         if (dialog.run () == RESPONSE_CANCEL) {
6288                 return;
6289         }
6290
6291         framepos_t crossfade_len = spin_crossfade.get_value();
6292         framepos_t pull_back_frames = spin_pullback.get_value();
6293
6294         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6295         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6296
6297         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6298
6299         _session->begin_reversible_command (_("close region gaps"));
6300
6301         int idx = 0;
6302         boost::shared_ptr<Region> last_region;
6303
6304         rs.sort_by_position_and_track();
6305
6306         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6307
6308                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6309
6310                 if (!pl->frozen()) {
6311                         /* we haven't seen this playlist before */
6312
6313                         /* remember used playlists so we can thaw them later */
6314                         used_playlists.push_back(pl);
6315                         pl->freeze();
6316                 }
6317
6318                 framepos_t position = (*r)->region()->position();
6319
6320                 if (idx == 0 || position < last_region->position()){
6321                         last_region = (*r)->region();
6322                         idx++;
6323                         continue;
6324                 }
6325
6326                 (*r)->region()->trim_front( (position - pull_back_frames));
6327                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6328
6329                 last_region = (*r)->region();
6330
6331                 idx++;
6332         }
6333
6334         while (used_playlists.size() > 0) {
6335                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6336                 (*i)->thaw();
6337                 used_playlists.pop_front();
6338         }
6339
6340         _session->commit_reversible_command ();
6341 }
6342
6343 void
6344 Editor::tab_to_transient (bool forward)
6345 {
6346         AnalysisFeatureList positions;
6347
6348         RegionSelection rs = get_regions_from_selection_and_entered ();
6349
6350         if (!_session) {
6351                 return;
6352         }
6353
6354         framepos_t pos = _session->audible_frame ();
6355
6356         if (!selection->tracks.empty()) {
6357
6358                 /* don't waste time searching for transients in duplicate playlists.
6359                  */
6360
6361                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6362
6363                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6364
6365                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6366
6367                         if (rtv) {
6368                                 boost::shared_ptr<Track> tr = rtv->track();
6369                                 if (tr) {
6370                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
6371                                         if (pl) {
6372                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6373
6374                                                 if (result >= 0) {
6375                                                         positions.push_back (result);
6376                                                 }
6377                                         }
6378                                 }
6379                         }
6380                 }
6381
6382         } else {
6383
6384                 if (rs.empty()) {
6385                         return;
6386                 }
6387
6388                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6389                         (*r)->region()->get_transients (positions);
6390                 }
6391         }
6392
6393         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6394
6395         if (forward) {
6396                 AnalysisFeatureList::iterator x;
6397
6398                 for (x = positions.begin(); x != positions.end(); ++x) {
6399                         if ((*x) > pos) {
6400                                 break;
6401                         }
6402                 }
6403
6404                 if (x != positions.end ()) {
6405                         _session->request_locate (*x);
6406                 }
6407
6408         } else {
6409                 AnalysisFeatureList::reverse_iterator x;
6410
6411                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6412                         if ((*x) < pos) {
6413                                 break;
6414                         }
6415                 }
6416
6417                 if (x != positions.rend ()) {
6418                         _session->request_locate (*x);
6419                 }
6420         }
6421 }
6422
6423 void
6424 Editor::playhead_forward_to_grid ()
6425 {
6426         if (!_session) {
6427                 return;
6428         }
6429         
6430         framepos_t pos = playhead_cursor->current_frame ();
6431         if (pos < max_framepos - 1) {
6432                 pos += 2;
6433                 snap_to_internal (pos, 1, false);
6434                 _session->request_locate (pos);
6435         }
6436 }
6437
6438
6439 void
6440 Editor::playhead_backward_to_grid ()
6441 {
6442         if (!_session) {
6443                 return;
6444         }
6445         
6446         framepos_t pos = playhead_cursor->current_frame ();
6447         if (pos > 2) {
6448                 pos -= 2;
6449                 snap_to_internal (pos, -1, false);
6450                 _session->request_locate (pos);
6451         }
6452 }
6453
6454 void
6455 Editor::set_track_height (Height h)
6456 {
6457         TrackSelection& ts (selection->tracks);
6458
6459         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6460                 (*x)->set_height_enum (h);
6461         }
6462 }
6463
6464 void
6465 Editor::toggle_tracks_active ()
6466 {
6467         TrackSelection& ts (selection->tracks);
6468         bool first = true;
6469         bool target = false;
6470
6471         if (ts.empty()) {
6472                 return;
6473         }
6474
6475         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6476                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6477
6478                 if (rtv) {
6479                         if (first) {
6480                                 target = !rtv->_route->active();
6481                                 first = false;
6482                         }
6483                         rtv->_route->set_active (target, this);
6484                 }
6485         }
6486 }
6487
6488 void
6489 Editor::remove_tracks ()
6490 {
6491         TrackSelection& ts (selection->tracks);
6492
6493         if (ts.empty()) {
6494                 return;
6495         }
6496
6497         vector<string> choices;
6498         string prompt;
6499         int ntracks = 0;
6500         int nbusses = 0;
6501         const char* trackstr;
6502         const char* busstr;
6503         vector<boost::shared_ptr<Route> > routes;
6504         bool special_bus = false;
6505
6506         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6507                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6508                 if (!rtv) {
6509                         continue;
6510                 }
6511                 if (rtv->is_track()) {
6512                         ntracks++;
6513                 } else {
6514                         nbusses++;
6515                 }
6516                 routes.push_back (rtv->_route);
6517
6518                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6519                         special_bus = true;
6520                 }
6521         }
6522
6523         if (special_bus && !Config->get_allow_special_bus_removal()) {
6524                 MessageDialog msg (_("That would be bad news ...."),
6525                                    false,
6526                                    Gtk::MESSAGE_INFO,
6527                                    Gtk::BUTTONS_OK);
6528                 msg.set_secondary_text (string_compose (_(
6529                                                                 "Removing the master or monitor bus is such a bad idea\n\
6530 that %1 is not going to allow it.\n\
6531 \n\
6532 If you really want to do this sort of thing\n\
6533 edit your ardour.rc file to set the\n\
6534 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6535
6536                 msg.present ();
6537                 msg.run ();
6538                 return;
6539         }
6540
6541         if (ntracks + nbusses == 0) {
6542                 return;
6543         }
6544
6545         if (ntracks > 1) {
6546                 trackstr = _("tracks");
6547         } else {
6548                 trackstr = _("track");
6549         }
6550
6551         if (nbusses > 1) {
6552                 busstr = _("busses");
6553         } else {
6554                 busstr = _("bus");
6555         }
6556
6557         if (ntracks) {
6558                 if (nbusses) {
6559                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6560                                                     "(You may also lose the playlists associated with the %2)\n\n"
6561                                                     "This action cannot be undone, and the session file will be overwritten!"),
6562                                                   ntracks, trackstr, nbusses, busstr);
6563                 } else {
6564                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
6565                                                     "(You may also lose the playlists associated with the %2)\n\n"
6566                                                     "This action cannot be undone, and the session file will be overwritten!"),
6567                                                   ntracks, trackstr);
6568                 }
6569         } else if (nbusses) {
6570                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
6571                                             "This action cannot be undon, and the session file will be overwritten"),
6572                                           nbusses, busstr);
6573         }
6574
6575         choices.push_back (_("No, do nothing."));
6576         if (ntracks + nbusses > 1) {
6577                 choices.push_back (_("Yes, remove them."));
6578         } else {
6579                 choices.push_back (_("Yes, remove it."));
6580         }
6581
6582         string title;
6583         if (ntracks) {
6584                 title = string_compose (_("Remove %1"), trackstr);
6585         } else {
6586                 title = string_compose (_("Remove %1"), busstr);
6587         }
6588
6589         Choice prompter (title, prompt, choices);
6590
6591         if (prompter.run () != 1) {
6592                 return;
6593         }
6594
6595         {
6596                 Session::StateProtector sp (_session);
6597                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6598                         _session->remove_route (*x);
6599                 }
6600         }
6601 }
6602
6603 void
6604 Editor::do_insert_time ()
6605 {
6606         if (selection->tracks.empty()) {
6607                 return;
6608         }
6609
6610         InsertTimeDialog d (*this);
6611         int response = d.run ();
6612
6613         if (response != RESPONSE_OK) {
6614                 return;
6615         }
6616
6617         if (d.distance() == 0) {
6618                 return;
6619         }
6620
6621         InsertTimeOption opt = d.intersected_region_action ();
6622
6623         insert_time (
6624                 get_preferred_edit_position(),
6625                 d.distance(),
6626                 opt,
6627                 d.all_playlists(),
6628                 d.move_glued(),
6629                 d.move_markers(),
6630                 d.move_glued_markers(),
6631                 d.move_locked_markers(),
6632                 d.move_tempos()
6633                 );
6634 }
6635
6636 void
6637 Editor::insert_time (
6638         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6639         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6640         )
6641 {
6642         bool commit = false;
6643
6644         if (Config->get_edit_mode() == Lock) {
6645                 return;
6646         }
6647
6648         begin_reversible_command (_("insert time"));
6649
6650         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6651
6652         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6653
6654                 /* regions */
6655
6656                 /* don't operate on any playlist more than once, which could
6657                  * happen if "all playlists" is enabled, but there is more
6658                  * than 1 track using playlists "from" a given track.
6659                  */
6660
6661                 set<boost::shared_ptr<Playlist> > pl;
6662
6663                 if (all_playlists) {
6664                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6665                         if (rtav) {
6666                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6667                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6668                                         pl.insert (*p);
6669                                 }
6670                         }
6671                 } else {
6672                         if ((*x)->playlist ()) {
6673                                 pl.insert ((*x)->playlist ());
6674                         }
6675                 }
6676                 
6677                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6678
6679                         (*i)->clear_changes ();
6680                         (*i)->clear_owned_changes ();
6681
6682                         if (opt == SplitIntersected) {
6683                                 (*i)->split (pos);
6684                         }
6685
6686                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6687
6688                         vector<Command*> cmds;
6689                         (*i)->rdiff (cmds);
6690                         _session->add_commands (cmds);
6691
6692                         _session->add_command (new StatefulDiffCommand (*i));
6693                         commit = true;
6694                 }
6695
6696                 /* automation */
6697                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6698                 if (rtav) {
6699                         rtav->route ()->shift (pos, frames);
6700                         commit = true;
6701                 }
6702         }
6703
6704         /* markers */
6705         if (markers_too) {
6706                 bool moved = false;
6707                 XMLNode& before (_session->locations()->get_state());
6708                 Locations::LocationList copy (_session->locations()->list());
6709
6710                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6711
6712                         Locations::LocationList::const_iterator tmp;
6713
6714                         bool const was_locked = (*i)->locked ();
6715                         if (locked_markers_too) {
6716                                 (*i)->unlock ();
6717                         }
6718
6719                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6720
6721                                 if ((*i)->start() >= pos) {
6722                                         (*i)->set_start ((*i)->start() + frames);
6723                                         if (!(*i)->is_mark()) {
6724                                                 (*i)->set_end ((*i)->end() + frames);
6725                                         }
6726                                         moved = true;
6727                                 }
6728
6729                         }
6730
6731                         if (was_locked) {
6732                                 (*i)->lock ();
6733                         }
6734                 }
6735
6736                 if (moved) {
6737                         XMLNode& after (_session->locations()->get_state());
6738                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6739                 }
6740         }
6741
6742         if (tempo_too) {
6743                 _session->tempo_map().insert_time (pos, frames);
6744         }
6745
6746         if (commit) {
6747                 commit_reversible_command ();
6748         }
6749 }
6750
6751 void
6752 Editor::fit_selected_tracks ()
6753 {
6754         if (!selection->tracks.empty()) {
6755                 fit_tracks (selection->tracks);
6756         } else {
6757                 TrackViewList tvl;
6758
6759                 /* no selected tracks - use tracks with selected regions */
6760
6761                 if (!selection->regions.empty()) {
6762                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6763                                 tvl.push_back (&(*r)->get_time_axis_view ());
6764                         }
6765
6766                         if (!tvl.empty()) {
6767                                 fit_tracks (tvl);
6768                         }
6769                 } else if (internal_editing()) {
6770                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6771                            the entered track
6772                         */
6773                         if (entered_track) {
6774                                 tvl.push_back (entered_track);
6775                                 fit_tracks (tvl);
6776                         }
6777                 }
6778         }
6779 }
6780
6781 void
6782 Editor::fit_tracks (TrackViewList & tracks)
6783 {
6784         if (tracks.empty()) {
6785                 return;
6786         }
6787
6788         uint32_t child_heights = 0;
6789         int visible_tracks = 0;
6790
6791         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6792
6793                 if (!(*t)->marked_for_display()) {
6794                         continue;
6795                 }
6796
6797                 child_heights += (*t)->effective_height() - (*t)->current_height();
6798                 ++visible_tracks;
6799         }
6800
6801         /* compute the per-track height from:
6802
6803            total canvas visible height - 
6804                  height that will be taken by visible children of selected
6805                  tracks - height of the ruler/hscroll area 
6806         */
6807         uint32_t h = (uint32_t) floor ((_visible_canvas_height - (child_heights + _trackview_group->canvas_origin().y)) / visible_tracks);
6808         double first_y_pos = DBL_MAX;
6809
6810         if (h < TimeAxisView::preset_height (HeightSmall)) {
6811                 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6812                 /* too small to be displayed */
6813                 return;
6814         }
6815
6816         undo_visual_stack.push_back (current_visual_state (true));
6817         no_save_visual = true;
6818
6819         /* build a list of all tracks, including children */
6820
6821         TrackViewList all;
6822         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6823                 all.push_back (*i);
6824                 TimeAxisView::Children c = (*i)->get_child_list ();
6825                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6826                         all.push_back (j->get());
6827                 }
6828         }
6829
6830         /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6831
6832         bool within_selected = false;
6833
6834         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6835
6836                 TrackViewList::iterator next;
6837
6838                 next = t;
6839                 ++next;
6840                 
6841                 if ((*t)->marked_for_display ()) {
6842                         if (tracks.contains (*t)) { 
6843                                 (*t)->set_height (h);
6844                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6845                                 within_selected = true;
6846                         } else if (within_selected) {
6847                                 hide_track_in_display (*t);
6848                         }
6849                 }
6850         }
6851
6852         /*
6853            set the controls_layout height now, because waiting for its size
6854            request signal handler will cause the vertical adjustment setting to fail
6855         */
6856
6857         controls_layout.property_height () = _full_canvas_height;
6858         vertical_adjustment.set_value (first_y_pos);
6859
6860         redo_visual_stack.push_back (current_visual_state (true));
6861 }
6862
6863 void
6864 Editor::save_visual_state (uint32_t n)
6865 {
6866         while (visual_states.size() <= n) {
6867                 visual_states.push_back (0);
6868         }
6869
6870         if (visual_states[n] != 0) {
6871                 delete visual_states[n];
6872         }
6873
6874         visual_states[n] = current_visual_state (true);
6875         gdk_beep ();
6876 }
6877
6878 void
6879 Editor::goto_visual_state (uint32_t n)
6880 {
6881         if (visual_states.size() <= n) {
6882                 return;
6883         }
6884
6885         if (visual_states[n] == 0) {
6886                 return;
6887         }
6888
6889         use_visual_state (*visual_states[n]);
6890 }
6891
6892 void
6893 Editor::start_visual_state_op (uint32_t n)
6894 {
6895         save_visual_state (n);
6896         
6897         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6898         char buf[32];
6899         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6900         pup->set_text (buf);
6901         pup->touch();
6902 }
6903
6904 void
6905 Editor::cancel_visual_state_op (uint32_t n)
6906 {
6907         goto_visual_state (n);
6908 }
6909
6910 void
6911 Editor::toggle_region_mute ()
6912 {
6913         if (_ignore_region_action) {
6914                 return;
6915         }
6916
6917         RegionSelection rs = get_regions_from_selection_and_entered ();
6918
6919         if (rs.empty ()) {
6920                 return;
6921         }
6922
6923         if (rs.size() > 1) {
6924                 begin_reversible_command (_("mute regions"));
6925         } else {
6926                 begin_reversible_command (_("mute region"));
6927         }
6928
6929         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6930
6931                 (*i)->region()->playlist()->clear_changes ();
6932                 (*i)->region()->set_muted (!(*i)->region()->muted ());
6933                 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6934
6935         }
6936
6937         commit_reversible_command ();
6938 }
6939
6940 void
6941 Editor::combine_regions ()
6942 {
6943         /* foreach track with selected regions, take all selected regions
6944            and join them into a new region containing the subregions (as a
6945            playlist)
6946         */
6947
6948         typedef set<RouteTimeAxisView*> RTVS;
6949         RTVS tracks;
6950
6951         if (selection->regions.empty()) {
6952                 return;
6953         }
6954
6955         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6956                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6957
6958                 if (rtv) {
6959                         tracks.insert (rtv);
6960                 }
6961         }
6962
6963         begin_reversible_command (_("combine regions"));
6964
6965         vector<RegionView*> new_selection;
6966
6967         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6968                 RegionView* rv;
6969
6970                 if ((rv = (*i)->combine_regions ()) != 0) {
6971                         new_selection.push_back (rv);
6972                 }
6973         }
6974
6975         selection->clear_regions ();
6976         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6977                 selection->add (*i);
6978         }
6979
6980         commit_reversible_command ();
6981 }
6982
6983 void
6984 Editor::uncombine_regions ()
6985 {
6986         typedef set<RouteTimeAxisView*> RTVS;
6987         RTVS tracks;
6988
6989         if (selection->regions.empty()) {
6990                 return;
6991         }
6992
6993         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6994                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6995
6996                 if (rtv) {
6997                         tracks.insert (rtv);
6998                 }
6999         }
7000
7001         begin_reversible_command (_("uncombine regions"));
7002
7003         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7004                 (*i)->uncombine_regions ();
7005         }
7006
7007         commit_reversible_command ();
7008 }
7009
7010 void
7011 Editor::toggle_midi_input_active (bool flip_others)
7012 {
7013         bool onoff = false;
7014         boost::shared_ptr<RouteList> rl (new RouteList);
7015
7016         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7017                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7018
7019                 if (!rtav) {
7020                         continue;
7021                 }
7022
7023                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7024
7025                 if (mt) {
7026                         rl->push_back (rtav->route());
7027                         onoff = !mt->input_active();
7028                 }
7029         }
7030         
7031         _session->set_exclusive_input_active (rl, onoff, flip_others);
7032 }
7033
7034 void
7035 Editor::lock ()
7036 {
7037         if (!lock_dialog) {
7038                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
7039
7040                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
7041                 lock_dialog->get_vbox()->pack_start (*padlock);
7042
7043                 ArdourButton* b = manage (new ArdourButton);
7044                 b->set_name ("lock button");
7045                 b->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Click to unlock")));
7046                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
7047                 lock_dialog->get_vbox()->pack_start (*b);
7048                 
7049                 lock_dialog->get_vbox()->show_all ();
7050                 lock_dialog->set_size_request (200, 200);
7051         }
7052         
7053 #ifdef __APPLE__
7054         /* The global menu bar continues to be accessible to applications
7055            with modal dialogs, which means that we need to desensitize
7056            all items in the menu bar. Since those items are really just
7057            proxies for actions, that means disabling all actions.
7058         */
7059         ActionManager::disable_all_actions ();
7060 #endif
7061         lock_dialog->present ();
7062 }
7063
7064 void
7065 Editor::unlock ()
7066 {
7067         lock_dialog->hide ();
7068         
7069 #ifdef __APPLE__
7070         ActionManager::pop_action_state ();
7071 #endif  
7072
7073         if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
7074                 start_lock_event_timing ();
7075         }
7076 }