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