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