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