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