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