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