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