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