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