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