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