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