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