Don't move the Range Selection after performing Multi Duplicate
[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                 if (times == 1.0f) {
4941                         // now "move" range selection to after the current range selection
4942                         framecnt_t distance = 0;
4943
4944                         if (clicked_selection) {
4945                                 distance =
4946                                     selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4947                         } else {
4948                                 distance = selection->time.end_frame () - selection->time.start ();
4949                         }
4950
4951                         selection->move_time (distance);
4952                 }
4953                 commit_reversible_command ();
4954         }
4955 }
4956
4957 /** Reset all selected points to the relevant default value */
4958 void
4959 Editor::reset_point_selection ()
4960 {
4961         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4962                 ARDOUR::AutomationList::iterator j = (*i)->model ();
4963                 (*j)->value = (*i)->line().the_list()->default_value ();
4964         }
4965 }
4966
4967 void
4968 Editor::center_playhead ()
4969 {
4970         float const page = _visible_canvas_width * samples_per_pixel;
4971         center_screen_internal (playhead_cursor->current_frame (), page);
4972 }
4973
4974 void
4975 Editor::center_edit_point ()
4976 {
4977         float const page = _visible_canvas_width * samples_per_pixel;
4978         center_screen_internal (get_preferred_edit_position(), page);
4979 }
4980
4981 /** Caller must begin and commit a reversible command */
4982 void
4983 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4984 {
4985         playlist->clear_changes ();
4986         playlist->clear ();
4987         _session->add_command (new StatefulDiffCommand (playlist));
4988 }
4989
4990 void
4991 Editor::nudge_track (bool use_edit, bool forwards)
4992 {
4993         boost::shared_ptr<Playlist> playlist;
4994         framepos_t distance;
4995         framepos_t next_distance;
4996         framepos_t start;
4997
4998         if (use_edit) {
4999                 start = get_preferred_edit_position();
5000         } else {
5001                 start = 0;
5002         }
5003
5004         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5005                 return;
5006         }
5007
5008         if (selection->tracks.empty()) {
5009                 return;
5010         }
5011
5012         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5013         bool in_command = false;
5014
5015         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5016
5017                 if ((playlist = (*i)->playlist()) == 0) {
5018                         continue;
5019                 }
5020
5021                 playlist->clear_changes ();
5022                 playlist->clear_owned_changes ();
5023
5024                 playlist->nudge_after (start, distance, forwards);
5025
5026                 if (!in_command) {
5027                         begin_reversible_command (_("nudge track"));
5028                         in_command = true;
5029                 }
5030                 vector<Command*> cmds;
5031
5032                 playlist->rdiff (cmds);
5033                 _session->add_commands (cmds);
5034
5035                 _session->add_command (new StatefulDiffCommand (playlist));
5036         }
5037
5038         if (in_command) {
5039                 commit_reversible_command ();
5040         }
5041 }
5042
5043 void
5044 Editor::remove_last_capture ()
5045 {
5046         vector<string> choices;
5047         string prompt;
5048
5049         if (!_session) {
5050                 return;
5051         }
5052
5053         if (Config->get_verify_remove_last_capture()) {
5054                 prompt  = _("Do you really want to destroy the last capture?"
5055                             "\n(This is destructive and cannot be undone)");
5056
5057                 choices.push_back (_("No, do nothing."));
5058                 choices.push_back (_("Yes, destroy it."));
5059
5060                 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5061
5062                 if (prompter.run () == 1) {
5063                         _session->remove_last_capture ();
5064                         _regions->redisplay ();
5065                 }
5066
5067         } else {
5068                 _session->remove_last_capture();
5069                 _regions->redisplay ();
5070         }
5071 }
5072
5073 void
5074 Editor::normalize_region ()
5075 {
5076         if (!_session) {
5077                 return;
5078         }
5079
5080         RegionSelection rs = get_regions_from_selection_and_entered ();
5081
5082         if (rs.empty()) {
5083                 return;
5084         }
5085
5086         NormalizeDialog dialog (rs.size() > 1);
5087
5088         if (dialog.run () != RESPONSE_ACCEPT) {
5089                 return;
5090         }
5091
5092         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5093         gdk_flush ();
5094
5095         /* XXX: should really only count audio regions here */
5096         int const regions = rs.size ();
5097
5098         /* Make a list of the selected audio regions' maximum amplitudes, and also
5099            obtain the maximum amplitude of them all.
5100         */
5101         list<double> max_amps;
5102         list<double> rms_vals;
5103         double max_amp = 0;
5104         double max_rms = 0;
5105         bool use_rms = dialog.constrain_rms ();
5106
5107         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5108                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5109                 if (!arv) {
5110                         continue;
5111                 }
5112                 dialog.descend (1.0 / regions);
5113                 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5114                 if (use_rms) {
5115                         double r = arv->audio_region()->rms (&dialog);
5116                         max_rms = max (max_rms, r);
5117                         rms_vals.push_back (r);
5118                 }
5119
5120                 if (a == -1) {
5121                         /* the user cancelled the operation */
5122                         return;
5123                 }
5124
5125                 max_amps.push_back (a);
5126                 max_amp = max (max_amp, a);
5127                 dialog.ascend ();
5128         }
5129
5130         list<double>::const_iterator a = max_amps.begin ();
5131         list<double>::const_iterator l = rms_vals.begin ();
5132         bool in_command = false;
5133
5134         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5135                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5136                 if (!arv) {
5137                         continue;
5138                 }
5139
5140                 arv->region()->clear_changes ();
5141
5142                 double amp = dialog.normalize_individually() ? *a : max_amp;
5143                 double target = dialog.target_peak (); // dB
5144
5145                 if (use_rms) {
5146                         double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5147                         const double t_rms = dialog.target_rms ();
5148                         const gain_t c_peak = dB_to_coefficient (target);
5149                         const gain_t c_rms  = dB_to_coefficient (t_rms);
5150                         if ((amp_rms / c_rms) > (amp / c_peak)) {
5151                                 amp = amp_rms;
5152                                 target = t_rms;
5153                         }
5154                 }
5155
5156                 arv->audio_region()->normalize (amp, target);
5157
5158                 if (!in_command) {
5159                         begin_reversible_command (_("normalize"));
5160                         in_command = true;
5161                 }
5162                 _session->add_command (new StatefulDiffCommand (arv->region()));
5163
5164                 ++a;
5165                 ++l;
5166         }
5167
5168         if (in_command) {
5169                 commit_reversible_command ();
5170         }
5171 }
5172
5173
5174 void
5175 Editor::reset_region_scale_amplitude ()
5176 {
5177         if (!_session) {
5178                 return;
5179         }
5180
5181         RegionSelection rs = get_regions_from_selection_and_entered ();
5182
5183         if (rs.empty()) {
5184                 return;
5185         }
5186
5187         bool in_command = false;
5188
5189         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5190                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5191                 if (!arv)
5192                         continue;
5193                 arv->region()->clear_changes ();
5194                 arv->audio_region()->set_scale_amplitude (1.0f);
5195
5196                 if(!in_command) {
5197                                 begin_reversible_command ("reset gain");
5198                                 in_command = true;
5199                 }
5200                 _session->add_command (new StatefulDiffCommand (arv->region()));
5201         }
5202
5203         if (in_command) {
5204                 commit_reversible_command ();
5205         }
5206 }
5207
5208 void
5209 Editor::adjust_region_gain (bool up)
5210 {
5211         RegionSelection rs = get_regions_from_selection_and_entered ();
5212
5213         if (!_session || rs.empty()) {
5214                 return;
5215         }
5216
5217         bool in_command = false;
5218
5219         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5220                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5221                 if (!arv) {
5222                         continue;
5223                 }
5224
5225                 arv->region()->clear_changes ();
5226
5227                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5228
5229                 if (up) {
5230                         dB += 1;
5231                 } else {
5232                         dB -= 1;
5233                 }
5234
5235                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5236
5237                 if (!in_command) {
5238                                 begin_reversible_command ("adjust region gain");
5239                                 in_command = true;
5240                 }
5241                 _session->add_command (new StatefulDiffCommand (arv->region()));
5242         }
5243
5244         if (in_command) {
5245                 commit_reversible_command ();
5246         }
5247 }
5248
5249
5250 void
5251 Editor::reverse_region ()
5252 {
5253         if (!_session) {
5254                 return;
5255         }
5256
5257         Reverse rev (*_session);
5258         apply_filter (rev, _("reverse regions"));
5259 }
5260
5261 void
5262 Editor::strip_region_silence ()
5263 {
5264         if (!_session) {
5265                 return;
5266         }
5267
5268         RegionSelection rs = get_regions_from_selection_and_entered ();
5269
5270         if (rs.empty()) {
5271                 return;
5272         }
5273
5274         std::list<RegionView*> audio_only;
5275
5276         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5277                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5278                 if (arv) {
5279                         audio_only.push_back (arv);
5280                 }
5281         }
5282
5283         assert (!audio_only.empty());
5284
5285         StripSilenceDialog d (_session, audio_only);
5286         int const r = d.run ();
5287
5288         d.drop_rects ();
5289
5290         if (r == Gtk::RESPONSE_OK) {
5291                 ARDOUR::AudioIntervalMap silences;
5292                 d.silences (silences);
5293                 StripSilence s (*_session, silences, d.fade_length());
5294
5295                 apply_filter (s, _("strip silence"), &d);
5296         }
5297 }
5298
5299 Command*
5300 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5301 {
5302         Evoral::Sequence<Evoral::Beats>::Notes selected;
5303         mrv.selection_as_notelist (selected, true);
5304
5305         vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5306         v.push_back (selected);
5307
5308         Evoral::Beats pos_beats  = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5309
5310         return op (mrv.midi_region()->model(), pos_beats, v);
5311 }
5312
5313 void
5314 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5315 {
5316         if (rs.empty()) {
5317                 return;
5318         }
5319
5320         bool in_command = false;
5321
5322         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5323                 RegionSelection::const_iterator tmp = r;
5324                 ++tmp;
5325
5326                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5327
5328                 if (mrv) {
5329                         Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5330                         if (cmd) {
5331                                 if (!in_command) {
5332                                         begin_reversible_command (op.name ());
5333                                         in_command = true;
5334                                 }
5335                                 (*cmd)();
5336                                 _session->add_command (cmd);
5337                         }
5338                 }
5339
5340                 r = tmp;
5341         }
5342
5343         if (in_command) {
5344                 commit_reversible_command ();
5345         }
5346 }
5347
5348 void
5349 Editor::fork_region ()
5350 {
5351         RegionSelection rs = get_regions_from_selection_and_entered ();
5352
5353         if (rs.empty()) {
5354                 return;
5355         }
5356
5357         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5358         bool in_command = false;
5359
5360         gdk_flush ();
5361
5362         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5363                 RegionSelection::iterator tmp = r;
5364                 ++tmp;
5365
5366                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5367
5368                 if (mrv) {
5369                         try {
5370                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5371                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5372                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5373
5374                                 if (!in_command) {
5375                                         begin_reversible_command (_("Fork Region(s)"));
5376                                         in_command = true;
5377                                 }
5378                                 playlist->clear_changes ();
5379                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5380                                 _session->add_command(new StatefulDiffCommand (playlist));
5381                         } catch (...) {
5382                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5383                         }
5384                 }
5385
5386                 r = tmp;
5387         }
5388
5389         if (in_command) {
5390                 commit_reversible_command ();
5391         }
5392 }
5393
5394 void
5395 Editor::quantize_region ()
5396 {
5397         if (_session) {
5398                 quantize_regions(get_regions_from_selection_and_entered ());
5399         }
5400 }
5401
5402 void
5403 Editor::quantize_regions (const RegionSelection& rs)
5404 {
5405         if (rs.n_midi_regions() == 0) {
5406                 return;
5407         }
5408
5409         if (!quantize_dialog) {
5410                 quantize_dialog = new QuantizeDialog (*this);
5411         }
5412
5413         if (quantize_dialog->is_mapped()) {
5414                 /* in progress already */
5415                 return;
5416         }
5417
5418         quantize_dialog->present ();
5419         const int r = quantize_dialog->run ();
5420         quantize_dialog->hide ();
5421
5422         if (r == Gtk::RESPONSE_OK) {
5423                 Quantize quant (quantize_dialog->snap_start(),
5424                                 quantize_dialog->snap_end(),
5425                                 quantize_dialog->start_grid_size(),
5426                                 quantize_dialog->end_grid_size(),
5427                                 quantize_dialog->strength(),
5428                                 quantize_dialog->swing(),
5429                                 quantize_dialog->threshold());
5430
5431                 apply_midi_note_edit_op (quant, rs);
5432         }
5433 }
5434
5435 void
5436 Editor::legatize_region (bool shrink_only)
5437 {
5438         if (_session) {
5439                 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5440         }
5441 }
5442
5443 void
5444 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5445 {
5446         if (rs.n_midi_regions() == 0) {
5447                 return;
5448         }
5449
5450         Legatize legatize(shrink_only);
5451         apply_midi_note_edit_op (legatize, rs);
5452 }
5453
5454 void
5455 Editor::transform_region ()
5456 {
5457         if (_session) {
5458                 transform_regions(get_regions_from_selection_and_entered ());
5459         }
5460 }
5461
5462 void
5463 Editor::transform_regions (const RegionSelection& rs)
5464 {
5465         if (rs.n_midi_regions() == 0) {
5466                 return;
5467         }
5468
5469         TransformDialog td;
5470
5471         td.present();
5472         const int r = td.run();
5473         td.hide();
5474
5475         if (r == Gtk::RESPONSE_OK) {
5476                 Transform transform(td.get());
5477                 apply_midi_note_edit_op(transform, rs);
5478         }
5479 }
5480
5481 void
5482 Editor::transpose_region ()
5483 {
5484         if (_session) {
5485                 transpose_regions(get_regions_from_selection_and_entered ());
5486         }
5487 }
5488
5489 void
5490 Editor::transpose_regions (const RegionSelection& rs)
5491 {
5492         if (rs.n_midi_regions() == 0) {
5493                 return;
5494         }
5495
5496         TransposeDialog d;
5497         int const r = d.run ();
5498
5499         if (r == RESPONSE_ACCEPT) {
5500                 Transpose transpose(d.semitones ());
5501                 apply_midi_note_edit_op (transpose, rs);
5502         }
5503 }
5504
5505 void
5506 Editor::insert_patch_change (bool from_context)
5507 {
5508         RegionSelection rs = get_regions_from_selection_and_entered ();
5509
5510         if (rs.empty ()) {
5511                 return;
5512         }
5513
5514         const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5515
5516         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5517            there may be more than one, but the PatchChangeDialog can only offer
5518            one set of patch menus.
5519         */
5520         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5521
5522         Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5523         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5524
5525         if (d.run() == RESPONSE_CANCEL) {
5526                 return;
5527         }
5528
5529         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5530                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5531                 if (mrv) {
5532                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5533                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5534                         }
5535                 }
5536         }
5537 }
5538
5539 void
5540 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5541 {
5542         RegionSelection rs = get_regions_from_selection_and_entered ();
5543
5544         if (rs.empty()) {
5545                 return;
5546         }
5547
5548         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5549         bool in_command = false;
5550
5551         gdk_flush ();
5552
5553         int n = 0;
5554         int const N = rs.size ();
5555
5556         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5557                 RegionSelection::iterator tmp = r;
5558                 ++tmp;
5559
5560                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5561                 if (arv) {
5562                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5563
5564                         if (progress) {
5565                                 progress->descend (1.0 / N);
5566                         }
5567
5568                         if (arv->audio_region()->apply (filter, progress) == 0) {
5569
5570                                 playlist->clear_changes ();
5571                                 playlist->clear_owned_changes ();
5572
5573                                 if (!in_command) {
5574                                         begin_reversible_command (command);
5575                                         in_command = true;
5576                                 }
5577
5578                                 if (filter.results.empty ()) {
5579
5580                                         /* no regions returned; remove the old one */
5581                                         playlist->remove_region (arv->region ());
5582
5583                                 } else {
5584
5585                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5586
5587                                         /* first region replaces the old one */
5588                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5589                                         ++res;
5590
5591                                         /* add the rest */
5592                                         while (res != filter.results.end()) {
5593                                                 playlist->add_region (*res, (*res)->position());
5594                                                 ++res;
5595                                         }
5596
5597                                 }
5598
5599                                 /* We might have removed regions, which alters other regions' layering_index,
5600                                    so we need to do a recursive diff here.
5601                                 */
5602                                 vector<Command*> cmds;
5603                                 playlist->rdiff (cmds);
5604                                 _session->add_commands (cmds);
5605
5606                                 _session->add_command(new StatefulDiffCommand (playlist));
5607                         }
5608
5609                         if (progress) {
5610                                 progress->ascend ();
5611                         }
5612                 }
5613
5614                 r = tmp;
5615                 ++n;
5616         }
5617
5618         if (in_command) {
5619                 commit_reversible_command ();
5620         }
5621 }
5622
5623 void
5624 Editor::external_edit_region ()
5625 {
5626         /* more to come */
5627 }
5628
5629 void
5630 Editor::reset_region_gain_envelopes ()
5631 {
5632         RegionSelection rs = get_regions_from_selection_and_entered ();
5633
5634         if (!_session || rs.empty()) {
5635                 return;
5636         }
5637
5638         bool in_command = false;
5639
5640         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5641                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5642                 if (arv) {
5643                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5644                         XMLNode& before (alist->get_state());
5645
5646                         arv->audio_region()->set_default_envelope ();
5647
5648                         if (!in_command) {
5649                                 begin_reversible_command (_("reset region gain"));
5650                                 in_command = true;
5651                         }
5652                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5653                 }
5654         }
5655
5656         if (in_command) {
5657                 commit_reversible_command ();
5658         }
5659 }
5660
5661 void
5662 Editor::set_region_gain_visibility (RegionView* rv)
5663 {
5664         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5665         if (arv) {
5666                 arv->update_envelope_visibility();
5667         }
5668 }
5669
5670 void
5671 Editor::set_gain_envelope_visibility ()
5672 {
5673         if (!_session) {
5674                 return;
5675         }
5676
5677         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5678                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5679                 if (v) {
5680                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5681                 }
5682         }
5683 }
5684
5685 void
5686 Editor::toggle_gain_envelope_active ()
5687 {
5688         if (_ignore_region_action) {
5689                 return;
5690         }
5691
5692         RegionSelection rs = get_regions_from_selection_and_entered ();
5693
5694         if (!_session || rs.empty()) {
5695                 return;
5696         }
5697
5698         bool in_command = false;
5699
5700         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5701                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5702                 if (arv) {
5703                         arv->region()->clear_changes ();
5704                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5705
5706                         if (!in_command) {
5707                                 begin_reversible_command (_("region gain envelope active"));
5708                                 in_command = true;
5709                         }
5710                         _session->add_command (new StatefulDiffCommand (arv->region()));
5711                 }
5712         }
5713
5714         if (in_command) {
5715                 commit_reversible_command ();
5716         }
5717 }
5718
5719 void
5720 Editor::toggle_region_lock ()
5721 {
5722         if (_ignore_region_action) {
5723                 return;
5724         }
5725
5726         RegionSelection rs = get_regions_from_selection_and_entered ();
5727
5728         if (!_session || rs.empty()) {
5729                 return;
5730         }
5731
5732         begin_reversible_command (_("toggle region lock"));
5733
5734         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5735                 (*i)->region()->clear_changes ();
5736                 (*i)->region()->set_locked (!(*i)->region()->locked());
5737                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5738         }
5739
5740         commit_reversible_command ();
5741 }
5742
5743 void
5744 Editor::toggle_region_video_lock ()
5745 {
5746         if (_ignore_region_action) {
5747                 return;
5748         }
5749
5750         RegionSelection rs = get_regions_from_selection_and_entered ();
5751
5752         if (!_session || rs.empty()) {
5753                 return;
5754         }
5755
5756         begin_reversible_command (_("Toggle Video Lock"));
5757
5758         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5759                 (*i)->region()->clear_changes ();
5760                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5761                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5762         }
5763
5764         commit_reversible_command ();
5765 }
5766
5767 void
5768 Editor::toggle_region_lock_style ()
5769 {
5770         if (_ignore_region_action) {
5771                 return;
5772         }
5773
5774         RegionSelection rs = get_regions_from_selection_and_entered ();
5775
5776         if (!_session || rs.empty()) {
5777                 return;
5778         }
5779
5780         begin_reversible_command (_("region lock style"));
5781
5782         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5783                 (*i)->region()->clear_changes ();
5784                 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5785                 (*i)->region()->set_position_lock_style (ns);
5786                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5787         }
5788
5789         commit_reversible_command ();
5790 }
5791
5792 void
5793 Editor::toggle_opaque_region ()
5794 {
5795         if (_ignore_region_action) {
5796                 return;
5797         }
5798
5799         RegionSelection rs = get_regions_from_selection_and_entered ();
5800
5801         if (!_session || rs.empty()) {
5802                 return;
5803         }
5804
5805         begin_reversible_command (_("change region opacity"));
5806
5807         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5808                 (*i)->region()->clear_changes ();
5809                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5810                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5811         }
5812
5813         commit_reversible_command ();
5814 }
5815
5816 void
5817 Editor::toggle_record_enable ()
5818 {
5819         bool new_state = false;
5820         bool first = true;
5821         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5822                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5823                 if (!rtav)
5824                         continue;
5825                 if (!rtav->is_track())
5826                         continue;
5827
5828                 if (first) {
5829                         new_state = !rtav->track()->rec_enable_control()->get_value();
5830                         first = false;
5831                 }
5832
5833                 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5834         }
5835 }
5836
5837 void
5838 Editor::toggle_solo ()
5839 {
5840         bool new_state = false;
5841         bool first = true;
5842         boost::shared_ptr<ControlList> cl (new ControlList);
5843
5844         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5845                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5846
5847                 if (!rtav) {
5848                         continue;
5849                 }
5850
5851                 if (first) {
5852                         new_state = !rtav->route()->soloed ();
5853                         first = false;
5854                 }
5855
5856                 cl->push_back (rtav->route()->solo_control());
5857         }
5858
5859         _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5860 }
5861
5862 void
5863 Editor::toggle_mute ()
5864 {
5865         bool new_state = false;
5866         bool first = true;
5867         boost::shared_ptr<RouteList> rl (new RouteList);
5868
5869         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5870                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5871
5872                 if (!rtav) {
5873                         continue;
5874                 }
5875
5876                 if (first) {
5877                         new_state = !rtav->route()->muted();
5878                         first = false;
5879                 }
5880
5881                 rl->push_back (rtav->route());
5882         }
5883
5884         _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5885 }
5886
5887 void
5888 Editor::toggle_solo_isolate ()
5889 {
5890 }
5891
5892
5893 void
5894 Editor::fade_range ()
5895 {
5896         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5897
5898         begin_reversible_command (_("fade range"));
5899
5900         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5901                 (*i)->fade_range (selection->time);
5902         }
5903
5904         commit_reversible_command ();
5905 }
5906
5907
5908 void
5909 Editor::set_fade_length (bool in)
5910 {
5911         RegionSelection rs = get_regions_from_selection_and_entered ();
5912
5913         if (rs.empty()) {
5914                 return;
5915         }
5916
5917         /* we need a region to measure the offset from the start */
5918
5919         RegionView* rv = rs.front ();
5920
5921         framepos_t pos = get_preferred_edit_position();
5922         framepos_t len;
5923         char const * cmd;
5924
5925         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5926                 /* edit point is outside the relevant region */
5927                 return;
5928         }
5929
5930         if (in) {
5931                 if (pos <= rv->region()->position()) {
5932                         /* can't do it */
5933                         return;
5934                 }
5935                 len = pos - rv->region()->position();
5936                 cmd = _("set fade in length");
5937         } else {
5938                 if (pos >= rv->region()->last_frame()) {
5939                         /* can't do it */
5940                         return;
5941                 }
5942                 len = rv->region()->last_frame() - pos;
5943                 cmd = _("set fade out length");
5944         }
5945
5946         bool in_command = false;
5947
5948         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5949                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5950
5951                 if (!tmp) {
5952                         continue;
5953                 }
5954
5955                 boost::shared_ptr<AutomationList> alist;
5956                 if (in) {
5957                         alist = tmp->audio_region()->fade_in();
5958                 } else {
5959                         alist = tmp->audio_region()->fade_out();
5960                 }
5961
5962                 XMLNode &before = alist->get_state();
5963
5964                 if (in) {
5965                         tmp->audio_region()->set_fade_in_length (len);
5966                         tmp->audio_region()->set_fade_in_active (true);
5967                 } else {
5968                         tmp->audio_region()->set_fade_out_length (len);
5969                         tmp->audio_region()->set_fade_out_active (true);
5970                 }
5971
5972                 if (!in_command) {
5973                         begin_reversible_command (cmd);
5974                         in_command = true;
5975                 }
5976                 XMLNode &after = alist->get_state();
5977                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5978         }
5979
5980         if (in_command) {
5981                 commit_reversible_command ();
5982         }
5983 }
5984
5985 void
5986 Editor::set_fade_in_shape (FadeShape shape)
5987 {
5988         RegionSelection rs = get_regions_from_selection_and_entered ();
5989
5990         if (rs.empty()) {
5991                 return;
5992         }
5993         bool in_command = false;
5994
5995         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5996                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5997
5998                 if (!tmp) {
5999                         continue;
6000                 }
6001
6002                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6003                 XMLNode &before = alist->get_state();
6004
6005                 tmp->audio_region()->set_fade_in_shape (shape);
6006
6007                 if (!in_command) {
6008                         begin_reversible_command (_("set fade in shape"));
6009                         in_command = true;
6010                 }
6011                 XMLNode &after = alist->get_state();
6012                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6013         }
6014
6015         if (in_command) {
6016                 commit_reversible_command ();
6017         }
6018 }
6019
6020 void
6021 Editor::set_fade_out_shape (FadeShape shape)
6022 {
6023         RegionSelection rs = get_regions_from_selection_and_entered ();
6024
6025         if (rs.empty()) {
6026                 return;
6027         }
6028         bool in_command = false;
6029
6030         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6031                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6032
6033                 if (!tmp) {
6034                         continue;
6035                 }
6036
6037                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6038                 XMLNode &before = alist->get_state();
6039
6040                 tmp->audio_region()->set_fade_out_shape (shape);
6041
6042                 if(!in_command) {
6043                         begin_reversible_command (_("set fade out shape"));
6044                         in_command = true;
6045                 }
6046                 XMLNode &after = alist->get_state();
6047                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6048         }
6049
6050         if (in_command) {
6051                 commit_reversible_command ();
6052         }
6053 }
6054
6055 void
6056 Editor::set_fade_in_active (bool yn)
6057 {
6058         RegionSelection rs = get_regions_from_selection_and_entered ();
6059
6060         if (rs.empty()) {
6061                 return;
6062         }
6063         bool in_command = false;
6064
6065         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6066                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6067
6068                 if (!tmp) {
6069                         continue;
6070                 }
6071
6072
6073                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6074
6075                 ar->clear_changes ();
6076                 ar->set_fade_in_active (yn);
6077
6078                 if (!in_command) {
6079                         begin_reversible_command (_("set fade in active"));
6080                         in_command = true;
6081                 }
6082                 _session->add_command (new StatefulDiffCommand (ar));
6083         }
6084
6085         if (in_command) {
6086                 commit_reversible_command ();
6087         }
6088 }
6089
6090 void
6091 Editor::set_fade_out_active (bool yn)
6092 {
6093         RegionSelection rs = get_regions_from_selection_and_entered ();
6094
6095         if (rs.empty()) {
6096                 return;
6097         }
6098         bool in_command = false;
6099
6100         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6101                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6102
6103                 if (!tmp) {
6104                         continue;
6105                 }
6106
6107                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6108
6109                 ar->clear_changes ();
6110                 ar->set_fade_out_active (yn);
6111
6112                 if (!in_command) {
6113                         begin_reversible_command (_("set fade out active"));
6114                         in_command = true;
6115                 }
6116                 _session->add_command(new StatefulDiffCommand (ar));
6117         }
6118
6119         if (in_command) {
6120                 commit_reversible_command ();
6121         }
6122 }
6123
6124 void
6125 Editor::toggle_region_fades (int dir)
6126 {
6127         if (_ignore_region_action) {
6128                 return;
6129         }
6130
6131         boost::shared_ptr<AudioRegion> ar;
6132         bool yn = false;
6133
6134         RegionSelection rs = get_regions_from_selection_and_entered ();
6135
6136         if (rs.empty()) {
6137                 return;
6138         }
6139
6140         RegionSelection::iterator i;
6141         for (i = rs.begin(); i != rs.end(); ++i) {
6142                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6143                         if (dir == -1) {
6144                                 yn = ar->fade_out_active ();
6145                         } else {
6146                                 yn = ar->fade_in_active ();
6147                         }
6148                         break;
6149                 }
6150         }
6151
6152         if (i == rs.end()) {
6153                 return;
6154         }
6155
6156         /* XXX should this undo-able? */
6157         bool in_command = false;
6158
6159         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6160                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6161                         continue;
6162                 }
6163                 ar->clear_changes ();
6164
6165                 if (dir == 1 || dir == 0) {
6166                         ar->set_fade_in_active (!yn);
6167                 }
6168
6169                 if (dir == -1 || dir == 0) {
6170                         ar->set_fade_out_active (!yn);
6171                 }
6172                 if (!in_command) {
6173                         begin_reversible_command (_("toggle fade active"));
6174                         in_command = true;
6175                 }
6176                 _session->add_command(new StatefulDiffCommand (ar));
6177         }
6178
6179         if (in_command) {
6180                 commit_reversible_command ();
6181         }
6182 }
6183
6184
6185 /** Update region fade visibility after its configuration has been changed */
6186 void
6187 Editor::update_region_fade_visibility ()
6188 {
6189         bool _fade_visibility = _session->config.get_show_region_fades ();
6190
6191         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6192                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6193                 if (v) {
6194                         if (_fade_visibility) {
6195                                 v->audio_view()->show_all_fades ();
6196                         } else {
6197                                 v->audio_view()->hide_all_fades ();
6198                         }
6199                 }
6200         }
6201 }
6202
6203 void
6204 Editor::set_edit_point ()
6205 {
6206         framepos_t where;
6207         bool ignored;
6208
6209         if (!mouse_frame (where, ignored)) {
6210                 return;
6211         }
6212
6213         snap_to (where);
6214
6215         if (selection->markers.empty()) {
6216
6217                 mouse_add_new_marker (where);
6218
6219         } else {
6220                 bool ignored;
6221
6222                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6223
6224                 if (loc) {
6225                         loc->move_to (where);
6226                 }
6227         }
6228 }
6229
6230 void
6231 Editor::set_playhead_cursor ()
6232 {
6233         if (entered_marker) {
6234                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6235         } else {
6236                 framepos_t where;
6237                 bool ignored;
6238
6239                 if (!mouse_frame (where, ignored)) {
6240                         return;
6241                 }
6242
6243                 snap_to (where);
6244
6245                 if (_session) {
6246                         _session->request_locate (where, _session->transport_rolling());
6247                 }
6248         }
6249
6250         if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6251                 cancel_time_selection();
6252         }
6253 }
6254
6255 void
6256 Editor::split_region ()
6257 {
6258         if (_drags->active ()) {
6259                 return;
6260         }
6261
6262         //if a range is selected, separate it
6263         if ( !selection->time.empty()) {
6264                 separate_regions_between (selection->time);
6265                 return;
6266         }
6267
6268         //if no range was selected, try to find some regions to split
6269         if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6270
6271                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6272
6273                 framepos_t where = get_preferred_edit_position ();
6274
6275                 if (rs.empty()) {
6276                         return;
6277                 }
6278
6279                 if (snap_musical()) {
6280                         split_regions_at (where, rs, get_grid_music_divisions (0));
6281                 } else {
6282                         split_regions_at (where, rs, 0);
6283                 }
6284         }
6285 }
6286
6287 void
6288 Editor::select_next_route()
6289 {
6290         if (selection->tracks.empty()) {
6291                 selection->set (track_views.front());
6292                 return;
6293         }
6294
6295         TimeAxisView* current = selection->tracks.front();
6296
6297         RouteUI *rui;
6298         do {
6299                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6300
6301                         if (*i == current) {
6302                                 ++i;
6303                                 if (i != track_views.end()) {
6304                                         current = (*i);
6305                                 } else {
6306                                         current = (*(track_views.begin()));
6307                                         //selection->set (*(track_views.begin()));
6308                                 }
6309                                 break;
6310                         }
6311                 }
6312
6313                 rui = dynamic_cast<RouteUI *>(current);
6314
6315         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6316
6317         selection->set (current);
6318
6319         ensure_time_axis_view_is_visible (*current, false);
6320 }
6321
6322 void
6323 Editor::select_prev_route()
6324 {
6325         if (selection->tracks.empty()) {
6326                 selection->set (track_views.front());
6327                 return;
6328         }
6329
6330         TimeAxisView* current = selection->tracks.front();
6331
6332         RouteUI *rui;
6333         do {
6334                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6335
6336                         if (*i == current) {
6337                                 ++i;
6338                                 if (i != track_views.rend()) {
6339                                         current = (*i);
6340                                 } else {
6341                                         current = *(track_views.rbegin());
6342                                 }
6343                                 break;
6344                         }
6345                 }
6346                 rui = dynamic_cast<RouteUI *>(current);
6347
6348         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6349
6350         selection->set (current);
6351
6352         ensure_time_axis_view_is_visible (*current, false);
6353 }
6354
6355 void
6356 Editor::set_loop_from_selection (bool play)
6357 {
6358         if (_session == 0) {
6359                 return;
6360         }
6361
6362         framepos_t start, end;
6363         if (!get_selection_extents ( start, end))
6364                 return;
6365
6366         set_loop_range (start, end,  _("set loop range from selection"));
6367
6368         if (play) {
6369                 _session->request_play_loop (true, true);
6370         }
6371 }
6372
6373 void
6374 Editor::set_loop_from_region (bool play)
6375 {
6376         framepos_t start, end;
6377         if (!get_selection_extents ( start, end))
6378                 return;
6379
6380         set_loop_range (start, end, _("set loop range from region"));
6381
6382         if (play) {
6383                 _session->request_locate (start, true);
6384                 _session->request_play_loop (true);
6385         }
6386 }
6387
6388 void
6389 Editor::set_punch_from_selection ()
6390 {
6391         if (_session == 0) {
6392                 return;
6393         }
6394
6395         framepos_t start, end;
6396         if (!get_selection_extents ( start, end))
6397                 return;
6398
6399         set_punch_range (start, end,  _("set punch range from selection"));
6400 }
6401
6402 void
6403 Editor::set_auto_punch_range ()
6404 {
6405         // auto punch in/out button from a single button
6406         // If Punch In is unset, set punch range from playhead to end, enable punch in
6407         // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6408         //   rewound beyond the Punch In marker, in which case that marker will be moved back
6409         //   to the current playhead position.
6410         // If punch out is set, it clears the punch range and Punch In/Out buttons
6411
6412         if (_session == 0) {
6413                 return;
6414         }
6415
6416         Location* tpl = transport_punch_location();
6417         framepos_t now = playhead_cursor->current_frame();
6418         framepos_t begin = now;
6419         framepos_t end = _session->current_end_frame();
6420
6421         if (!_session->config.get_punch_in()) {
6422                 // First Press - set punch in and create range from here to eternity
6423                 set_punch_range (begin, end, _("Auto Punch In"));
6424                 _session->config.set_punch_in(true);
6425         } else if (tpl && !_session->config.get_punch_out()) {
6426                 // Second press - update end range marker and set punch_out
6427                 if (now < tpl->start()) {
6428                         // playhead has been rewound - move start back  and pretend nothing happened
6429                         begin = now;
6430                         set_punch_range (begin, end, _("Auto Punch In/Out"));
6431                 } else {
6432                         // normal case for 2nd press - set the punch out
6433                         end = playhead_cursor->current_frame ();
6434                         set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6435                         _session->config.set_punch_out(true);
6436                 }
6437         } else  {
6438                 if (_session->config.get_punch_out()) {
6439                         _session->config.set_punch_out(false);
6440                 }
6441
6442                 if (_session->config.get_punch_in()) {
6443                         _session->config.set_punch_in(false);
6444                 }
6445
6446                 if (tpl)
6447                 {
6448                         // third press - unset punch in/out and remove range
6449                         _session->locations()->remove(tpl);
6450                 }
6451         }
6452
6453 }
6454
6455 void
6456 Editor::set_session_extents_from_selection ()
6457 {
6458         if (_session == 0) {
6459                 return;
6460         }
6461
6462         framepos_t start, end;
6463         if (!get_selection_extents ( start, end))
6464                 return;
6465
6466         Location* loc;
6467         if ((loc = _session->locations()->session_range_location()) == 0) {
6468                 _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6469         } else {
6470                 XMLNode &before = loc->get_state();
6471
6472                 _session->set_session_extents (start, end);
6473
6474                 XMLNode &after = loc->get_state();
6475
6476                 begin_reversible_command (_("set session start/end from selection"));
6477
6478                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6479
6480                 commit_reversible_command ();
6481         }
6482
6483         _session->set_end_is_free (false);
6484 }
6485
6486 void
6487 Editor::set_punch_start_from_edit_point ()
6488 {
6489         if (_session) {
6490
6491                 framepos_t start = 0;
6492                 framepos_t end = max_framepos;
6493
6494                 //use the existing punch end, if any
6495                 Location* tpl = transport_punch_location();
6496                 if (tpl) {
6497                         end = tpl->end();
6498                 }
6499
6500                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6501                         start = _session->audible_frame();
6502                 } else {
6503                         start = get_preferred_edit_position();
6504                 }
6505
6506                 //snap the selection start/end
6507                 snap_to(start);
6508
6509                 //if there's not already a sensible selection endpoint, go "forever"
6510                 if ( start > end ) {
6511                         end = max_framepos;
6512                 }
6513
6514                 set_punch_range (start, end, _("set punch start from EP"));
6515         }
6516
6517 }
6518
6519 void
6520 Editor::set_punch_end_from_edit_point ()
6521 {
6522         if (_session) {
6523
6524                 framepos_t start = 0;
6525                 framepos_t end = max_framepos;
6526
6527                 //use the existing punch start, if any
6528                 Location* tpl = transport_punch_location();
6529                 if (tpl) {
6530                         start = tpl->start();
6531                 }
6532
6533                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6534                         end = _session->audible_frame();
6535                 } else {
6536                         end = get_preferred_edit_position();
6537                 }
6538
6539                 //snap the selection start/end
6540                 snap_to(end);
6541
6542                 set_punch_range (start, end, _("set punch end from EP"));
6543
6544         }
6545 }
6546
6547 void
6548 Editor::set_loop_start_from_edit_point ()
6549 {
6550         if (_session) {
6551
6552                 framepos_t start = 0;
6553                 framepos_t end = max_framepos;
6554
6555                 //use the existing loop end, if any
6556                 Location* tpl = transport_loop_location();
6557                 if (tpl) {
6558                         end = tpl->end();
6559                 }
6560
6561                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6562                         start = _session->audible_frame();
6563                 } else {
6564                         start = get_preferred_edit_position();
6565                 }
6566
6567                 //snap the selection start/end
6568                 snap_to(start);
6569
6570                 //if there's not already a sensible selection endpoint, go "forever"
6571                 if ( start > end ) {
6572                         end = max_framepos;
6573                 }
6574
6575                 set_loop_range (start, end, _("set loop start from EP"));
6576         }
6577
6578 }
6579
6580 void
6581 Editor::set_loop_end_from_edit_point ()
6582 {
6583         if (_session) {
6584
6585                 framepos_t start = 0;
6586                 framepos_t end = max_framepos;
6587
6588                 //use the existing loop start, if any
6589                 Location* tpl = transport_loop_location();
6590                 if (tpl) {
6591                         start = tpl->start();
6592                 }
6593
6594                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6595                         end = _session->audible_frame();
6596                 } else {
6597                         end = get_preferred_edit_position();
6598                 }
6599
6600                 //snap the selection start/end
6601                 snap_to(end);
6602
6603                 set_loop_range (start, end, _("set loop end from EP"));
6604         }
6605 }
6606
6607 void
6608 Editor::set_punch_from_region ()
6609 {
6610         framepos_t start, end;
6611         if (!get_selection_extents ( start, end))
6612                 return;
6613
6614         set_punch_range (start, end, _("set punch range from region"));
6615 }
6616
6617 void
6618 Editor::pitch_shift_region ()
6619 {
6620         RegionSelection rs = get_regions_from_selection_and_entered ();
6621
6622         RegionSelection audio_rs;
6623         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6624                 if (dynamic_cast<AudioRegionView*> (*i)) {
6625                         audio_rs.push_back (*i);
6626                 }
6627         }
6628
6629         if (audio_rs.empty()) {
6630                 return;
6631         }
6632
6633         pitch_shift (audio_rs, 1.2);
6634 }
6635
6636 void
6637 Editor::set_tempo_from_region ()
6638 {
6639         RegionSelection rs = get_regions_from_selection_and_entered ();
6640
6641         if (!_session || rs.empty()) {
6642                 return;
6643         }
6644
6645         RegionView* rv = rs.front();
6646
6647         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6648 }
6649
6650 void
6651 Editor::use_range_as_bar ()
6652 {
6653         framepos_t start, end;
6654         if (get_edit_op_range (start, end)) {
6655                 define_one_bar (start, end);
6656         }
6657 }
6658
6659 void
6660 Editor::define_one_bar (framepos_t start, framepos_t end)
6661 {
6662         framepos_t length = end - start;
6663
6664         const Meter& m (_session->tempo_map().meter_at_frame (start));
6665
6666         /* length = 1 bar */
6667
6668         /* We're going to deliver a constant tempo here,
6669            so we can use frames per beat to determine length.
6670            now we want frames per beat.
6671            we have frames per bar, and beats per bar, so ...
6672         */
6673
6674         /* XXXX METER MATH */
6675
6676         double frames_per_beat = length / m.divisions_per_bar();
6677
6678         /* beats per minute = */
6679
6680         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6681
6682         /* now decide whether to:
6683
6684             (a) set global tempo
6685             (b) add a new tempo marker
6686
6687         */
6688
6689         const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6690
6691         bool do_global = false;
6692
6693         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6694
6695                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6696                    at the start, or create a new marker
6697                 */
6698
6699                 vector<string> options;
6700                 options.push_back (_("Cancel"));
6701                 options.push_back (_("Add new marker"));
6702                 options.push_back (_("Set global tempo"));
6703
6704                 Choice c (
6705                         _("Define one bar"),
6706                         _("Do you want to set the global tempo or add a new tempo marker?"),
6707                         options
6708                         );
6709
6710                 c.set_default_response (2);
6711
6712                 switch (c.run()) {
6713                 case 0:
6714                         return;
6715
6716                 case 2:
6717                         do_global = true;
6718                         break;
6719
6720                 default:
6721                         do_global = false;
6722                 }
6723
6724         } else {
6725
6726                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6727                    if the marker is at the region starter, change it, otherwise add
6728                    a new tempo marker
6729                 */
6730         }
6731
6732         begin_reversible_command (_("set tempo from region"));
6733         XMLNode& before (_session->tempo_map().get_state());
6734
6735         if (do_global) {
6736                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6737         } else if (t.frame() == start) {
6738                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6739         } else {
6740                 const Tempo tempo (beats_per_minute, t.note_type());
6741                 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6742         }
6743
6744         XMLNode& after (_session->tempo_map().get_state());
6745
6746         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6747         commit_reversible_command ();
6748 }
6749
6750 void
6751 Editor::split_region_at_transients ()
6752 {
6753         AnalysisFeatureList positions;
6754
6755         RegionSelection rs = get_regions_from_selection_and_entered ();
6756
6757         if (!_session || rs.empty()) {
6758                 return;
6759         }
6760
6761         begin_reversible_command (_("split regions"));
6762
6763         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6764
6765                 RegionSelection::iterator tmp;
6766
6767                 tmp = i;
6768                 ++tmp;
6769
6770                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6771
6772                 if (ar) {
6773                         ar->transients (positions);
6774                         split_region_at_points ((*i)->region(), positions, true);
6775                         positions.clear ();
6776                 }
6777
6778                 i = tmp;
6779         }
6780
6781         commit_reversible_command ();
6782
6783 }
6784
6785 void
6786 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6787 {
6788         bool use_rhythmic_rodent = false;
6789
6790         boost::shared_ptr<Playlist> pl = r->playlist();
6791
6792         list<boost::shared_ptr<Region> > new_regions;
6793
6794         if (!pl) {
6795                 return;
6796         }
6797
6798         if (positions.empty()) {
6799                 return;
6800         }
6801
6802         if (positions.size() > 20 && can_ferret) {
6803                 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);
6804                 MessageDialog msg (msgstr,
6805                                    false,
6806                                    Gtk::MESSAGE_INFO,
6807                                    Gtk::BUTTONS_OK_CANCEL);
6808
6809                 if (can_ferret) {
6810                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6811                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6812                 } else {
6813                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6814                 }
6815
6816                 msg.set_title (_("Excessive split?"));
6817                 msg.present ();
6818
6819                 int response = msg.run();
6820                 msg.hide ();
6821
6822                 switch (response) {
6823                 case RESPONSE_OK:
6824                         break;
6825                 case RESPONSE_APPLY:
6826                         use_rhythmic_rodent = true;
6827                         break;
6828                 default:
6829                         return;
6830                 }
6831         }
6832
6833         if (use_rhythmic_rodent) {
6834                 show_rhythm_ferret ();
6835                 return;
6836         }
6837
6838         AnalysisFeatureList::const_iterator x;
6839
6840         pl->clear_changes ();
6841         pl->clear_owned_changes ();
6842
6843         x = positions.begin();
6844
6845         if (x == positions.end()) {
6846                 return;
6847         }
6848
6849         pl->freeze ();
6850         pl->remove_region (r);
6851
6852         framepos_t pos = 0;
6853
6854         framepos_t rstart = r->first_frame ();
6855         framepos_t rend = r->last_frame ();
6856
6857         while (x != positions.end()) {
6858
6859                 /* deal with positons that are out of scope of present region bounds */
6860                 if (*x <= rstart || *x > rend) {
6861                         ++x;
6862                         continue;
6863                 }
6864
6865                 /* file start = original start + how far we from the initial position ?  */
6866
6867                 framepos_t file_start = r->start() + pos;
6868
6869                 /* length = next position - current position */
6870
6871                 framepos_t len = (*x) - pos - rstart;
6872
6873                 /* XXX we do we really want to allow even single-sample regions?
6874                  * shouldn't we have some kind of lower limit on region size?
6875                  */
6876
6877                 if (len <= 0) {
6878                         break;
6879                 }
6880
6881                 string new_name;
6882
6883                 if (RegionFactory::region_name (new_name, r->name())) {
6884                         break;
6885                 }
6886
6887                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6888
6889                 PropertyList plist;
6890
6891                 plist.add (ARDOUR::Properties::start, file_start);
6892                 plist.add (ARDOUR::Properties::length, len);
6893                 plist.add (ARDOUR::Properties::name, new_name);
6894                 plist.add (ARDOUR::Properties::layer, 0);
6895                 // TODO set transients_offset
6896
6897                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6898                 /* because we set annouce to false, manually add the new region to the
6899                  * RegionFactory map
6900                  */
6901                 RegionFactory::map_add (nr);
6902
6903                 pl->add_region (nr, rstart + pos);
6904
6905                 if (select_new) {
6906                         new_regions.push_front(nr);
6907                 }
6908
6909                 pos += len;
6910                 ++x;
6911         }
6912
6913         string new_name;
6914
6915         RegionFactory::region_name (new_name, r->name());
6916
6917         /* Add the final region */
6918         PropertyList plist;
6919
6920         plist.add (ARDOUR::Properties::start, r->start() + pos);
6921         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6922         plist.add (ARDOUR::Properties::name, new_name);
6923         plist.add (ARDOUR::Properties::layer, 0);
6924
6925         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6926         /* because we set annouce to false, manually add the new region to the
6927            RegionFactory map
6928         */
6929         RegionFactory::map_add (nr);
6930         pl->add_region (nr, r->position() + pos);
6931
6932         if (select_new) {
6933                 new_regions.push_front(nr);
6934         }
6935
6936         pl->thaw ();
6937
6938         /* We might have removed regions, which alters other regions' layering_index,
6939            so we need to do a recursive diff here.
6940         */
6941         vector<Command*> cmds;
6942         pl->rdiff (cmds);
6943         _session->add_commands (cmds);
6944
6945         _session->add_command (new StatefulDiffCommand (pl));
6946
6947         if (select_new) {
6948
6949                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6950                         set_selected_regionview_from_region_list ((*i), Selection::Add);
6951                 }
6952         }
6953 }
6954
6955 void
6956 Editor::place_transient()
6957 {
6958         if (!_session) {
6959                 return;
6960         }
6961
6962         RegionSelection rs = get_regions_from_selection_and_edit_point ();
6963
6964         if (rs.empty()) {
6965                 return;
6966         }
6967
6968         framepos_t where = get_preferred_edit_position();
6969
6970         begin_reversible_command (_("place transient"));
6971
6972         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6973                 (*r)->region()->add_transient(where);
6974         }
6975
6976         commit_reversible_command ();
6977 }
6978
6979 void
6980 Editor::remove_transient(ArdourCanvas::Item* item)
6981 {
6982         if (!_session) {
6983                 return;
6984         }
6985
6986         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6987         assert (_line);
6988
6989         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6990         _arv->remove_transient (*(float*) _line->get_data ("position"));
6991 }
6992
6993 void
6994 Editor::snap_regions_to_grid ()
6995 {
6996         list <boost::shared_ptr<Playlist > > used_playlists;
6997
6998         RegionSelection rs = get_regions_from_selection_and_entered ();
6999
7000         if (!_session || rs.empty()) {
7001                 return;
7002         }
7003
7004         begin_reversible_command (_("snap regions to grid"));
7005
7006         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7007
7008                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7009
7010                 if (!pl->frozen()) {
7011                         /* we haven't seen this playlist before */
7012
7013                         /* remember used playlists so we can thaw them later */
7014                         used_playlists.push_back(pl);
7015                         pl->freeze();
7016                 }
7017
7018                 framepos_t start_frame = (*r)->region()->first_frame ();
7019                 snap_to (start_frame);
7020                 (*r)->region()->set_position (start_frame);
7021         }
7022
7023         while (used_playlists.size() > 0) {
7024                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7025                 (*i)->thaw();
7026                 used_playlists.pop_front();
7027         }
7028
7029         commit_reversible_command ();
7030 }
7031
7032 void
7033 Editor::close_region_gaps ()
7034 {
7035         list <boost::shared_ptr<Playlist > > used_playlists;
7036
7037         RegionSelection rs = get_regions_from_selection_and_entered ();
7038
7039         if (!_session || rs.empty()) {
7040                 return;
7041         }
7042
7043         Dialog dialog (_("Close Region Gaps"));
7044
7045         Table table (2, 3);
7046         table.set_spacings (12);
7047         table.set_border_width (12);
7048         Label* l = manage (left_aligned_label (_("Crossfade length")));
7049         table.attach (*l, 0, 1, 0, 1);
7050
7051         SpinButton spin_crossfade (1, 0);
7052         spin_crossfade.set_range (0, 15);
7053         spin_crossfade.set_increments (1, 1);
7054         spin_crossfade.set_value (5);
7055         table.attach (spin_crossfade, 1, 2, 0, 1);
7056
7057         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7058
7059         l = manage (left_aligned_label (_("Pull-back length")));
7060         table.attach (*l, 0, 1, 1, 2);
7061
7062         SpinButton spin_pullback (1, 0);
7063         spin_pullback.set_range (0, 100);
7064         spin_pullback.set_increments (1, 1);
7065         spin_pullback.set_value(30);
7066         table.attach (spin_pullback, 1, 2, 1, 2);
7067
7068         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7069
7070         dialog.get_vbox()->pack_start (table);
7071         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7072         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7073         dialog.show_all ();
7074
7075         if (dialog.run () == RESPONSE_CANCEL) {
7076                 return;
7077         }
7078
7079         framepos_t crossfade_len = spin_crossfade.get_value();
7080         framepos_t pull_back_frames = spin_pullback.get_value();
7081
7082         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7083         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7084
7085         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7086
7087         begin_reversible_command (_("close region gaps"));
7088
7089         int idx = 0;
7090         boost::shared_ptr<Region> last_region;
7091
7092         rs.sort_by_position_and_track();
7093
7094         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7095
7096                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7097
7098                 if (!pl->frozen()) {
7099                         /* we haven't seen this playlist before */
7100
7101                         /* remember used playlists so we can thaw them later */
7102                         used_playlists.push_back(pl);
7103                         pl->freeze();
7104                 }
7105
7106                 framepos_t position = (*r)->region()->position();
7107
7108                 if (idx == 0 || position < last_region->position()){
7109                         last_region = (*r)->region();
7110                         idx++;
7111                         continue;
7112                 }
7113
7114                 (*r)->region()->trim_front( (position - pull_back_frames));
7115                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7116
7117                 last_region = (*r)->region();
7118
7119                 idx++;
7120         }
7121
7122         while (used_playlists.size() > 0) {
7123                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7124                 (*i)->thaw();
7125                 used_playlists.pop_front();
7126         }
7127
7128         commit_reversible_command ();
7129 }
7130
7131 void
7132 Editor::tab_to_transient (bool forward)
7133 {
7134         AnalysisFeatureList positions;
7135
7136         RegionSelection rs = get_regions_from_selection_and_entered ();
7137
7138         if (!_session) {
7139                 return;
7140         }
7141
7142         framepos_t pos = _session->audible_frame ();
7143
7144         if (!selection->tracks.empty()) {
7145
7146                 /* don't waste time searching for transients in duplicate playlists.
7147                  */
7148
7149                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7150
7151                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7152
7153                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7154
7155                         if (rtv) {
7156                                 boost::shared_ptr<Track> tr = rtv->track();
7157                                 if (tr) {
7158                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
7159                                         if (pl) {
7160                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7161
7162                                                 if (result >= 0) {
7163                                                         positions.push_back (result);
7164                                                 }
7165                                         }
7166                                 }
7167                         }
7168                 }
7169
7170         } else {
7171
7172                 if (rs.empty()) {
7173                         return;
7174                 }
7175
7176                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7177                         (*r)->region()->get_transients (positions);
7178                 }
7179         }
7180
7181         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7182
7183         if (forward) {
7184                 AnalysisFeatureList::iterator x;
7185
7186                 for (x = positions.begin(); x != positions.end(); ++x) {
7187                         if ((*x) > pos) {
7188                                 break;
7189                         }
7190                 }
7191
7192                 if (x != positions.end ()) {
7193                         _session->request_locate (*x);
7194                 }
7195
7196         } else {
7197                 AnalysisFeatureList::reverse_iterator x;
7198
7199                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7200                         if ((*x) < pos) {
7201                                 break;
7202                         }
7203                 }
7204
7205                 if (x != positions.rend ()) {
7206                         _session->request_locate (*x);
7207                 }
7208         }
7209 }
7210
7211 void
7212 Editor::playhead_forward_to_grid ()
7213 {
7214         if (!_session) {
7215                 return;
7216         }
7217
7218         framepos_t pos = playhead_cursor->current_frame ();
7219         if (pos < max_framepos - 1) {
7220                 pos += 2;
7221                 snap_to_internal (pos, RoundUpAlways, false);
7222                 _session->request_locate (pos);
7223         }
7224 }
7225
7226
7227 void
7228 Editor::playhead_backward_to_grid ()
7229 {
7230         if (!_session) {
7231                 return;
7232         }
7233
7234         framepos_t pos = playhead_cursor->current_frame ();
7235         if (pos > 2) {
7236                 pos -= 2;
7237                 snap_to_internal (pos, RoundDownAlways, false);
7238                 _session->request_locate (pos);
7239         }
7240 }
7241
7242 void
7243 Editor::set_track_height (Height h)
7244 {
7245         TrackSelection& ts (selection->tracks);
7246
7247         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7248                 (*x)->set_height_enum (h);
7249         }
7250 }
7251
7252 void
7253 Editor::toggle_tracks_active ()
7254 {
7255         TrackSelection& ts (selection->tracks);
7256         bool first = true;
7257         bool target = false;
7258
7259         if (ts.empty()) {
7260                 return;
7261         }
7262
7263         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7264                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7265
7266                 if (rtv) {
7267                         if (first) {
7268                                 target = !rtv->_route->active();
7269                                 first = false;
7270                         }
7271                         rtv->_route->set_active (target, this);
7272                 }
7273         }
7274 }
7275
7276 void
7277 Editor::remove_tracks ()
7278 {
7279         /* this will delete GUI objects that may be the subject of an event
7280            handler in which this method is called. Defer actual deletion to the
7281            next idle callback, when all event handling is finished.
7282         */
7283         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7284 }
7285
7286 bool
7287 Editor::idle_remove_tracks ()
7288 {
7289         Session::StateProtector sp (_session);
7290         _remove_tracks ();
7291         return false; /* do not call again */
7292 }
7293
7294 void
7295 Editor::_remove_tracks ()
7296 {
7297         TrackSelection& ts (selection->tracks);
7298
7299         if (ts.empty()) {
7300                 return;
7301         }
7302
7303         vector<string> choices;
7304         string prompt;
7305         int ntracks = 0;
7306         int nbusses = 0;
7307         const char* trackstr;
7308         const char* busstr;
7309         vector<boost::shared_ptr<Route> > routes;
7310         bool special_bus = false;
7311
7312         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7313                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7314                 if (!rtv) {
7315                         continue;
7316                 }
7317                 if (rtv->is_track()) {
7318                         ntracks++;
7319                 } else {
7320                         nbusses++;
7321                 }
7322                 routes.push_back (rtv->_route);
7323
7324                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7325                         special_bus = true;
7326                 }
7327         }
7328
7329         if (special_bus && !Config->get_allow_special_bus_removal()) {
7330                 MessageDialog msg (_("That would be bad news ...."),
7331                                    false,
7332                                    Gtk::MESSAGE_INFO,
7333                                    Gtk::BUTTONS_OK);
7334                 msg.set_secondary_text (string_compose (_(
7335                                                                 "Removing the master or monitor bus is such a bad idea\n\
7336 that %1 is not going to allow it.\n\
7337 \n\
7338 If you really want to do this sort of thing\n\
7339 edit your ardour.rc file to set the\n\
7340 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7341
7342                 msg.present ();
7343                 msg.run ();
7344                 return;
7345         }
7346
7347         if (ntracks + nbusses == 0) {
7348                 return;
7349         }
7350
7351         trackstr = P_("track", "tracks", ntracks);
7352         busstr = P_("bus", "busses", nbusses);
7353
7354         if (ntracks) {
7355                 if (nbusses) {
7356                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7357                                                     "(You may also lose the playlists associated with the %2)\n\n"
7358                                                     "This action cannot be undone, and the session file will be overwritten!"),
7359                                                   ntracks, trackstr, nbusses, busstr);
7360                 } else {
7361                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7362                                                     "(You may also lose the playlists associated with the %2)\n\n"
7363                                                     "This action cannot be undone, and the session file will be overwritten!"),
7364                                                   ntracks, trackstr);
7365                 }
7366         } else if (nbusses) {
7367                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7368                                             "This action cannot be undone, and the session file will be overwritten"),
7369                                           nbusses, busstr);
7370         }
7371
7372         choices.push_back (_("No, do nothing."));
7373         if (ntracks + nbusses > 1) {
7374                 choices.push_back (_("Yes, remove them."));
7375         } else {
7376                 choices.push_back (_("Yes, remove it."));
7377         }
7378
7379         string title;
7380         if (ntracks) {
7381                 title = string_compose (_("Remove %1"), trackstr);
7382         } else {
7383                 title = string_compose (_("Remove %1"), busstr);
7384         }
7385
7386         Choice prompter (title, prompt, choices);
7387
7388         if (prompter.run () != 1) {
7389                 return;
7390         }
7391
7392         if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7393                 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7394                  * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7395                  * likely because deletion requires selection) this will call
7396                  * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7397                  * It's likewise likely that the route that has just been displayed in the
7398                  * Editor-Mixer will be next in line for deletion.
7399                  *
7400                  * So simply switch to the master-bus (if present)
7401                  */
7402                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7403                         if ((*i)->stripable ()->is_master ()) {
7404                                 set_selected_mixer_strip (*(*i));
7405                                 break;
7406                         }
7407                 }
7408         }
7409
7410         Mixer_UI::instance()->selection().block_routes_changed (true);
7411         selection->block_tracks_changed (true);
7412         {
7413                 DisplaySuspender ds;
7414                 boost::shared_ptr<RouteList> rl (new RouteList);
7415                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7416                         rl->push_back (*x);
7417                 }
7418                 _session->remove_routes (rl);
7419         }
7420         /* TrackSelection and RouteList leave scope,
7421          * destructors are called,
7422          * diskstream drops references, save_state is called (again for every track)
7423          */
7424         selection->block_tracks_changed (false);
7425         Mixer_UI::instance()->selection().block_routes_changed (false);
7426         selection->TracksChanged (); /* EMIT SIGNAL */
7427 }
7428
7429 void
7430 Editor::do_insert_time ()
7431 {
7432         if (selection->tracks.empty()) {
7433                 return;
7434         }
7435
7436         InsertRemoveTimeDialog d (*this);
7437         int response = d.run ();
7438
7439         if (response != RESPONSE_OK) {
7440                 return;
7441         }
7442
7443         if (d.distance() == 0) {
7444                 return;
7445         }
7446
7447         insert_time (
7448                 d.position(),
7449                 d.distance(),
7450                 d.intersected_region_action (),
7451                 d.all_playlists(),
7452                 d.move_glued(),
7453                 d.move_markers(),
7454                 d.move_glued_markers(),
7455                 d.move_locked_markers(),
7456                 d.move_tempos()
7457                 );
7458 }
7459
7460 void
7461 Editor::insert_time (
7462         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7463         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7464         )
7465 {
7466
7467         if (Config->get_edit_mode() == Lock) {
7468                 return;
7469         }
7470         bool in_command = false;
7471
7472         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7473
7474         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7475
7476                 /* regions */
7477
7478                 /* don't operate on any playlist more than once, which could
7479                  * happen if "all playlists" is enabled, but there is more
7480                  * than 1 track using playlists "from" a given track.
7481                  */
7482
7483                 set<boost::shared_ptr<Playlist> > pl;
7484
7485                 if (all_playlists) {
7486                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7487                         if (rtav && rtav->track ()) {
7488                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7489                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7490                                         pl.insert (*p);
7491                                 }
7492                         }
7493                 } else {
7494                         if ((*x)->playlist ()) {
7495                                 pl.insert ((*x)->playlist ());
7496                         }
7497                 }
7498
7499                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7500
7501                         (*i)->clear_changes ();
7502                         (*i)->clear_owned_changes ();
7503
7504                         if (opt == SplitIntersected) {
7505                                 /* non musical split */
7506                                 (*i)->split (pos, 0);
7507                         }
7508
7509                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7510
7511                         if (!in_command) {
7512                                 begin_reversible_command (_("insert time"));
7513                                 in_command = true;
7514                         }
7515                         vector<Command*> cmds;
7516                         (*i)->rdiff (cmds);
7517                         _session->add_commands (cmds);
7518
7519                         _session->add_command (new StatefulDiffCommand (*i));
7520                 }
7521
7522                 /* automation */
7523                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7524                 if (rtav) {
7525                         if (!in_command) {
7526                                 begin_reversible_command (_("insert time"));
7527                                 in_command = true;
7528                         }
7529                         rtav->route ()->shift (pos, frames);
7530                 }
7531         }
7532
7533         /* markers */
7534         if (markers_too) {
7535                 bool moved = false;
7536                 XMLNode& before (_session->locations()->get_state());
7537                 Locations::LocationList copy (_session->locations()->list());
7538
7539                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7540
7541                         Locations::LocationList::const_iterator tmp;
7542
7543                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7544                                 bool const was_locked = (*i)->locked ();
7545                                 if (locked_markers_too) {
7546                                         (*i)->unlock ();
7547                                 }
7548
7549                                 if ((*i)->start() >= pos) {
7550                                         // move end first, in case we're moving by more than the length of the range
7551                                         if (!(*i)->is_mark()) {
7552                                                 (*i)->set_end ((*i)->end() + frames);
7553                                         }
7554                                         (*i)->set_start ((*i)->start() + frames);
7555                                         moved = true;
7556                                 }
7557
7558                                 if (was_locked) {
7559                                         (*i)->lock ();
7560                                 }
7561                         }
7562                 }
7563
7564                 if (moved) {
7565                         if (!in_command) {
7566                                 begin_reversible_command (_("insert time"));
7567                                 in_command = true;
7568                         }
7569                         XMLNode& after (_session->locations()->get_state());
7570                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7571                 }
7572         }
7573
7574         if (tempo_too) {
7575                 if (!in_command) {
7576                         begin_reversible_command (_("insert time"));
7577                         in_command = true;
7578                 }
7579                 XMLNode& before (_session->tempo_map().get_state());
7580                 _session->tempo_map().insert_time (pos, frames);
7581                 XMLNode& after (_session->tempo_map().get_state());
7582                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7583         }
7584
7585         if (in_command) {
7586                 commit_reversible_command ();
7587         }
7588 }
7589
7590 void
7591 Editor::do_remove_time ()
7592 {
7593         if (selection->tracks.empty()) {
7594                 return;
7595         }
7596
7597         InsertRemoveTimeDialog d (*this, true);
7598
7599         int response = d.run ();
7600
7601         if (response != RESPONSE_OK) {
7602                 return;
7603         }
7604
7605         framecnt_t distance = d.distance();
7606
7607         if (distance == 0) {
7608                 return;
7609         }
7610
7611         remove_time (
7612                 d.position(),
7613                 distance,
7614                 SplitIntersected,
7615                 d.move_glued(),
7616                 d.move_markers(),
7617                 d.move_glued_markers(),
7618                 d.move_locked_markers(),
7619                 d.move_tempos()
7620         );
7621 }
7622
7623 void
7624 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7625                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7626 {
7627         if (Config->get_edit_mode() == Lock) {
7628                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7629                 return;
7630         }
7631         bool in_command = false;
7632
7633         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7634                 /* regions */
7635                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7636
7637                 if (pl) {
7638
7639                         XMLNode &before = pl->get_state();
7640
7641                         std::list<AudioRange> rl;
7642                         AudioRange ar(pos, pos+frames, 0);
7643                         rl.push_back(ar);
7644                         pl->cut (rl);
7645                         pl->shift (pos, -frames, true, ignore_music_glue);
7646
7647                         if (!in_command) {
7648                                 begin_reversible_command (_("remove time"));
7649                                 in_command = true;
7650                         }
7651                         XMLNode &after = pl->get_state();
7652
7653                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7654                 }
7655
7656                 /* automation */
7657                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7658                 if (rtav) {
7659                         if (!in_command) {
7660                                 begin_reversible_command (_("remove time"));
7661                                 in_command = true;
7662                         }
7663                         rtav->route ()->shift (pos, -frames);
7664                 }
7665         }
7666
7667         std::list<Location*> loc_kill_list;
7668
7669         /* markers */
7670         if (markers_too) {
7671                 bool moved = false;
7672                 XMLNode& before (_session->locations()->get_state());
7673                 Locations::LocationList copy (_session->locations()->list());
7674
7675                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7676                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7677
7678                                 bool const was_locked = (*i)->locked ();
7679                                 if (locked_markers_too) {
7680                                         (*i)->unlock ();
7681                                 }
7682
7683                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7684                                         if ((*i)->end() >= pos
7685                                         && (*i)->end() < pos+frames
7686                                         && (*i)->start() >= pos
7687                                         && (*i)->end() < pos+frames) {  // range is completely enclosed;  kill it
7688                                                 moved = true;
7689                                                 loc_kill_list.push_back(*i);
7690                                         } else {  // only start or end is included, try to do the right thing
7691                                                 // move start before moving end, to avoid trying to move the end to before the start
7692                                                 // if we're removing more time than the length of the range
7693                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7694                                                         // start is within cut
7695                                                         (*i)->set_start (pos);  // bring the start marker to the beginning of the cut
7696                                                         moved = true;
7697                                                 } else if ((*i)->start() >= pos+frames) {
7698                                                         // start (and thus entire range) lies beyond end of cut
7699                                                         (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7700                                                         moved = true;
7701                                                 }
7702                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7703                                                         // end is inside cut
7704                                                         (*i)->set_end (pos);  // bring the end to the cut
7705                                                         moved = true;
7706                                                 } else if ((*i)->end() >= pos+frames) {
7707                                                         // end is beyond end of cut
7708                                                         (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7709                                                         moved = true;
7710                                                 }
7711
7712                                         }
7713                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7714                                         loc_kill_list.push_back(*i);
7715                                         moved = true;
7716                                 } else if ((*i)->start() >= pos) {
7717                                         (*i)->set_start ((*i)->start() -frames);
7718                                         moved = true;
7719                                 }
7720
7721                                 if (was_locked) {
7722                                         (*i)->lock ();
7723                                 }
7724                         }
7725                 }
7726
7727                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7728                         _session->locations()->remove( *i );
7729                 }
7730
7731                 if (moved) {
7732                         if (!in_command) {
7733                                 begin_reversible_command (_("remove time"));
7734                                 in_command = true;
7735                         }
7736                         XMLNode& after (_session->locations()->get_state());
7737                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7738                 }
7739         }
7740
7741         if (tempo_too) {
7742                 XMLNode& before (_session->tempo_map().get_state());
7743
7744                 if (_session->tempo_map().remove_time (pos, frames) ) {
7745                         if (!in_command) {
7746                                 begin_reversible_command (_("remove time"));
7747                                 in_command = true;
7748                         }
7749                         XMLNode& after (_session->tempo_map().get_state());
7750                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7751                 }
7752         }
7753
7754         if (in_command) {
7755                 commit_reversible_command ();
7756         }
7757 }
7758
7759 void
7760 Editor::fit_selection ()
7761 {
7762         if (!selection->tracks.empty()) {
7763                 fit_tracks (selection->tracks);
7764         } else {
7765                 TrackViewList tvl;
7766
7767                 /* no selected tracks - use tracks with selected regions */
7768
7769                 if (!selection->regions.empty()) {
7770                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7771                                 tvl.push_back (&(*r)->get_time_axis_view ());
7772                         }
7773
7774                         if (!tvl.empty()) {
7775                                 fit_tracks (tvl);
7776                         }
7777                 } else if (internal_editing()) {
7778                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7779                            the entered track
7780                         */
7781                         if (entered_track) {
7782                                 tvl.push_back (entered_track);
7783                                 fit_tracks (tvl);
7784                         }
7785                 }
7786         }
7787
7788 }
7789
7790 void
7791 Editor::fit_tracks (TrackViewList & tracks)
7792 {
7793         if (tracks.empty()) {
7794                 return;
7795         }
7796
7797         uint32_t child_heights = 0;
7798         int visible_tracks = 0;
7799
7800         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7801
7802                 if (!(*t)->marked_for_display()) {
7803                         continue;
7804                 }
7805
7806                 child_heights += (*t)->effective_height() - (*t)->current_height();
7807                 ++visible_tracks;
7808         }
7809
7810         /* compute the per-track height from:
7811
7812            total canvas visible height -
7813                  height that will be taken by visible children of selected
7814                  tracks - height of the ruler/hscroll area
7815         */
7816         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7817         double first_y_pos = DBL_MAX;
7818
7819         if (h < TimeAxisView::preset_height (HeightSmall)) {
7820                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7821                 /* too small to be displayed */
7822                 return;
7823         }
7824
7825         undo_visual_stack.push_back (current_visual_state (true));
7826         PBD::Unwinder<bool> nsv (no_save_visual, true);
7827
7828         /* build a list of all tracks, including children */
7829
7830         TrackViewList all;
7831         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7832                 all.push_back (*i);
7833                 TimeAxisView::Children c = (*i)->get_child_list ();
7834                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7835                         all.push_back (j->get());
7836                 }
7837         }
7838
7839
7840         // find selection range.
7841         // if someone knows how to user TrackViewList::iterator for this
7842         // I'm all ears.
7843         int selected_top = -1;
7844         int selected_bottom = -1;
7845         int i = 0;
7846         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7847                 if ((*t)->marked_for_display ()) {
7848                         if (tracks.contains(*t)) {
7849                                 if (selected_top == -1) {
7850                                         selected_top = i;
7851                                 }
7852                                 selected_bottom = i;
7853                         }
7854                 }
7855         }
7856
7857         i = 0;
7858         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7859                 if ((*t)->marked_for_display ()) {
7860                         if (tracks.contains(*t)) {
7861                                 (*t)->set_height (h);
7862                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7863                         } else {
7864                                 if (i > selected_top && i < selected_bottom) {
7865                                         hide_track_in_display (*t);
7866                                 }
7867                         }
7868                 }
7869         }
7870
7871         /*
7872            set the controls_layout height now, because waiting for its size
7873            request signal handler will cause the vertical adjustment setting to fail
7874         */
7875
7876         controls_layout.property_height () = _full_canvas_height;
7877         vertical_adjustment.set_value (first_y_pos);
7878
7879         redo_visual_stack.push_back (current_visual_state (true));
7880
7881         visible_tracks_selector.set_text (_("Sel"));
7882 }
7883
7884 void
7885 Editor::save_visual_state (uint32_t n)
7886 {
7887         while (visual_states.size() <= n) {
7888                 visual_states.push_back (0);
7889         }
7890
7891         if (visual_states[n] != 0) {
7892                 delete visual_states[n];
7893         }
7894
7895         visual_states[n] = current_visual_state (true);
7896         gdk_beep ();
7897 }
7898
7899 void
7900 Editor::goto_visual_state (uint32_t n)
7901 {
7902         if (visual_states.size() <= n) {
7903                 return;
7904         }
7905
7906         if (visual_states[n] == 0) {
7907                 return;
7908         }
7909
7910         use_visual_state (*visual_states[n]);
7911 }
7912
7913 void
7914 Editor::start_visual_state_op (uint32_t n)
7915 {
7916         save_visual_state (n);
7917
7918         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7919         char buf[32];
7920         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7921         pup->set_text (buf);
7922         pup->touch();
7923 }
7924
7925 void
7926 Editor::cancel_visual_state_op (uint32_t n)
7927 {
7928         goto_visual_state (n);
7929 }
7930
7931 void
7932 Editor::toggle_region_mute ()
7933 {
7934         if (_ignore_region_action) {
7935                 return;
7936         }
7937
7938         RegionSelection rs = get_regions_from_selection_and_entered ();
7939
7940         if (rs.empty ()) {
7941                 return;
7942         }
7943
7944         if (rs.size() > 1) {
7945                 begin_reversible_command (_("mute regions"));
7946         } else {
7947                 begin_reversible_command (_("mute region"));
7948         }
7949
7950         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7951
7952                 (*i)->region()->playlist()->clear_changes ();
7953                 (*i)->region()->set_muted (!(*i)->region()->muted ());
7954                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7955
7956         }
7957
7958         commit_reversible_command ();
7959 }
7960
7961 void
7962 Editor::combine_regions ()
7963 {
7964         /* foreach track with selected regions, take all selected regions
7965            and join them into a new region containing the subregions (as a
7966            playlist)
7967         */
7968
7969         typedef set<RouteTimeAxisView*> RTVS;
7970         RTVS tracks;
7971
7972         if (selection->regions.empty()) {
7973                 return;
7974         }
7975
7976         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7977                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7978
7979                 if (rtv) {
7980                         tracks.insert (rtv);
7981                 }
7982         }
7983
7984         begin_reversible_command (_("combine regions"));
7985
7986         vector<RegionView*> new_selection;
7987
7988         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7989                 RegionView* rv;
7990
7991                 if ((rv = (*i)->combine_regions ()) != 0) {
7992                         new_selection.push_back (rv);
7993                 }
7994         }
7995
7996         selection->clear_regions ();
7997         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7998                 selection->add (*i);
7999         }
8000
8001         commit_reversible_command ();
8002 }
8003
8004 void
8005 Editor::uncombine_regions ()
8006 {
8007         typedef set<RouteTimeAxisView*> RTVS;
8008         RTVS tracks;
8009
8010         if (selection->regions.empty()) {
8011                 return;
8012         }
8013
8014         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8015                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8016
8017                 if (rtv) {
8018                         tracks.insert (rtv);
8019                 }
8020         }
8021
8022         begin_reversible_command (_("uncombine regions"));
8023
8024         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8025                 (*i)->uncombine_regions ();
8026         }
8027
8028         commit_reversible_command ();
8029 }
8030
8031 void
8032 Editor::toggle_midi_input_active (bool flip_others)
8033 {
8034         bool onoff = false;
8035         boost::shared_ptr<RouteList> rl (new RouteList);
8036
8037         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8038                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8039
8040                 if (!rtav) {
8041                         continue;
8042                 }
8043
8044                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8045
8046                 if (mt) {
8047                         rl->push_back (rtav->route());
8048                         onoff = !mt->input_active();
8049                 }
8050         }
8051
8052         _session->set_exclusive_input_active (rl, onoff, flip_others);
8053 }
8054
8055 static bool ok_fine (GdkEventAny*) { return true; }
8056
8057 void
8058 Editor::lock ()
8059 {
8060         if (!lock_dialog) {
8061                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8062
8063                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8064                 lock_dialog->get_vbox()->pack_start (*padlock);
8065                 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8066
8067                 ArdourButton* b = manage (new ArdourButton);
8068                 b->set_name ("lock button");
8069                 b->set_text (_("Click to unlock"));
8070                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8071                 lock_dialog->get_vbox()->pack_start (*b);
8072
8073                 lock_dialog->get_vbox()->show_all ();
8074                 lock_dialog->set_size_request (200, 200);
8075         }
8076
8077         delete _main_menu_disabler;
8078         _main_menu_disabler = new MainMenuDisabler;
8079
8080         lock_dialog->present ();
8081
8082         lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8083 }
8084
8085 void
8086 Editor::unlock ()
8087 {
8088         lock_dialog->hide ();
8089
8090         delete _main_menu_disabler;
8091         _main_menu_disabler = 0;
8092
8093         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8094                 start_lock_event_timing ();
8095         }
8096 }
8097
8098 void
8099 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8100 {
8101         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8102 }
8103
8104 void
8105 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8106 {
8107         Timers::TimerSuspender t;
8108         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8109         Gtkmm2ext::UI::instance()->flush_pending (1);
8110 }
8111
8112 void
8113 Editor::bring_all_sources_into_session ()
8114 {
8115         if (!_session) {
8116                 return;
8117         }
8118
8119         Gtk::Label msg;
8120         ArdourDialog w (_("Moving embedded files into session folder"));
8121         w.get_vbox()->pack_start (msg);
8122         w.present ();
8123
8124         /* flush all pending GUI events because we're about to start copying
8125          * files
8126          */
8127
8128         Timers::TimerSuspender t;
8129         Gtkmm2ext::UI::instance()->flush_pending (3);
8130
8131         cerr << " Do it\n";
8132
8133         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8134 }