Rework region selection XML
[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                         playlist->clear_changes ();
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                 begin_reversible_command (_("Crop Regions to Time Selection"));
3373                 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3374                         crop_region_to ((*i).start, (*i).end);
3375                 }
3376                 commit_reversible_command();
3377         } else {
3378
3379                 framepos_t start;
3380                 framepos_t end;
3381
3382                 if (get_edit_op_range (start, end)) {
3383                         begin_reversible_command (_("Crop Regions to Edit Range"));
3384
3385                         crop_region_to (start, end);
3386
3387                         commit_reversible_command();
3388                 }
3389         }
3390
3391 }
3392
3393 void
3394 Editor::crop_region_to (framepos_t start, framepos_t end)
3395 {
3396         vector<boost::shared_ptr<Playlist> > playlists;
3397         boost::shared_ptr<Playlist> playlist;
3398         TrackViewList ts;
3399
3400         if (selection->tracks.empty()) {
3401                 ts = track_views.filter_to_unique_playlists();
3402         } else {
3403                 ts = selection->tracks.filter_to_unique_playlists ();
3404         }
3405
3406         sort_track_selection (ts);
3407
3408         for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3409
3410                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3411
3412                 if (!rtv) {
3413                         continue;
3414                 }
3415
3416                 boost::shared_ptr<Track> t = rtv->track();
3417
3418                 if (t != 0 && ! t->destructive()) {
3419
3420                         if ((playlist = rtv->playlist()) != 0) {
3421                                 playlists.push_back (playlist);
3422                         }
3423                 }
3424         }
3425
3426         if (playlists.empty()) {
3427                 return;
3428         }
3429
3430         framepos_t pos;
3431         framepos_t new_start;
3432         framepos_t new_end;
3433         framecnt_t new_length;
3434
3435         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3436
3437                 /* Only the top regions at start and end have to be cropped */
3438                 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3439                 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3440
3441                 vector<boost::shared_ptr<Region> > regions;
3442
3443                 if (region_at_start != 0) {
3444                         regions.push_back (region_at_start);
3445                 }
3446                 if (region_at_end != 0) {
3447                         regions.push_back (region_at_end);
3448                 }
3449
3450                 /* now adjust lengths */
3451                 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3452
3453                         pos = (*i)->position();
3454                         new_start = max (start, pos);
3455                         if (max_framepos - pos > (*i)->length()) {
3456                                 new_end = pos + (*i)->length() - 1;
3457                         } else {
3458                                 new_end = max_framepos;
3459                         }
3460                         new_end = min (end, new_end);
3461                         new_length = new_end - new_start + 1;
3462
3463                         (*i)->clear_changes ();
3464                         (*i)->trim_to (new_start, new_length);
3465                         _session->add_command (new StatefulDiffCommand (*i));
3466                 }
3467         }
3468 }
3469
3470 void
3471 Editor::region_fill_track ()
3472 {
3473         boost::shared_ptr<Playlist> playlist;
3474         RegionSelection regions = get_regions_from_selection_and_entered ();
3475         RegionSelection foo;
3476
3477         framepos_t const end = _session->current_end_frame ();
3478
3479         if (regions.empty () || regions.end_frame () + 1 >= end) {
3480                 return;
3481         }
3482
3483         framepos_t const start_frame = regions.start ();
3484         framepos_t const end_frame = regions.end_frame ();
3485         framecnt_t const gap = end_frame - start_frame + 1;
3486
3487         begin_reversible_command (Operations::region_fill);
3488
3489         selection->clear_regions ();
3490
3491         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3492
3493                 boost::shared_ptr<Region> r ((*i)->region());
3494
3495                 TimeAxisView& tv = (*i)->get_time_axis_view();
3496                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3497                 latest_regionviews.clear ();
3498                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3499
3500                 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3501                 playlist = (*i)->region()->playlist();
3502                 playlist->clear_changes ();
3503                 playlist->duplicate_until (r, position, gap, end);
3504                 _session->add_command(new StatefulDiffCommand (playlist));
3505
3506                 c.disconnect ();
3507
3508                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3509         }
3510
3511         if (!foo.empty()) {
3512                 selection->set (foo);
3513         }
3514
3515         commit_reversible_command ();
3516 }
3517
3518 void
3519 Editor::set_region_sync_position ()
3520 {
3521         set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3522 }
3523
3524 void
3525 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3526 {
3527         bool in_command = false;
3528
3529         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3530
3531                 if (!(*r)->region()->covers (where)) {
3532                         continue;
3533                 }
3534
3535                 boost::shared_ptr<Region> region ((*r)->region());
3536
3537                 if (!in_command) {
3538                         begin_reversible_command (_("set sync point"));
3539                         in_command = true;
3540                 }
3541
3542                 region->clear_changes ();
3543                 region->set_sync_position (where);
3544                 _session->add_command(new StatefulDiffCommand (region));
3545         }
3546
3547         if (in_command) {
3548                 commit_reversible_command ();
3549         }
3550 }
3551
3552 /** Remove the sync positions of the selection */
3553 void
3554 Editor::remove_region_sync ()
3555 {
3556         RegionSelection rs = get_regions_from_selection_and_entered ();
3557
3558         if (rs.empty()) {
3559                 return;
3560         }
3561
3562         begin_reversible_command (_("remove region sync"));
3563
3564         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3565
3566                 (*i)->region()->clear_changes ();
3567                 (*i)->region()->clear_sync_position ();
3568                 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3569         }
3570
3571         commit_reversible_command ();
3572 }
3573
3574 void
3575 Editor::naturalize_region ()
3576 {
3577         RegionSelection rs = get_regions_from_selection_and_entered ();
3578
3579         if (rs.empty()) {
3580                 return;
3581         }
3582
3583         if (rs.size() > 1) {
3584                 begin_reversible_command (_("move regions to original position"));
3585         } else {
3586                 begin_reversible_command (_("move region to original position"));
3587         }
3588
3589         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3590                 (*i)->region()->clear_changes ();
3591                 (*i)->region()->move_to_natural_position ();
3592                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3593         }
3594
3595         commit_reversible_command ();
3596 }
3597
3598 void
3599 Editor::align_regions (RegionPoint what)
3600 {
3601         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3602
3603         if (rs.empty()) {
3604                 return;
3605         }
3606
3607         begin_reversible_command (_("align selection"));
3608
3609         framepos_t const position = get_preferred_edit_position ();
3610
3611         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3612                 align_region_internal ((*i)->region(), what, position);
3613         }
3614
3615         commit_reversible_command ();
3616 }
3617
3618 struct RegionSortByTime {
3619         bool operator() (const RegionView* a, const RegionView* b) {
3620                 return a->region()->position() < b->region()->position();
3621         }
3622 };
3623
3624 void
3625 Editor::align_regions_relative (RegionPoint point)
3626 {
3627         RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3628
3629         if (rs.empty()) {
3630                 return;
3631         }
3632
3633         framepos_t const position = get_preferred_edit_position ();
3634
3635         framepos_t distance = 0;
3636         framepos_t pos = 0;
3637         int dir = 1;
3638
3639         list<RegionView*> sorted;
3640         rs.by_position (sorted);
3641
3642         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3643
3644         switch (point) {
3645         case Start:
3646                 pos = position;
3647                 if (position > r->position()) {
3648                         distance = position - r->position();
3649                 } else {
3650                         distance = r->position() - position;
3651                         dir = -1;
3652                 }
3653                 break;
3654
3655         case End:
3656                 if (position > r->last_frame()) {
3657                         distance = position - r->last_frame();
3658                         pos = r->position() + distance;
3659                 } else {
3660                         distance = r->last_frame() - position;
3661                         pos = r->position() - distance;
3662                         dir = -1;
3663                 }
3664                 break;
3665
3666         case SyncPoint:
3667                 pos = r->adjust_to_sync (position);
3668                 if (pos > r->position()) {
3669                         distance = pos - r->position();
3670                 } else {
3671                         distance = r->position() - pos;
3672                         dir = -1;
3673                 }
3674                 break;
3675         }
3676
3677         if (pos == r->position()) {
3678                 return;
3679         }
3680
3681         begin_reversible_command (_("align selection (relative)"));
3682
3683         /* move first one specially */
3684
3685         r->clear_changes ();
3686         r->set_position (pos);
3687         _session->add_command(new StatefulDiffCommand (r));
3688
3689         /* move rest by the same amount */
3690
3691         sorted.pop_front();
3692
3693         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3694
3695                 boost::shared_ptr<Region> region ((*i)->region());
3696
3697                 region->clear_changes ();
3698
3699                 if (dir > 0) {
3700                         region->set_position (region->position() + distance);
3701                 } else {
3702                         region->set_position (region->position() - distance);
3703                 }
3704
3705                 _session->add_command(new StatefulDiffCommand (region));
3706
3707         }
3708
3709         commit_reversible_command ();
3710 }
3711
3712 void
3713 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3714 {
3715         begin_reversible_command (_("align region"));
3716         align_region_internal (region, point, position);
3717         commit_reversible_command ();
3718 }
3719
3720 void
3721 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3722 {
3723         region->clear_changes ();
3724
3725         switch (point) {
3726         case SyncPoint:
3727                 region->set_position (region->adjust_to_sync (position));
3728                 break;
3729
3730         case End:
3731                 if (position > region->length()) {
3732                         region->set_position (position - region->length());
3733                 }
3734                 break;
3735
3736         case Start:
3737                 region->set_position (position);
3738                 break;
3739         }
3740
3741         _session->add_command(new StatefulDiffCommand (region));
3742 }
3743
3744 void
3745 Editor::trim_region_front ()
3746 {
3747         trim_region (true);
3748 }
3749
3750 void
3751 Editor::trim_region_back ()
3752 {
3753         trim_region (false);
3754 }
3755
3756 void
3757 Editor::trim_region (bool front)
3758 {
3759         framepos_t where = get_preferred_edit_position();
3760         RegionSelection rs = get_regions_from_selection_and_edit_point ();
3761
3762         if (rs.empty()) {
3763                 return;
3764         }
3765
3766         begin_reversible_command (front ? _("trim front") : _("trim back"));
3767
3768         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3769                 if (!(*i)->region()->locked()) {
3770
3771                         (*i)->region()->clear_changes ();
3772
3773                         if (front) {
3774                                 (*i)->region()->trim_front (where);
3775                         } else {
3776                                 (*i)->region()->trim_end (where);
3777                         }
3778
3779                         _session->add_command (new StatefulDiffCommand ((*i)->region()));
3780                 }
3781         }
3782
3783         commit_reversible_command ();
3784 }
3785
3786 /** Trim the end of the selected regions to the position of the edit cursor */
3787 void
3788 Editor::trim_region_to_loop ()
3789 {
3790         Location* loc = _session->locations()->auto_loop_location();
3791         if (!loc) {
3792                 return;
3793         }
3794         trim_region_to_location (*loc, _("trim to loop"));
3795 }
3796
3797 void
3798 Editor::trim_region_to_punch ()
3799 {
3800         Location* loc = _session->locations()->auto_punch_location();
3801         if (!loc) {
3802                 return;
3803         }
3804         trim_region_to_location (*loc, _("trim to punch"));
3805 }
3806
3807 void
3808 Editor::trim_region_to_location (const Location& loc, const char* str)
3809 {
3810         RegionSelection rs = get_regions_from_selection_and_entered ();
3811         bool in_command = false;
3812
3813         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3814                 RegionView* rv = (*x);
3815
3816                 /* require region to span proposed trim */
3817                 switch (rv->region()->coverage (loc.start(), loc.end())) {
3818                 case Evoral::OverlapInternal:
3819                         break;
3820                 default:
3821                         continue;
3822                 }
3823
3824                 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3825                 if (!tav) {
3826                         return;
3827                 }
3828
3829                 float speed = 1.0;
3830                 framepos_t start;
3831                 framepos_t end;
3832
3833                 if (tav->track() != 0) {
3834                         speed = tav->track()->speed();
3835                 }
3836
3837                 start = session_frame_to_track_frame (loc.start(), speed);
3838                 end = session_frame_to_track_frame (loc.end(), speed);
3839
3840                 rv->region()->clear_changes ();
3841                 rv->region()->trim_to (start, (end - start));
3842
3843                 if (!in_command) {
3844                         begin_reversible_command (str);
3845                         in_command = true;
3846                 }
3847                 _session->add_command(new StatefulDiffCommand (rv->region()));
3848         }
3849
3850         if (in_command) {
3851                 commit_reversible_command ();
3852         }
3853 }
3854
3855 void
3856 Editor::trim_region_to_previous_region_end ()
3857 {
3858         return trim_to_region(false);
3859 }
3860
3861 void
3862 Editor::trim_region_to_next_region_start ()
3863 {
3864         return trim_to_region(true);
3865 }
3866
3867 void
3868 Editor::trim_to_region(bool forward)
3869 {
3870         RegionSelection rs = get_regions_from_selection_and_entered ();
3871         bool in_command = false;
3872
3873         boost::shared_ptr<Region> next_region;
3874
3875         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3876
3877                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3878
3879                 if (!arv) {
3880                         continue;
3881                 }
3882
3883                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3884
3885                 if (!atav) {
3886                         continue;
3887                 }
3888
3889                 float speed = 1.0;
3890
3891                 if (atav->track() != 0) {
3892                         speed = atav->track()->speed();
3893                 }
3894
3895
3896                 boost::shared_ptr<Region> region = arv->region();
3897                 boost::shared_ptr<Playlist> playlist (region->playlist());
3898
3899                 region->clear_changes ();
3900
3901                 if (forward) {
3902
3903                         next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3904
3905                         if (!next_region) {
3906                                 continue;
3907                         }
3908
3909                         region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3910                         arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3911                 }
3912                 else {
3913
3914                         next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3915
3916                         if(!next_region){
3917                                 continue;
3918                         }
3919
3920                         region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3921
3922                         arv->region_changed (ARDOUR::bounds_change);
3923                 }
3924
3925                 if (!in_command) {
3926                         begin_reversible_command (_("trim to region"));
3927                         in_command = true;
3928                 }
3929                 _session->add_command(new StatefulDiffCommand (region));
3930         }
3931
3932         if (in_command) {
3933                 commit_reversible_command ();
3934         }
3935 }
3936
3937 void
3938 Editor::unfreeze_route ()
3939 {
3940         if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3941                 return;
3942         }
3943
3944         clicked_routeview->track()->unfreeze ();
3945 }
3946
3947 void*
3948 Editor::_freeze_thread (void* arg)
3949 {
3950         return static_cast<Editor*>(arg)->freeze_thread ();
3951 }
3952
3953 void*
3954 Editor::freeze_thread ()
3955 {
3956         /* create event pool because we may need to talk to the session */
3957         SessionEvent::create_per_thread_pool ("freeze events", 64);
3958         /* create per-thread buffers for process() tree to use */
3959         clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3960         current_interthread_info->done = true;
3961         return 0;
3962 }
3963
3964 void
3965 Editor::freeze_route ()
3966 {
3967         if (!_session) {
3968                 return;
3969         }
3970
3971         /* stop transport before we start. this is important */
3972
3973         _session->request_transport_speed (0.0);
3974
3975         /* wait for just a little while, because the above call is asynchronous */
3976
3977         Glib::usleep (250000);
3978
3979         if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3980                 return;
3981         }
3982
3983         if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3984                 MessageDialog d (
3985                         _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3986                           "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3987                         );
3988                 d.set_title (_("Cannot freeze"));
3989                 d.run ();
3990                 return;
3991         }
3992
3993         if (clicked_routeview->track()->has_external_redirects()) {
3994                 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"
3995                                                    "Freezing will only process the signal as far as the first send/insert/return."),
3996                                                  clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3997
3998                 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3999                 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4000                 d.set_title (_("Freeze Limits"));
4001
4002                 int response = d.run ();
4003
4004                 switch (response) {
4005                 case Gtk::RESPONSE_CANCEL:
4006                         return;
4007                 default:
4008                         break;
4009                 }
4010         }
4011
4012         InterThreadInfo itt;
4013         current_interthread_info = &itt;
4014
4015         InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4016
4017         pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4018
4019         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4020
4021         while (!itt.done && !itt.cancel) {
4022                 gtk_main_iteration ();
4023         }
4024
4025         pthread_join (itt.thread, 0);
4026         current_interthread_info = 0;
4027 }
4028
4029 void
4030 Editor::bounce_range_selection (bool replace, bool enable_processing)
4031 {
4032         if (selection->time.empty()) {
4033                 return;
4034         }
4035
4036         TrackSelection views = selection->tracks;
4037
4038         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4039
4040                 if (enable_processing) {
4041
4042                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4043
4044                         if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4045                                 MessageDialog d (
4046                                         _("You can't perform this operation because the processing of the signal "
4047                                           "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4048                                           "You can do this without processing, which is a different operation.")
4049                                         );
4050                                 d.set_title (_("Cannot bounce"));
4051                                 d.run ();
4052                                 return;
4053                         }
4054                 }
4055         }
4056
4057         framepos_t start = selection->time[clicked_selection].start;
4058         framepos_t end = selection->time[clicked_selection].end;
4059         framepos_t cnt = end - start + 1;
4060         bool in_command = false;
4061
4062         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4063
4064                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4065
4066                 if (!rtv) {
4067                         continue;
4068                 }
4069
4070                 boost::shared_ptr<Playlist> playlist;
4071
4072                 if ((playlist = rtv->playlist()) == 0) {
4073                         continue;
4074                 }
4075
4076                 InterThreadInfo itt;
4077
4078                 playlist->clear_changes ();
4079                 playlist->clear_owned_changes ();
4080
4081                 boost::shared_ptr<Region> r;
4082
4083                 if (enable_processing) {
4084                         r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4085                 } else {
4086                         r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4087                 }
4088
4089                 if (!r) {
4090                         continue;
4091                 }
4092
4093                 if (replace) {
4094                         list<AudioRange> ranges;
4095                         ranges.push_back (AudioRange (start, start+cnt, 0));
4096                         playlist->cut (ranges); // discard result
4097                         playlist->add_region (r, start);
4098                 }
4099
4100                 if (!in_command) {
4101                         begin_reversible_command (_("bounce range"));
4102                         in_command = true;
4103                 }
4104                 vector<Command*> cmds;
4105                 playlist->rdiff (cmds);
4106                 _session->add_commands (cmds);
4107
4108                 _session->add_command (new StatefulDiffCommand (playlist));
4109         }
4110
4111         if (in_command) {
4112                 commit_reversible_command ();
4113         }
4114 }
4115
4116 /** Delete selected regions, automation points or a time range */
4117 void
4118 Editor::delete_ ()
4119 {
4120         //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4121         //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4122         bool deleted = false;
4123         if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4124                 deleted = current_mixer_strip->delete_processors ();
4125
4126         if (!deleted)
4127                 cut_copy (Delete);
4128 }
4129
4130 /** Cut selected regions, automation points or a time range */
4131 void
4132 Editor::cut ()
4133 {
4134         cut_copy (Cut);
4135 }
4136
4137 /** Copy selected regions, automation points or a time range */
4138 void
4139 Editor::copy ()
4140 {
4141         cut_copy (Copy);
4142 }
4143
4144
4145 /** @return true if a Cut, Copy or Clear is possible */
4146 bool
4147 Editor::can_cut_copy () const
4148 {
4149         if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4150                 return true;
4151
4152         return false;
4153 }
4154
4155
4156 /** Cut, copy or clear selected regions, automation points or a time range.
4157  * @param op Operation (Delete, Cut, Copy or Clear)
4158  */
4159 void
4160 Editor::cut_copy (CutCopyOp op)
4161 {
4162         /* only cancel selection if cut/copy is successful.*/
4163
4164         string opname;
4165
4166         switch (op) {
4167         case Delete:
4168                 opname = _("delete");
4169                 break;
4170         case Cut:
4171                 opname = _("cut");
4172                 break;
4173         case Copy:
4174                 opname = _("copy");
4175                 break;
4176         case Clear:
4177                 opname = _("clear");
4178                 break;
4179         }
4180
4181         /* if we're deleting something, and the mouse is still pressed,
4182            the thing we started a drag for will be gone when we release
4183            the mouse button(s). avoid this. see part 2 at the end of
4184            this function.
4185         */
4186
4187         if (op == Delete || op == Cut || op == Clear) {
4188                 if (_drags->active ()) {
4189                         _drags->abort ();
4190                 }
4191         }
4192
4193         if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4194                 cut_buffer->clear ();
4195         }
4196
4197         if (entered_marker) {
4198
4199                 /* cut/delete op while pointing at a marker */
4200
4201                 bool ignored;
4202                 Location* loc = find_location_from_marker (entered_marker, ignored);
4203
4204                 if (_session && loc) {
4205                         entered_marker = NULL;
4206                         Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4207                 }
4208
4209                 _drags->abort ();
4210                 return;
4211         }
4212
4213         switch (mouse_mode) {
4214         case MouseDraw:
4215         case MouseContent:
4216                 begin_reversible_command (opname + ' ' + X_("MIDI"));
4217                 cut_copy_midi (op);
4218                 commit_reversible_command ();
4219                 return;
4220         default:
4221                 break;
4222         }
4223
4224         bool did_edit = false;
4225
4226         if (!selection->regions.empty() || !selection->points.empty()) {
4227                 begin_reversible_command (opname + ' ' + _("objects"));
4228                 did_edit = true;
4229
4230                 if (!selection->regions.empty()) {
4231                         cut_copy_regions (op, selection->regions);
4232
4233                         if (op == Cut || op == Delete) {
4234                                 selection->clear_regions ();
4235                         }
4236                 }
4237
4238                 if (!selection->points.empty()) {
4239                         cut_copy_points (op);
4240
4241                         if (op == Cut || op == Delete) {
4242                                 selection->clear_points ();
4243                         }
4244                 }
4245         } else if (selection->time.empty()) {
4246                 framepos_t start, end;
4247                 /* no time selection, see if we can get an edit range
4248                    and use that.
4249                 */
4250                 if (get_edit_op_range (start, end)) {
4251                         selection->set (start, end);
4252                 }
4253         } else if (!selection->time.empty()) {
4254                 begin_reversible_command (opname + ' ' + _("range"));
4255
4256                 did_edit = true;
4257                 cut_copy_ranges (op);
4258
4259                 if (op == Cut || op == Delete) {
4260                         selection->clear_time ();
4261                 }
4262         }
4263
4264         if (did_edit) {
4265                 /* reset repeated paste state */
4266                 paste_count    = 0;
4267                 last_paste_pos = 0;
4268                 commit_reversible_command ();
4269         }
4270
4271         if (op == Delete || op == Cut || op == Clear) {
4272                 _drags->abort ();
4273         }
4274 }
4275
4276
4277 struct AutomationRecord {
4278         AutomationRecord () : state (0) , line(NULL) {}
4279         AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4280
4281         XMLNode* state; ///< state before any operation
4282         const AutomationLine* line; ///< line this came from
4283         boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4284 };
4285
4286 struct PointsSelectionPositionSorter {
4287         bool operator() (ControlPoint* a, ControlPoint* b) {
4288                 return (*(a->model()))->when < (*(b->model()))->when;
4289         }
4290 };
4291
4292 /** Cut, copy or clear selected automation points.
4293  *  @param op Operation (Cut, Copy or Clear)
4294  */
4295 void
4296 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4297 {
4298         if (selection->points.empty ()) {
4299                 return;
4300         }
4301
4302         /* XXX: not ideal, as there may be more than one track involved in the point selection */
4303         _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4304
4305         /* Keep a record of the AutomationLists that we end up using in this operation */
4306         typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4307         Lists lists;
4308
4309         /* user could select points in any order */
4310         selection->points.sort(PointsSelectionPositionSorter ());
4311
4312         /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4313         for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4314                 const AutomationLine&                   line = (*sel_point)->line();
4315                 const boost::shared_ptr<AutomationList> al   = line.the_list();
4316                 if (lists.find (al) == lists.end ()) {
4317                         /* We haven't seen this list yet, so make a record for it.  This includes
4318                            taking a copy of its current state, in case this is needed for undo later.
4319                         */
4320                         lists[al] = AutomationRecord (&al->get_state (), &line);
4321                 }
4322         }
4323
4324         if (op == Cut || op == Copy) {
4325                 /* This operation will involve putting things in the cut buffer, so create an empty
4326                    ControlList for each of our source lists to put the cut buffer data in.
4327                 */
4328                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4329                         i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4330                 }
4331
4332                 /* Add all selected points to the relevant copy ControlLists */
4333                 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4334                 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4335                         boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
4336                         AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4337
4338                         lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4339                         if (midi) {
4340                                 /* Update earliest MIDI start time in beats */
4341                                 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4342                         } else {
4343                                 /* Update earliest session start time in frames */
4344                                 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4345                         }
4346                 }
4347
4348                 /* Snap start time backwards, so copy/paste is snap aligned. */
4349                 if (midi) {
4350                         if (earliest == Evoral::Beats::max()) {
4351                                 earliest = Evoral::Beats();  // Weird... don't offset
4352                         }
4353                         earliest.round_down_to_beat();
4354                 } else {
4355                         if (start.frame == std::numeric_limits<double>::max()) {
4356                                 start.frame = 0;  // Weird... don't offset
4357                         }
4358                         snap_to(start, RoundDownMaybe);
4359                 }
4360
4361                 const double line_offset = midi ? earliest.to_double() : start.frame;
4362                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4363                         /* Correct this copy list so that it is relative to the earliest
4364                            start time, so relative ordering between points is preserved
4365                            when copying from several lists and the paste starts at the
4366                            earliest copied piece of data. */
4367                         boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4368                         for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4369                                 (*ctrl_evt)->when -= line_offset;
4370                         }
4371
4372                         /* And add it to the cut buffer */
4373                         cut_buffer->add (al_cpy);
4374                 }
4375         }
4376
4377         if (op == Delete || op == Cut) {
4378                 /* This operation needs to remove things from the main AutomationList, so do that now */
4379
4380                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4381                         i->first->freeze ();
4382                 }
4383
4384                 /* Remove each selected point from its AutomationList */
4385                 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4386                         AutomationLine& line = (*sel_point)->line ();
4387                         boost::shared_ptr<AutomationList> al = line.the_list();
4388
4389                         bool erase = true;
4390
4391                         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4392                                 /* removing of first and last gain point in region gain lines is prohibited*/
4393                                 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4394                                         erase = false;
4395                                 }
4396                         }
4397
4398                         if(erase) {
4399                                 al->erase ((*sel_point)->model ());
4400                         }
4401                 }
4402
4403                 /* Thaw the lists and add undo records for them */
4404                 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4405                         boost::shared_ptr<AutomationList> al = i->first;
4406                         al->thaw ();
4407                         _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4408                 }
4409         }
4410 }
4411
4412 /** Cut, copy or clear selected automation points.
4413  * @param op Operation (Cut, Copy or Clear)
4414  */
4415 void
4416 Editor::cut_copy_midi (CutCopyOp op)
4417 {
4418         Evoral::Beats earliest = Evoral::Beats::max();
4419         for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4420                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4421                 if (mrv) {
4422                         if (!mrv->selection().empty()) {
4423                                 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4424                         }
4425                         mrv->cut_copy_clear (op);
4426
4427                         /* XXX: not ideal, as there may be more than one track involved in the selection */
4428                         _last_cut_copy_source_track = &mrv->get_time_axis_view();
4429                 }
4430         }
4431
4432         if (!selection->points.empty()) {
4433                 cut_copy_points (op, earliest, true);
4434                 if (op == Cut || op == Delete) {
4435                         selection->clear_points ();
4436                 }
4437         }
4438 }
4439
4440 struct lt_playlist {
4441         bool operator () (const PlaylistState& a, const PlaylistState& b) {
4442                 return a.playlist < b.playlist;
4443         }
4444 };
4445
4446 struct PlaylistMapping {
4447         TimeAxisView* tv;
4448         boost::shared_ptr<Playlist> pl;
4449
4450         PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4451 };
4452
4453 /** Remove `clicked_regionview' */
4454 void
4455 Editor::remove_clicked_region ()
4456 {
4457         if (clicked_routeview == 0 || clicked_regionview == 0) {
4458                 return;
4459         }
4460
4461         begin_reversible_command (_("remove region"));
4462
4463         boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4464
4465         playlist->clear_changes ();
4466         playlist->clear_owned_changes ();
4467         playlist->remove_region (clicked_regionview->region());
4468         if (Config->get_edit_mode() == Ripple)
4469                 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4470
4471         /* We might have removed regions, which alters other regions' layering_index,
4472            so we need to do a recursive diff here.
4473         */
4474         vector<Command*> cmds;
4475         playlist->rdiff (cmds);
4476         _session->add_commands (cmds);
4477
4478         _session->add_command(new StatefulDiffCommand (playlist));
4479         commit_reversible_command ();
4480 }
4481
4482
4483 /** Remove the selected regions */
4484 void
4485 Editor::remove_selected_regions ()
4486 {
4487         RegionSelection rs = get_regions_from_selection_and_entered ();
4488
4489         if (!_session || rs.empty()) {
4490                 return;
4491         }
4492
4493         list<boost::shared_ptr<Region> > regions_to_remove;
4494
4495         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4496                 // we can't just remove the region(s) in this loop because
4497                 // this removes them from the RegionSelection, and they thus
4498                 // disappear from underneath the iterator, and the ++i above
4499                 // SEGVs in a puzzling fashion.
4500
4501                 // so, first iterate over the regions to be removed from rs and
4502                 // add them to the regions_to_remove list, and then
4503                 // iterate over the list to actually remove them.
4504
4505                 regions_to_remove.push_back ((*i)->region());
4506         }
4507
4508         vector<boost::shared_ptr<Playlist> > playlists;
4509
4510         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4511
4512                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4513
4514                 if (!playlist) {
4515                         // is this check necessary?
4516                         continue;
4517                 }
4518
4519                 /* get_regions_from_selection_and_entered() guarantees that
4520                    the playlists involved are unique, so there is no need
4521                    to check here.
4522                 */
4523
4524                 playlists.push_back (playlist);
4525
4526                 playlist->clear_changes ();
4527                 playlist->clear_owned_changes ();
4528                 playlist->freeze ();
4529                 playlist->remove_region (*rl);
4530                 if (Config->get_edit_mode() == Ripple)
4531                         playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4532
4533         }
4534
4535         vector<boost::shared_ptr<Playlist> >::iterator pl;
4536         bool in_command = false;
4537
4538         for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4539                 (*pl)->thaw ();
4540
4541                 /* We might have removed regions, which alters other regions' layering_index,
4542                    so we need to do a recursive diff here.
4543                 */
4544
4545                 if (!in_command) {
4546                         begin_reversible_command (_("remove region"));
4547                         in_command = true;
4548                 }
4549                 vector<Command*> cmds;
4550                 (*pl)->rdiff (cmds);
4551                 _session->add_commands (cmds);
4552
4553                 _session->add_command(new StatefulDiffCommand (*pl));
4554         }
4555
4556         if (in_command) {
4557                 commit_reversible_command ();
4558         }
4559 }
4560
4561 /** Cut, copy or clear selected regions.
4562  * @param op Operation (Cut, Copy or Clear)
4563  */
4564 void
4565 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4566 {
4567         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4568            a map when we want ordered access to both elements. i think.
4569         */
4570
4571         vector<PlaylistMapping> pmap;
4572
4573         framepos_t first_position = max_framepos;
4574
4575         typedef set<boost::shared_ptr<Playlist> > FreezeList;
4576         FreezeList freezelist;
4577
4578         /* get ordering correct before we cut/copy */
4579
4580         rs.sort_by_position_and_track ();
4581
4582         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4583
4584                 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4585
4586                 if (op == Cut || op == Clear || op == Delete) {
4587                         boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4588
4589                         if (pl) {
4590                                 FreezeList::iterator fl;
4591
4592                                 // only take state if this is a new playlist.
4593                                 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4594                                         if ((*fl) == pl) {
4595                                                 break;
4596                                         }
4597                                 }
4598
4599                                 if (fl == freezelist.end()) {
4600                                         pl->clear_changes();
4601                                         pl->clear_owned_changes ();
4602                                         pl->freeze ();
4603                                         freezelist.insert (pl);
4604                                 }
4605                         }
4606                 }
4607
4608                 TimeAxisView* tv = &(*x)->get_time_axis_view();
4609                 vector<PlaylistMapping>::iterator z;
4610
4611                 for (z = pmap.begin(); z != pmap.end(); ++z) {
4612                         if ((*z).tv == tv) {
4613                                 break;
4614                         }
4615                 }
4616
4617                 if (z == pmap.end()) {
4618                         pmap.push_back (PlaylistMapping (tv));
4619                 }
4620         }
4621
4622         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4623
4624                 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4625
4626                 if (!pl) {
4627                         /* region not yet associated with a playlist (e.g. unfinished
4628                            capture pass.
4629                         */
4630                         ++x;
4631                         continue;
4632                 }
4633
4634                 TimeAxisView& tv = (*x)->get_time_axis_view();
4635                 boost::shared_ptr<Playlist> npl;
4636                 RegionSelection::iterator tmp;
4637
4638                 tmp = x;
4639                 ++tmp;
4640
4641                 if (op != Delete) {
4642
4643                         vector<PlaylistMapping>::iterator z;
4644
4645                         for (z = pmap.begin(); z != pmap.end(); ++z) {
4646                                 if ((*z).tv == &tv) {
4647                                         break;
4648                                 }
4649                         }
4650
4651                         assert (z != pmap.end());
4652
4653                         if (!(*z).pl) {
4654                                 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4655                                 npl->freeze();
4656                                 (*z).pl = npl;
4657                         } else {
4658                                 npl = (*z).pl;
4659                         }
4660                 }
4661
4662                 boost::shared_ptr<Region> r = (*x)->region();
4663                 boost::shared_ptr<Region> _xx;
4664
4665                 assert (r != 0);
4666
4667                 switch (op) {
4668                 case Delete:
4669                         pl->remove_region (r);
4670                         if (Config->get_edit_mode() == Ripple)
4671                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4672                         break;
4673
4674                 case Cut:
4675                         _xx = RegionFactory::create (r);
4676                         npl->add_region (_xx, r->position() - first_position);
4677                         pl->remove_region (r);
4678                         if (Config->get_edit_mode() == Ripple)
4679                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4680                         break;
4681
4682                 case Copy:
4683                         /* copy region before adding, so we're not putting same object into two different playlists */
4684                         npl->add_region (RegionFactory::create (r), r->position() - first_position);
4685                         break;
4686
4687                 case Clear:
4688                         pl->remove_region (r);
4689                         if (Config->get_edit_mode() == Ripple)
4690                                 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4691                         break;
4692                 }
4693
4694                 x = tmp;
4695         }
4696
4697         if (op != Delete) {
4698
4699                 list<boost::shared_ptr<Playlist> > foo;
4700
4701                 /* the pmap is in the same order as the tracks in which selected regions occurred */
4702
4703                 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4704                         if ((*i).pl) {
4705                                 (*i).pl->thaw();
4706                                 foo.push_back ((*i).pl);
4707                         }
4708                 }
4709
4710                 if (!foo.empty()) {
4711                         cut_buffer->set (foo);
4712                 }
4713
4714                 if (pmap.empty()) {
4715                         _last_cut_copy_source_track = 0;
4716                 } else {
4717                         _last_cut_copy_source_track = pmap.front().tv;
4718                 }
4719         }
4720
4721         for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4722                 (*pl)->thaw ();
4723
4724                 /* We might have removed regions, which alters other regions' layering_index,
4725                    so we need to do a recursive diff here.
4726                 */
4727                 vector<Command*> cmds;
4728                 (*pl)->rdiff (cmds);
4729                 _session->add_commands (cmds);
4730
4731                 _session->add_command (new StatefulDiffCommand (*pl));
4732         }
4733 }
4734
4735 void
4736 Editor::cut_copy_ranges (CutCopyOp op)
4737 {
4738         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4739
4740         /* Sort the track selection now, so that it if is used, the playlists
4741            selected by the calls below to cut_copy_clear are in the order that
4742            their tracks appear in the editor.  This makes things like paste
4743            of ranges work properly.
4744         */
4745
4746         sort_track_selection (ts);
4747
4748         if (ts.empty()) {
4749                 if (!entered_track) {
4750                         return;
4751                 }
4752                 ts.push_back (entered_track);
4753         }
4754
4755         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4756                 (*i)->cut_copy_clear (*selection, op);
4757         }
4758 }
4759
4760 void
4761 Editor::paste (float times, bool from_context)
4762 {
4763         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4764         MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4765         paste_internal (where.frame, times, 0);
4766 }
4767
4768 void
4769 Editor::mouse_paste ()
4770 {
4771         MusicFrame where (0, 0);
4772         bool ignored;
4773         if (!mouse_frame (where.frame, ignored)) {
4774                 return;
4775         }
4776
4777         snap_to (where);
4778         paste_internal (where.frame, 1, where.division);
4779 }
4780
4781 void
4782 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4783 {
4784         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4785
4786         if (cut_buffer->empty(internal_editing())) {
4787                 return;
4788         }
4789
4790         if (position == max_framepos) {
4791                 position = get_preferred_edit_position();
4792                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4793         }
4794
4795         if (position == last_paste_pos) {
4796                 /* repeated paste in the same position */
4797                 ++paste_count;
4798         } else {
4799                 /* paste in new location, reset repeated paste state */
4800                 paste_count = 0;
4801                 last_paste_pos = position;
4802         }
4803
4804         /* get everything in the correct order */
4805
4806         TrackViewList ts;
4807         if (!selection->tracks.empty()) {
4808                 /* If there is a track selection, paste into exactly those tracks and
4809                  * only those tracks.  This allows the user to be explicit and override
4810                  * the below "do the reasonable thing" logic. */
4811                 ts = selection->tracks.filter_to_unique_playlists ();
4812                 sort_track_selection (ts);
4813         } else {
4814                 /* Figure out which track to base the paste at. */
4815                 TimeAxisView* base_track = NULL;
4816                 if (_edit_point == Editing::EditAtMouse && entered_track) {
4817                         /* With the mouse edit point, paste onto the track under the mouse. */
4818                         base_track = entered_track;
4819                 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4820                         /* With the mouse edit point, paste onto the track of the region under the mouse. */
4821                         base_track = &entered_regionview->get_time_axis_view();
4822                 } else if (_last_cut_copy_source_track) {
4823                         /* Paste to the track that the cut/copy came from (see mantis #333). */
4824                         base_track = _last_cut_copy_source_track;
4825                 } else {
4826                         /* This is "impossible" since we've copied... well, do nothing. */
4827                         return;
4828                 }
4829
4830                 /* Walk up to parent if necessary, so base track is a route. */
4831                 while (base_track->get_parent()) {
4832                         base_track = base_track->get_parent();
4833                 }
4834
4835                 /* Add base track and all tracks below it.  The paste logic will select
4836                    the appropriate object types from the cut buffer in relative order. */
4837                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4838                         if ((*i)->order() >= base_track->order()) {
4839                                 ts.push_back(*i);
4840                         }
4841                 }
4842
4843                 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4844                 sort_track_selection (ts);
4845
4846                 /* Add automation children of each track in order, for pasting several lines. */
4847                 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4848                         /* Add any automation children for pasting several lines */
4849                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4850                         if (!rtv) {
4851                                 continue;
4852                         }
4853
4854                         typedef RouteTimeAxisView::AutomationTracks ATracks;
4855                         const ATracks& atracks = rtv->automation_tracks();
4856                         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4857                                 i = ts.insert(i, a->second.get());
4858                                 ++i;
4859                         }
4860                 }
4861
4862                 /* We now have a list of trackviews starting at base_track, including
4863                    automation children, in the order shown in the editor, e.g. R1,
4864                    R1.A1, R1.A2, R2, R2.A1, ... */
4865         }
4866
4867         begin_reversible_command (Operations::paste);
4868
4869         if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4870             dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4871             /* Only one line copied, and one automation track selected.  Do a
4872                "greedy" paste from one automation type to another. */
4873
4874                 PasteContext ctx(paste_count, times, ItemCounts(), true);
4875                 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4876
4877         } else {
4878
4879                 /* Paste into tracks */
4880
4881                 PasteContext ctx(paste_count, times, ItemCounts(), false);
4882                 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4883                         (*i)->paste (position, *cut_buffer, ctx, sub_num);
4884                 }
4885         }
4886
4887         commit_reversible_command ();
4888 }
4889
4890 void
4891 Editor::duplicate_regions (float times)
4892 {
4893         RegionSelection rs (get_regions_from_selection_and_entered());
4894         duplicate_some_regions (rs, times);
4895 }
4896
4897 void
4898 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4899 {
4900         if (regions.empty ()) {
4901                 return;
4902         }
4903
4904         boost::shared_ptr<Playlist> playlist;
4905         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4906         RegionSelection foo;
4907
4908         framepos_t const start_frame = regions.start ();
4909         framepos_t const end_frame = regions.end_frame ();
4910         framecnt_t const gap = end_frame - start_frame + 1;
4911
4912         begin_reversible_command (Operations::duplicate_region);
4913
4914         selection->clear_regions ();
4915
4916         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4917
4918                 boost::shared_ptr<Region> r ((*i)->region());
4919
4920                 TimeAxisView& tv = (*i)->get_time_axis_view();
4921                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4922                 latest_regionviews.clear ();
4923                 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4924
4925                 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4926                 playlist = (*i)->region()->playlist();
4927                 playlist->clear_changes ();
4928                 playlist->duplicate (r, position, gap, times);
4929                 _session->add_command(new StatefulDiffCommand (playlist));
4930
4931                 c.disconnect ();
4932
4933                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4934         }
4935
4936         if (!foo.empty()) {
4937                 selection->set (foo);
4938         }
4939
4940         commit_reversible_command ();
4941 }
4942
4943 void
4944 Editor::duplicate_selection (float times)
4945 {
4946         if (selection->time.empty() || selection->tracks.empty()) {
4947                 return;
4948         }
4949
4950         boost::shared_ptr<Playlist> playlist;
4951
4952         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4953
4954         bool in_command = false;
4955
4956         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4957                 if ((playlist = (*i)->playlist()) == 0) {
4958                         continue;
4959                 }
4960                 playlist->clear_changes ();
4961
4962                 if (clicked_selection) {
4963                         playlist->duplicate_range (selection->time[clicked_selection], times);
4964                 } else {
4965                         playlist->duplicate_ranges (selection->time, times);
4966                 }
4967
4968                 if (!in_command) {
4969                         begin_reversible_command (_("duplicate range selection"));
4970                         in_command = true;
4971                 }
4972                 _session->add_command (new StatefulDiffCommand (playlist));
4973
4974         }
4975
4976         if (in_command) {
4977                 if (times == 1.0f) {
4978                         // now "move" range selection to after the current range selection
4979                         framecnt_t distance = 0;
4980
4981                         if (clicked_selection) {
4982                                 distance =
4983                                     selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4984                         } else {
4985                                 distance = selection->time.end_frame () - selection->time.start ();
4986                         }
4987
4988                         selection->move_time (distance);
4989                 }
4990                 commit_reversible_command ();
4991         }
4992 }
4993
4994 /** Reset all selected points to the relevant default value */
4995 void
4996 Editor::reset_point_selection ()
4997 {
4998         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4999                 ARDOUR::AutomationList::iterator j = (*i)->model ();
5000                 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
5001         }
5002 }
5003
5004 void
5005 Editor::center_playhead ()
5006 {
5007         float const page = _visible_canvas_width * samples_per_pixel;
5008         center_screen_internal (playhead_cursor->current_frame (), page);
5009 }
5010
5011 void
5012 Editor::center_edit_point ()
5013 {
5014         float const page = _visible_canvas_width * samples_per_pixel;
5015         center_screen_internal (get_preferred_edit_position(), page);
5016 }
5017
5018 /** Caller must begin and commit a reversible command */
5019 void
5020 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5021 {
5022         playlist->clear_changes ();
5023         playlist->clear ();
5024         _session->add_command (new StatefulDiffCommand (playlist));
5025 }
5026
5027 void
5028 Editor::nudge_track (bool use_edit, bool forwards)
5029 {
5030         boost::shared_ptr<Playlist> playlist;
5031         framepos_t distance;
5032         framepos_t next_distance;
5033         framepos_t start;
5034
5035         if (use_edit) {
5036                 start = get_preferred_edit_position();
5037         } else {
5038                 start = 0;
5039         }
5040
5041         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5042                 return;
5043         }
5044
5045         if (selection->tracks.empty()) {
5046                 return;
5047         }
5048
5049         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5050         bool in_command = false;
5051
5052         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5053
5054                 if ((playlist = (*i)->playlist()) == 0) {
5055                         continue;
5056                 }
5057
5058                 playlist->clear_changes ();
5059                 playlist->clear_owned_changes ();
5060
5061                 playlist->nudge_after (start, distance, forwards);
5062
5063                 if (!in_command) {
5064                         begin_reversible_command (_("nudge track"));
5065                         in_command = true;
5066                 }
5067                 vector<Command*> cmds;
5068
5069                 playlist->rdiff (cmds);
5070                 _session->add_commands (cmds);
5071
5072                 _session->add_command (new StatefulDiffCommand (playlist));
5073         }
5074
5075         if (in_command) {
5076                 commit_reversible_command ();
5077         }
5078 }
5079
5080 void
5081 Editor::remove_last_capture ()
5082 {
5083         vector<string> choices;
5084         string prompt;
5085
5086         if (!_session) {
5087                 return;
5088         }
5089
5090         if (Config->get_verify_remove_last_capture()) {
5091                 prompt  = _("Do you really want to destroy the last capture?"
5092                             "\n(This is destructive and cannot be undone)");
5093
5094                 choices.push_back (_("No, do nothing."));
5095                 choices.push_back (_("Yes, destroy it."));
5096
5097                 Choice prompter (_("Destroy last capture"), prompt, choices);
5098
5099                 if (prompter.run () == 1) {
5100                         _session->remove_last_capture ();
5101                         _regions->redisplay ();
5102                 }
5103
5104         } else {
5105                 _session->remove_last_capture();
5106                 _regions->redisplay ();
5107         }
5108 }
5109
5110 void
5111 Editor::normalize_region ()
5112 {
5113         if (!_session) {
5114                 return;
5115         }
5116
5117         RegionSelection rs = get_regions_from_selection_and_entered ();
5118
5119         if (rs.empty()) {
5120                 return;
5121         }
5122
5123         NormalizeDialog dialog (rs.size() > 1);
5124
5125         if (dialog.run () != RESPONSE_ACCEPT) {
5126                 return;
5127         }
5128
5129         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5130         gdk_flush ();
5131
5132         /* XXX: should really only count audio regions here */
5133         int const regions = rs.size ();
5134
5135         /* Make a list of the selected audio regions' maximum amplitudes, and also
5136            obtain the maximum amplitude of them all.
5137         */
5138         list<double> max_amps;
5139         list<double> rms_vals;
5140         double max_amp = 0;
5141         double max_rms = 0;
5142         bool use_rms = dialog.constrain_rms ();
5143
5144         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5145                 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5146                 if (!arv) {
5147                         continue;
5148                 }
5149                 dialog.descend (1.0 / regions);
5150                 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5151                 if (use_rms) {
5152                         double r = arv->audio_region()->rms (&dialog);
5153                         max_rms = max (max_rms, r);
5154                         rms_vals.push_back (r);
5155                 }
5156
5157                 if (a == -1) {
5158                         /* the user cancelled the operation */
5159                         return;
5160                 }
5161
5162                 max_amps.push_back (a);
5163                 max_amp = max (max_amp, a);
5164                 dialog.ascend ();
5165         }
5166
5167         list<double>::const_iterator a = max_amps.begin ();
5168         list<double>::const_iterator l = rms_vals.begin ();
5169         bool in_command = false;
5170
5171         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5172                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5173                 if (!arv) {
5174                         continue;
5175                 }
5176
5177                 arv->region()->clear_changes ();
5178
5179                 double amp = dialog.normalize_individually() ? *a : max_amp;
5180                 double target = dialog.target_peak (); // dB
5181
5182                 if (use_rms) {
5183                         double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5184                         const double t_rms = dialog.target_rms ();
5185                         const gain_t c_peak = dB_to_coefficient (target);
5186                         const gain_t c_rms  = dB_to_coefficient (t_rms);
5187                         if ((amp_rms / c_rms) > (amp / c_peak)) {
5188                                 amp = amp_rms;
5189                                 target = t_rms;
5190                         }
5191                 }
5192
5193                 arv->audio_region()->normalize (amp, target);
5194
5195                 if (!in_command) {
5196                         begin_reversible_command (_("normalize"));
5197                         in_command = true;
5198                 }
5199                 _session->add_command (new StatefulDiffCommand (arv->region()));
5200
5201                 ++a;
5202                 ++l;
5203         }
5204
5205         if (in_command) {
5206                 commit_reversible_command ();
5207         }
5208 }
5209
5210
5211 void
5212 Editor::reset_region_scale_amplitude ()
5213 {
5214         if (!_session) {
5215                 return;
5216         }
5217
5218         RegionSelection rs = get_regions_from_selection_and_entered ();
5219
5220         if (rs.empty()) {
5221                 return;
5222         }
5223
5224         bool in_command = false;
5225
5226         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5227                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5228                 if (!arv)
5229                         continue;
5230                 arv->region()->clear_changes ();
5231                 arv->audio_region()->set_scale_amplitude (1.0f);
5232
5233                 if(!in_command) {
5234                                 begin_reversible_command ("reset gain");
5235                                 in_command = true;
5236                 }
5237                 _session->add_command (new StatefulDiffCommand (arv->region()));
5238         }
5239
5240         if (in_command) {
5241                 commit_reversible_command ();
5242         }
5243 }
5244
5245 void
5246 Editor::adjust_region_gain (bool up)
5247 {
5248         RegionSelection rs = get_regions_from_selection_and_entered ();
5249
5250         if (!_session || rs.empty()) {
5251                 return;
5252         }
5253
5254         bool in_command = false;
5255
5256         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5257                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5258                 if (!arv) {
5259                         continue;
5260                 }
5261
5262                 arv->region()->clear_changes ();
5263
5264                 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5265
5266                 if (up) {
5267                         dB += 1;
5268                 } else {
5269                         dB -= 1;
5270                 }
5271
5272                 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5273
5274                 if (!in_command) {
5275                                 begin_reversible_command ("adjust region gain");
5276                                 in_command = true;
5277                 }
5278                 _session->add_command (new StatefulDiffCommand (arv->region()));
5279         }
5280
5281         if (in_command) {
5282                 commit_reversible_command ();
5283         }
5284 }
5285
5286 void
5287 Editor::reset_region_gain ()
5288 {
5289         RegionSelection rs = get_regions_from_selection_and_entered ();
5290
5291         if (!_session || rs.empty()) {
5292                 return;
5293         }
5294
5295         bool in_command = false;
5296
5297         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5298                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5299                 if (!arv) {
5300                         continue;
5301                 }
5302
5303                 arv->region()->clear_changes ();
5304
5305                 arv->audio_region()->set_scale_amplitude (1.0f);
5306
5307                 if (!in_command) {
5308                                 begin_reversible_command ("reset region gain");
5309                                 in_command = true;
5310                 }
5311                 _session->add_command (new StatefulDiffCommand (arv->region()));
5312         }
5313
5314         if (in_command) {
5315                 commit_reversible_command ();
5316         }
5317 }
5318
5319 void
5320 Editor::reverse_region ()
5321 {
5322         if (!_session) {
5323                 return;
5324         }
5325
5326         Reverse rev (*_session);
5327         apply_filter (rev, _("reverse regions"));
5328 }
5329
5330 void
5331 Editor::strip_region_silence ()
5332 {
5333         if (!_session) {
5334                 return;
5335         }
5336
5337         RegionSelection rs = get_regions_from_selection_and_entered ();
5338
5339         if (rs.empty()) {
5340                 return;
5341         }
5342
5343         std::list<RegionView*> audio_only;
5344
5345         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5346                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5347                 if (arv) {
5348                         audio_only.push_back (arv);
5349                 }
5350         }
5351
5352         assert (!audio_only.empty());
5353
5354         StripSilenceDialog d (_session, audio_only);
5355         int const r = d.run ();
5356
5357         d.drop_rects ();
5358
5359         if (r == Gtk::RESPONSE_OK) {
5360                 ARDOUR::AudioIntervalMap silences;
5361                 d.silences (silences);
5362                 StripSilence s (*_session, silences, d.fade_length());
5363
5364                 apply_filter (s, _("strip silence"), &d);
5365         }
5366 }
5367
5368 Command*
5369 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5370 {
5371         Evoral::Sequence<Evoral::Beats>::Notes selected;
5372         mrv.selection_as_notelist (selected, true);
5373
5374         vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5375         v.push_back (selected);
5376
5377         Evoral::Beats pos_beats  = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5378
5379         return op (mrv.midi_region()->model(), pos_beats, v);
5380 }
5381
5382 void
5383 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5384 {
5385         if (rs.empty()) {
5386                 return;
5387         }
5388
5389         bool in_command = false;
5390
5391         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5392                 RegionSelection::const_iterator tmp = r;
5393                 ++tmp;
5394
5395                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5396
5397                 if (mrv) {
5398                         Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5399                         if (cmd) {
5400                                 if (!in_command) {
5401                                         begin_reversible_command (op.name ());
5402                                         in_command = true;
5403                                 }
5404                                 (*cmd)();
5405                                 _session->add_command (cmd);
5406                         }
5407                 }
5408
5409                 r = tmp;
5410         }
5411
5412         if (in_command) {
5413                 commit_reversible_command ();
5414         }
5415 }
5416
5417 void
5418 Editor::fork_region ()
5419 {
5420         RegionSelection rs = get_regions_from_selection_and_entered ();
5421
5422         if (rs.empty()) {
5423                 return;
5424         }
5425
5426         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5427         bool in_command = false;
5428
5429         gdk_flush ();
5430
5431         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5432                 RegionSelection::iterator tmp = r;
5433                 ++tmp;
5434
5435                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5436
5437                 if (mrv) {
5438                         try {
5439                                 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5440                                 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5441                                 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5442
5443                                 if (!in_command) {
5444                                         begin_reversible_command (_("Fork Region(s)"));
5445                                         in_command = true;
5446                                 }
5447                                 playlist->clear_changes ();
5448                                 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5449                                 _session->add_command(new StatefulDiffCommand (playlist));
5450                         } catch (...) {
5451                                 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5452                         }
5453                 }
5454
5455                 r = tmp;
5456         }
5457
5458         if (in_command) {
5459                 commit_reversible_command ();
5460         }
5461 }
5462
5463 void
5464 Editor::quantize_region ()
5465 {
5466         if (_session) {
5467                 quantize_regions(get_regions_from_selection_and_entered ());
5468         }
5469 }
5470
5471 void
5472 Editor::quantize_regions (const RegionSelection& rs)
5473 {
5474         if (rs.n_midi_regions() == 0) {
5475                 return;
5476         }
5477
5478         if (!quantize_dialog) {
5479                 quantize_dialog = new QuantizeDialog (*this);
5480         }
5481
5482         if (quantize_dialog->is_mapped()) {
5483                 /* in progress already */
5484                 return;
5485         }
5486
5487         quantize_dialog->present ();
5488         const int r = quantize_dialog->run ();
5489         quantize_dialog->hide ();
5490
5491         if (r == Gtk::RESPONSE_OK) {
5492                 Quantize quant (quantize_dialog->snap_start(),
5493                                 quantize_dialog->snap_end(),
5494                                 quantize_dialog->start_grid_size(),
5495                                 quantize_dialog->end_grid_size(),
5496                                 quantize_dialog->strength(),
5497                                 quantize_dialog->swing(),
5498                                 quantize_dialog->threshold());
5499
5500                 apply_midi_note_edit_op (quant, rs);
5501         }
5502 }
5503
5504 void
5505 Editor::legatize_region (bool shrink_only)
5506 {
5507         if (_session) {
5508                 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5509         }
5510 }
5511
5512 void
5513 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5514 {
5515         if (rs.n_midi_regions() == 0) {
5516                 return;
5517         }
5518
5519         Legatize legatize(shrink_only);
5520         apply_midi_note_edit_op (legatize, rs);
5521 }
5522
5523 void
5524 Editor::transform_region ()
5525 {
5526         if (_session) {
5527                 transform_regions(get_regions_from_selection_and_entered ());
5528         }
5529 }
5530
5531 void
5532 Editor::transform_regions (const RegionSelection& rs)
5533 {
5534         if (rs.n_midi_regions() == 0) {
5535                 return;
5536         }
5537
5538         TransformDialog td;
5539
5540         td.present();
5541         const int r = td.run();
5542         td.hide();
5543
5544         if (r == Gtk::RESPONSE_OK) {
5545                 Transform transform(td.get());
5546                 apply_midi_note_edit_op(transform, rs);
5547         }
5548 }
5549
5550 void
5551 Editor::transpose_region ()
5552 {
5553         if (_session) {
5554                 transpose_regions(get_regions_from_selection_and_entered ());
5555         }
5556 }
5557
5558 void
5559 Editor::transpose_regions (const RegionSelection& rs)
5560 {
5561         if (rs.n_midi_regions() == 0) {
5562                 return;
5563         }
5564
5565         TransposeDialog d;
5566         int const r = d.run ();
5567
5568         if (r == RESPONSE_ACCEPT) {
5569                 Transpose transpose(d.semitones ());
5570                 apply_midi_note_edit_op (transpose, rs);
5571         }
5572 }
5573
5574 void
5575 Editor::insert_patch_change (bool from_context)
5576 {
5577         RegionSelection rs = get_regions_from_selection_and_entered ();
5578
5579         if (rs.empty ()) {
5580                 return;
5581         }
5582
5583         const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5584
5585         /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5586            there may be more than one, but the PatchChangeDialog can only offer
5587            one set of patch menus.
5588         */
5589         MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5590
5591         Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5592         PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5593
5594         if (d.run() == RESPONSE_CANCEL) {
5595                 return;
5596         }
5597
5598         for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5599                 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5600                 if (mrv) {
5601                         if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5602                                 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5603                         }
5604                 }
5605         }
5606 }
5607
5608 void
5609 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5610 {
5611         RegionSelection rs = get_regions_from_selection_and_entered ();
5612
5613         if (rs.empty()) {
5614                 return;
5615         }
5616
5617         CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5618         bool in_command = false;
5619
5620         gdk_flush ();
5621
5622         int n = 0;
5623         int const N = rs.size ();
5624
5625         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5626                 RegionSelection::iterator tmp = r;
5627                 ++tmp;
5628
5629                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5630                 if (arv) {
5631                         boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5632
5633                         if (progress) {
5634                                 progress->descend (1.0 / N);
5635                         }
5636
5637                         if (arv->audio_region()->apply (filter, progress) == 0) {
5638
5639                                 playlist->clear_changes ();
5640                                 playlist->clear_owned_changes ();
5641
5642                                 if (!in_command) {
5643                                         begin_reversible_command (command);
5644                                         in_command = true;
5645                                 }
5646
5647                                 if (filter.results.empty ()) {
5648
5649                                         /* no regions returned; remove the old one */
5650                                         playlist->remove_region (arv->region ());
5651
5652                                 } else {
5653
5654                                         std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5655
5656                                         /* first region replaces the old one */
5657                                         playlist->replace_region (arv->region(), *res, (*res)->position());
5658                                         ++res;
5659
5660                                         /* add the rest */
5661                                         while (res != filter.results.end()) {
5662                                                 playlist->add_region (*res, (*res)->position());
5663                                                 ++res;
5664                                         }
5665
5666                                 }
5667
5668                                 /* We might have removed regions, which alters other regions' layering_index,
5669                                    so we need to do a recursive diff here.
5670                                 */
5671                                 vector<Command*> cmds;
5672                                 playlist->rdiff (cmds);
5673                                 _session->add_commands (cmds);
5674
5675                                 _session->add_command(new StatefulDiffCommand (playlist));
5676                         }
5677
5678                         if (progress) {
5679                                 progress->ascend ();
5680                         }
5681                 }
5682
5683                 r = tmp;
5684                 ++n;
5685         }
5686
5687         if (in_command) {
5688                 commit_reversible_command ();
5689         }
5690 }
5691
5692 void
5693 Editor::external_edit_region ()
5694 {
5695         /* more to come */
5696 }
5697
5698 void
5699 Editor::reset_region_gain_envelopes ()
5700 {
5701         RegionSelection rs = get_regions_from_selection_and_entered ();
5702
5703         if (!_session || rs.empty()) {
5704                 return;
5705         }
5706
5707         bool in_command = false;
5708
5709         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5710                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5711                 if (arv) {
5712                         boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5713                         XMLNode& before (alist->get_state());
5714
5715                         arv->audio_region()->set_default_envelope ();
5716
5717                         if (!in_command) {
5718                                 begin_reversible_command (_("reset region gain"));
5719                                 in_command = true;
5720                         }
5721                         _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5722                 }
5723         }
5724
5725         if (in_command) {
5726                 commit_reversible_command ();
5727         }
5728 }
5729
5730 void
5731 Editor::set_region_gain_visibility (RegionView* rv)
5732 {
5733         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5734         if (arv) {
5735                 arv->update_envelope_visibility();
5736         }
5737 }
5738
5739 void
5740 Editor::set_gain_envelope_visibility ()
5741 {
5742         if (!_session) {
5743                 return;
5744         }
5745
5746         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5747                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5748                 if (v) {
5749                         v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5750                 }
5751         }
5752 }
5753
5754 void
5755 Editor::toggle_gain_envelope_active ()
5756 {
5757         if (_ignore_region_action) {
5758                 return;
5759         }
5760
5761         RegionSelection rs = get_regions_from_selection_and_entered ();
5762
5763         if (!_session || rs.empty()) {
5764                 return;
5765         }
5766
5767         bool in_command = false;
5768
5769         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5770                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5771                 if (arv) {
5772                         arv->region()->clear_changes ();
5773                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5774
5775                         if (!in_command) {
5776                                 begin_reversible_command (_("region gain envelope active"));
5777                                 in_command = true;
5778                         }
5779                         _session->add_command (new StatefulDiffCommand (arv->region()));
5780                 }
5781         }
5782
5783         if (in_command) {
5784                 commit_reversible_command ();
5785         }
5786 }
5787
5788 void
5789 Editor::toggle_region_lock ()
5790 {
5791         if (_ignore_region_action) {
5792                 return;
5793         }
5794
5795         RegionSelection rs = get_regions_from_selection_and_entered ();
5796
5797         if (!_session || rs.empty()) {
5798                 return;
5799         }
5800
5801         begin_reversible_command (_("toggle region lock"));
5802
5803         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5804                 (*i)->region()->clear_changes ();
5805                 (*i)->region()->set_locked (!(*i)->region()->locked());
5806                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5807         }
5808
5809         commit_reversible_command ();
5810 }
5811
5812 void
5813 Editor::toggle_region_video_lock ()
5814 {
5815         if (_ignore_region_action) {
5816                 return;
5817         }
5818
5819         RegionSelection rs = get_regions_from_selection_and_entered ();
5820
5821         if (!_session || rs.empty()) {
5822                 return;
5823         }
5824
5825         begin_reversible_command (_("Toggle Video Lock"));
5826
5827         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5828                 (*i)->region()->clear_changes ();
5829                 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5830                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5831         }
5832
5833         commit_reversible_command ();
5834 }
5835
5836 void
5837 Editor::toggle_region_lock_style ()
5838 {
5839         if (_ignore_region_action) {
5840                 return;
5841         }
5842
5843         RegionSelection rs = get_regions_from_selection_and_entered ();
5844
5845         if (!_session || rs.empty()) {
5846                 return;
5847         }
5848
5849         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5850         vector<Widget*> proxies = a->get_proxies();
5851         Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5852
5853         assert (cmi);
5854
5855         begin_reversible_command (_("toggle region lock style"));
5856
5857         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5858                 (*i)->region()->clear_changes ();
5859                 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5860                 (*i)->region()->set_position_lock_style (ns);
5861                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5862         }
5863
5864         commit_reversible_command ();
5865 }
5866
5867 void
5868 Editor::toggle_opaque_region ()
5869 {
5870         if (_ignore_region_action) {
5871                 return;
5872         }
5873
5874         RegionSelection rs = get_regions_from_selection_and_entered ();
5875
5876         if (!_session || rs.empty()) {
5877                 return;
5878         }
5879
5880         begin_reversible_command (_("change region opacity"));
5881
5882         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5883                 (*i)->region()->clear_changes ();
5884                 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5885                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5886         }
5887
5888         commit_reversible_command ();
5889 }
5890
5891 void
5892 Editor::toggle_record_enable ()
5893 {
5894         bool new_state = false;
5895         bool first = true;
5896         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5897                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5898                 if (!rtav)
5899                         continue;
5900                 if (!rtav->is_track())
5901                         continue;
5902
5903                 if (first) {
5904                         new_state = !rtav->track()->rec_enable_control()->get_value();
5905                         first = false;
5906                 }
5907
5908                 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5909         }
5910 }
5911
5912 void
5913 Editor::toggle_solo ()
5914 {
5915         bool new_state = false;
5916         bool first = true;
5917         boost::shared_ptr<ControlList> cl (new ControlList);
5918
5919         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5920                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5921
5922                 if (!rtav) {
5923                         continue;
5924                 }
5925
5926                 if (first) {
5927                         new_state = !rtav->route()->soloed ();
5928                         first = false;
5929                 }
5930
5931                 cl->push_back (rtav->route()->solo_control());
5932         }
5933
5934         _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5935 }
5936
5937 void
5938 Editor::toggle_mute ()
5939 {
5940         bool new_state = false;
5941         bool first = true;
5942         boost::shared_ptr<RouteList> rl (new RouteList);
5943
5944         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5945                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5946
5947                 if (!rtav) {
5948                         continue;
5949                 }
5950
5951                 if (first) {
5952                         new_state = !rtav->route()->muted();
5953                         first = false;
5954                 }
5955
5956                 rl->push_back (rtav->route());
5957         }
5958
5959         _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5960 }
5961
5962 void
5963 Editor::toggle_solo_isolate ()
5964 {
5965 }
5966
5967
5968 void
5969 Editor::fade_range ()
5970 {
5971         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5972
5973         begin_reversible_command (_("fade range"));
5974
5975         for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5976                 (*i)->fade_range (selection->time);
5977         }
5978
5979         commit_reversible_command ();
5980 }
5981
5982
5983 void
5984 Editor::set_fade_length (bool in)
5985 {
5986         RegionSelection rs = get_regions_from_selection_and_entered ();
5987
5988         if (rs.empty()) {
5989                 return;
5990         }
5991
5992         /* we need a region to measure the offset from the start */
5993
5994         RegionView* rv = rs.front ();
5995
5996         framepos_t pos = get_preferred_edit_position();
5997         framepos_t len;
5998         char const * cmd;
5999
6000         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
6001                 /* edit point is outside the relevant region */
6002                 return;
6003         }
6004
6005         if (in) {
6006                 if (pos <= rv->region()->position()) {
6007                         /* can't do it */
6008                         return;
6009                 }
6010                 len = pos - rv->region()->position();
6011                 cmd = _("set fade in length");
6012         } else {
6013                 if (pos >= rv->region()->last_frame()) {
6014                         /* can't do it */
6015                         return;
6016                 }
6017                 len = rv->region()->last_frame() - pos;
6018                 cmd = _("set fade out length");
6019         }
6020
6021         bool in_command = false;
6022
6023         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6024                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6025
6026                 if (!tmp) {
6027                         continue;
6028                 }
6029
6030                 boost::shared_ptr<AutomationList> alist;
6031                 if (in) {
6032                         alist = tmp->audio_region()->fade_in();
6033                 } else {
6034                         alist = tmp->audio_region()->fade_out();
6035                 }
6036
6037                 XMLNode &before = alist->get_state();
6038
6039                 if (in) {
6040                         tmp->audio_region()->set_fade_in_length (len);
6041                         tmp->audio_region()->set_fade_in_active (true);
6042                 } else {
6043                         tmp->audio_region()->set_fade_out_length (len);
6044                         tmp->audio_region()->set_fade_out_active (true);
6045                 }
6046
6047                 if (!in_command) {
6048                         begin_reversible_command (cmd);
6049                         in_command = true;
6050                 }
6051                 XMLNode &after = alist->get_state();
6052                 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6053         }
6054
6055         if (in_command) {
6056                 commit_reversible_command ();
6057         }
6058 }
6059
6060 void
6061 Editor::set_fade_in_shape (FadeShape shape)
6062 {
6063         RegionSelection rs = get_regions_from_selection_and_entered ();
6064
6065         if (rs.empty()) {
6066                 return;
6067         }
6068         bool in_command = false;
6069
6070         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6071                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6072
6073                 if (!tmp) {
6074                         continue;
6075                 }
6076
6077                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6078                 XMLNode &before = alist->get_state();
6079
6080                 tmp->audio_region()->set_fade_in_shape (shape);
6081
6082                 if (!in_command) {
6083                         begin_reversible_command (_("set fade in shape"));
6084                         in_command = true;
6085                 }
6086                 XMLNode &after = alist->get_state();
6087                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6088         }
6089
6090         if (in_command) {
6091                 commit_reversible_command ();
6092         }
6093 }
6094
6095 void
6096 Editor::set_fade_out_shape (FadeShape shape)
6097 {
6098         RegionSelection rs = get_regions_from_selection_and_entered ();
6099
6100         if (rs.empty()) {
6101                 return;
6102         }
6103         bool in_command = false;
6104
6105         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6106                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6107
6108                 if (!tmp) {
6109                         continue;
6110                 }
6111
6112                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6113                 XMLNode &before = alist->get_state();
6114
6115                 tmp->audio_region()->set_fade_out_shape (shape);
6116
6117                 if(!in_command) {
6118                         begin_reversible_command (_("set fade out shape"));
6119                         in_command = true;
6120                 }
6121                 XMLNode &after = alist->get_state();
6122                 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6123         }
6124
6125         if (in_command) {
6126                 commit_reversible_command ();
6127         }
6128 }
6129
6130 void
6131 Editor::set_fade_in_active (bool yn)
6132 {
6133         RegionSelection rs = get_regions_from_selection_and_entered ();
6134
6135         if (rs.empty()) {
6136                 return;
6137         }
6138         bool in_command = false;
6139
6140         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6141                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6142
6143                 if (!tmp) {
6144                         continue;
6145                 }
6146
6147
6148                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6149
6150                 ar->clear_changes ();
6151                 ar->set_fade_in_active (yn);
6152
6153                 if (!in_command) {
6154                         begin_reversible_command (_("set fade in active"));
6155                         in_command = true;
6156                 }
6157                 _session->add_command (new StatefulDiffCommand (ar));
6158         }
6159
6160         if (in_command) {
6161                 commit_reversible_command ();
6162         }
6163 }
6164
6165 void
6166 Editor::set_fade_out_active (bool yn)
6167 {
6168         RegionSelection rs = get_regions_from_selection_and_entered ();
6169
6170         if (rs.empty()) {
6171                 return;
6172         }
6173         bool in_command = false;
6174
6175         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6176                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6177
6178                 if (!tmp) {
6179                         continue;
6180                 }
6181
6182                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6183
6184                 ar->clear_changes ();
6185                 ar->set_fade_out_active (yn);
6186
6187                 if (!in_command) {
6188                         begin_reversible_command (_("set fade out active"));
6189                         in_command = true;
6190                 }
6191                 _session->add_command(new StatefulDiffCommand (ar));
6192         }
6193
6194         if (in_command) {
6195                 commit_reversible_command ();
6196         }
6197 }
6198
6199 void
6200 Editor::toggle_region_fades (int dir)
6201 {
6202         if (_ignore_region_action) {
6203                 return;
6204         }
6205
6206         boost::shared_ptr<AudioRegion> ar;
6207         bool yn = false;
6208
6209         RegionSelection rs = get_regions_from_selection_and_entered ();
6210
6211         if (rs.empty()) {
6212                 return;
6213         }
6214
6215         RegionSelection::iterator i;
6216         for (i = rs.begin(); i != rs.end(); ++i) {
6217                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6218                         if (dir == -1) {
6219                                 yn = ar->fade_out_active ();
6220                         } else {
6221                                 yn = ar->fade_in_active ();
6222                         }
6223                         break;
6224                 }
6225         }
6226
6227         if (i == rs.end()) {
6228                 return;
6229         }
6230
6231         /* XXX should this undo-able? */
6232         bool in_command = false;
6233
6234         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6235                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6236                         continue;
6237                 }
6238                 ar->clear_changes ();
6239
6240                 if (dir == 1 || dir == 0) {
6241                         ar->set_fade_in_active (!yn);
6242                 }
6243
6244                 if (dir == -1 || dir == 0) {
6245                         ar->set_fade_out_active (!yn);
6246                 }
6247                 if (!in_command) {
6248                         begin_reversible_command (_("toggle fade active"));
6249                         in_command = true;
6250                 }
6251                 _session->add_command(new StatefulDiffCommand (ar));
6252         }
6253
6254         if (in_command) {
6255                 commit_reversible_command ();
6256         }
6257 }
6258
6259
6260 /** Update region fade visibility after its configuration has been changed */
6261 void
6262 Editor::update_region_fade_visibility ()
6263 {
6264         bool _fade_visibility = _session->config.get_show_region_fades ();
6265
6266         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6267                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6268                 if (v) {
6269                         if (_fade_visibility) {
6270                                 v->audio_view()->show_all_fades ();
6271                         } else {
6272                                 v->audio_view()->hide_all_fades ();
6273                         }
6274                 }
6275         }
6276 }
6277
6278 void
6279 Editor::set_edit_point ()
6280 {
6281         bool ignored;
6282         MusicFrame where (0, 0);
6283
6284         if (!mouse_frame (where.frame, ignored)) {
6285                 return;
6286         }
6287
6288         snap_to (where);
6289
6290         if (selection->markers.empty()) {
6291
6292                 mouse_add_new_marker (where.frame);
6293
6294         } else {
6295                 bool ignored;
6296
6297                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6298
6299                 if (loc) {
6300                         loc->move_to (where.frame, where.division);
6301                 }
6302         }
6303 }
6304
6305 void
6306 Editor::set_playhead_cursor ()
6307 {
6308         if (entered_marker) {
6309                 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6310         } else {
6311                 MusicFrame where (0, 0);
6312                 bool ignored;
6313
6314                 if (!mouse_frame (where.frame, ignored)) {
6315                         return;
6316                 }
6317
6318                 snap_to (where);
6319
6320                 if (_session) {
6321                         _session->request_locate (where.frame, _session->transport_rolling());
6322                 }
6323         }
6324
6325 //not sure what this was for;  remove it for now.
6326 //      if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6327 //              cancel_time_selection();
6328 //      }
6329
6330 }
6331
6332 void
6333 Editor::split_region ()
6334 {
6335         if (_drags->active ()) {
6336                 return;
6337         }
6338
6339         //if a range is selected, separate it
6340         if ( !selection->time.empty()) {
6341                 separate_regions_between (selection->time);
6342                 return;
6343         }
6344
6345         //if no range was selected, try to find some regions to split
6346         if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6347
6348                 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6349                 const framepos_t pos = get_preferred_edit_position();
6350                 const int32_t division = get_grid_music_divisions (0);
6351                 MusicFrame where (pos, division);
6352
6353                 if (rs.empty()) {
6354                         return;
6355                 }
6356
6357                 split_regions_at (where, rs);
6358
6359         }
6360 }
6361
6362 void
6363 Editor::select_next_route()
6364 {
6365         if (selection->tracks.empty()) {
6366                 selection->set (track_views.front());
6367                 return;
6368         }
6369
6370         TimeAxisView* current = selection->tracks.front();
6371
6372         RouteUI *rui;
6373         do {
6374                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6375
6376                         if (*i == current) {
6377                                 ++i;
6378                                 if (i != track_views.end()) {
6379                                         current = (*i);
6380                                 } else {
6381                                         current = (*(track_views.begin()));
6382                                         //selection->set (*(track_views.begin()));
6383                                 }
6384                                 break;
6385                         }
6386                 }
6387
6388                 rui = dynamic_cast<RouteUI *>(current);
6389
6390         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6391
6392         selection->set (current);
6393
6394         ensure_time_axis_view_is_visible (*current, false);
6395 }
6396
6397 void
6398 Editor::select_prev_route()
6399 {
6400         if (selection->tracks.empty()) {
6401                 selection->set (track_views.front());
6402                 return;
6403         }
6404
6405         TimeAxisView* current = selection->tracks.front();
6406
6407         RouteUI *rui;
6408         do {
6409                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6410
6411                         if (*i == current) {
6412                                 ++i;
6413                                 if (i != track_views.rend()) {
6414                                         current = (*i);
6415                                 } else {
6416                                         current = *(track_views.rbegin());
6417                                 }
6418                                 break;
6419                         }
6420                 }
6421                 rui = dynamic_cast<RouteUI *>(current);
6422
6423         } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6424
6425         selection->set (current);
6426
6427         ensure_time_axis_view_is_visible (*current, false);
6428 }
6429
6430 void
6431 Editor::set_loop_from_selection (bool play)
6432 {
6433         if (_session == 0) {
6434                 return;
6435         }
6436
6437         framepos_t start, end;
6438         if (!get_selection_extents ( start, end))
6439                 return;
6440
6441         set_loop_range (start, end,  _("set loop range from selection"));
6442
6443         if (play) {
6444                 _session->request_play_loop (true, true);
6445         }
6446 }
6447
6448 void
6449 Editor::set_loop_from_region (bool play)
6450 {
6451         framepos_t start, end;
6452         if (!get_selection_extents ( start, end))
6453                 return;
6454
6455         set_loop_range (start, end, _("set loop range from region"));
6456
6457         if (play) {
6458                 _session->request_locate (start, true);
6459                 _session->request_play_loop (true);
6460         }
6461 }
6462
6463 void
6464 Editor::set_punch_from_selection ()
6465 {
6466         if (_session == 0) {
6467                 return;
6468         }
6469
6470         framepos_t start, end;
6471         if (!get_selection_extents ( start, end))
6472                 return;
6473
6474         set_punch_range (start, end,  _("set punch range from selection"));
6475 }
6476
6477 void
6478 Editor::set_auto_punch_range ()
6479 {
6480         // auto punch in/out button from a single button
6481         // If Punch In is unset, set punch range from playhead to end, enable punch in
6482         // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6483         //   rewound beyond the Punch In marker, in which case that marker will be moved back
6484         //   to the current playhead position.
6485         // If punch out is set, it clears the punch range and Punch In/Out buttons
6486
6487         if (_session == 0) {
6488                 return;
6489         }
6490
6491         Location* tpl = transport_punch_location();
6492         framepos_t now = playhead_cursor->current_frame();
6493         framepos_t begin = now;
6494         framepos_t end = _session->current_end_frame();
6495
6496         if (!_session->config.get_punch_in()) {
6497                 // First Press - set punch in and create range from here to eternity
6498                 set_punch_range (begin, end, _("Auto Punch In"));
6499                 _session->config.set_punch_in(true);
6500         } else if (tpl && !_session->config.get_punch_out()) {
6501                 // Second press - update end range marker and set punch_out
6502                 if (now < tpl->start()) {
6503                         // playhead has been rewound - move start back  and pretend nothing happened
6504                         begin = now;
6505                         set_punch_range (begin, end, _("Auto Punch In/Out"));
6506                 } else {
6507                         // normal case for 2nd press - set the punch out
6508                         end = playhead_cursor->current_frame ();
6509                         set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6510                         _session->config.set_punch_out(true);
6511                 }
6512         } else  {
6513                 if (_session->config.get_punch_out()) {
6514                         _session->config.set_punch_out(false);
6515                 }
6516
6517                 if (_session->config.get_punch_in()) {
6518                         _session->config.set_punch_in(false);
6519                 }
6520
6521                 if (tpl)
6522                 {
6523                         // third press - unset punch in/out and remove range
6524                         _session->locations()->remove(tpl);
6525                 }
6526         }
6527
6528 }
6529
6530 void
6531 Editor::set_session_extents_from_selection ()
6532 {
6533         if (_session == 0) {
6534                 return;
6535         }
6536
6537         framepos_t start, end;
6538         if (!get_selection_extents ( start, end))
6539                 return;
6540
6541         Location* loc;
6542         if ((loc = _session->locations()->session_range_location()) == 0) {
6543                 _session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6544         } else {
6545                 XMLNode &before = loc->get_state();
6546
6547                 _session->set_session_extents (start, end);
6548
6549                 XMLNode &after = loc->get_state();
6550
6551                 begin_reversible_command (_("set session start/end from selection"));
6552
6553                 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6554
6555                 commit_reversible_command ();
6556         }
6557
6558         _session->set_end_is_free (false);
6559 }
6560
6561 void
6562 Editor::set_punch_start_from_edit_point ()
6563 {
6564         if (_session) {
6565
6566                 MusicFrame start (0, 0);
6567                 framepos_t end = max_framepos;
6568
6569                 //use the existing punch end, if any
6570                 Location* tpl = transport_punch_location();
6571                 if (tpl) {
6572                         end = tpl->end();
6573                 }
6574
6575                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6576                         start.frame = _session->audible_frame();
6577                 } else {
6578                         start.frame = get_preferred_edit_position();
6579                 }
6580
6581                 //snap the selection start/end
6582                 snap_to(start);
6583
6584                 //if there's not already a sensible selection endpoint, go "forever"
6585                 if (start.frame > end ) {
6586                         end = max_framepos;
6587                 }
6588
6589                 set_punch_range (start.frame, end, _("set punch start from EP"));
6590         }
6591
6592 }
6593
6594 void
6595 Editor::set_punch_end_from_edit_point ()
6596 {
6597         if (_session) {
6598
6599                 framepos_t start = 0;
6600                 MusicFrame end (max_framepos, 0);
6601
6602                 //use the existing punch start, if any
6603                 Location* tpl = transport_punch_location();
6604                 if (tpl) {
6605                         start = tpl->start();
6606                 }
6607
6608                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6609                         end.frame = _session->audible_frame();
6610                 } else {
6611                         end.frame = get_preferred_edit_position();
6612                 }
6613
6614                 //snap the selection start/end
6615                 snap_to (end);
6616
6617                 set_punch_range (start, end.frame, _("set punch end from EP"));
6618
6619         }
6620 }
6621
6622 void
6623 Editor::set_loop_start_from_edit_point ()
6624 {
6625         if (_session) {
6626
6627                 MusicFrame start (0, 0);
6628                 framepos_t end = max_framepos;
6629
6630                 //use the existing loop end, if any
6631                 Location* tpl = transport_loop_location();
6632                 if (tpl) {
6633                         end = tpl->end();
6634                 }
6635
6636                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6637                         start.frame = _session->audible_frame();
6638                 } else {
6639                         start.frame = get_preferred_edit_position();
6640                 }
6641
6642                 //snap the selection start/end
6643                 snap_to (start);
6644
6645                 //if there's not already a sensible selection endpoint, go "forever"
6646                 if (start.frame > end ) {
6647                         end = max_framepos;
6648                 }
6649
6650                 set_loop_range (start.frame, end, _("set loop start from EP"));
6651         }
6652
6653 }
6654
6655 void
6656 Editor::set_loop_end_from_edit_point ()
6657 {
6658         if (_session) {
6659
6660                 framepos_t start = 0;
6661                 MusicFrame end (max_framepos, 0);
6662
6663                 //use the existing loop start, if any
6664                 Location* tpl = transport_loop_location();
6665                 if (tpl) {
6666                         start = tpl->start();
6667                 }
6668
6669                 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6670                         end.frame = _session->audible_frame();
6671                 } else {
6672                         end.frame = get_preferred_edit_position();
6673                 }
6674
6675                 //snap the selection start/end
6676                 snap_to(end);
6677
6678                 set_loop_range (start, end.frame, _("set loop end from EP"));
6679         }
6680 }
6681
6682 void
6683 Editor::set_punch_from_region ()
6684 {
6685         framepos_t start, end;
6686         if (!get_selection_extents ( start, end))
6687                 return;
6688
6689         set_punch_range (start, end, _("set punch range from region"));
6690 }
6691
6692 void
6693 Editor::pitch_shift_region ()
6694 {
6695         RegionSelection rs = get_regions_from_selection_and_entered ();
6696
6697         RegionSelection audio_rs;
6698         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6699                 if (dynamic_cast<AudioRegionView*> (*i)) {
6700                         audio_rs.push_back (*i);
6701                 }
6702         }
6703
6704         if (audio_rs.empty()) {
6705                 return;
6706         }
6707
6708         pitch_shift (audio_rs, 1.2);
6709 }
6710
6711 void
6712 Editor::set_tempo_from_region ()
6713 {
6714         RegionSelection rs = get_regions_from_selection_and_entered ();
6715
6716         if (!_session || rs.empty()) {
6717                 return;
6718         }
6719
6720         RegionView* rv = rs.front();
6721
6722         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6723 }
6724
6725 void
6726 Editor::use_range_as_bar ()
6727 {
6728         framepos_t start, end;
6729         if (get_edit_op_range (start, end)) {
6730                 define_one_bar (start, end);
6731         }
6732 }
6733
6734 void
6735 Editor::define_one_bar (framepos_t start, framepos_t end)
6736 {
6737         framepos_t length = end - start;
6738
6739         const Meter& m (_session->tempo_map().meter_at_frame (start));
6740
6741         /* length = 1 bar */
6742
6743         /* We're going to deliver a constant tempo here,
6744            so we can use frames per beat to determine length.
6745            now we want frames per beat.
6746            we have frames per bar, and beats per bar, so ...
6747         */
6748
6749         /* XXXX METER MATH */
6750
6751         double frames_per_beat = length / m.divisions_per_bar();
6752
6753         /* beats per minute = */
6754
6755         double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6756
6757         /* now decide whether to:
6758
6759             (a) set global tempo
6760             (b) add a new tempo marker
6761
6762         */
6763
6764         const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6765
6766         bool do_global = false;
6767
6768         if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6769
6770                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6771                    at the start, or create a new marker
6772                 */
6773
6774                 vector<string> options;
6775                 options.push_back (_("Cancel"));
6776                 options.push_back (_("Add new marker"));
6777                 options.push_back (_("Set global tempo"));
6778
6779                 Choice c (
6780                         _("Define one bar"),
6781                         _("Do you want to set the global tempo or add a new tempo marker?"),
6782                         options
6783                         );
6784
6785                 c.set_default_response (2);
6786
6787                 switch (c.run()) {
6788                 case 0:
6789                         return;
6790
6791                 case 2:
6792                         do_global = true;
6793                         break;
6794
6795                 default:
6796                         do_global = false;
6797                 }
6798
6799         } else {
6800
6801                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6802                    if the marker is at the region starter, change it, otherwise add
6803                    a new tempo marker
6804                 */
6805         }
6806
6807         begin_reversible_command (_("set tempo from region"));
6808         XMLNode& before (_session->tempo_map().get_state());
6809
6810         if (do_global) {
6811                 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6812         } else if (t.frame() == start) {
6813                 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6814         } else {
6815                 /* constant tempo */
6816                 const Tempo tempo (beats_per_minute, t.note_type());
6817                 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6818         }
6819
6820         XMLNode& after (_session->tempo_map().get_state());
6821
6822         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6823         commit_reversible_command ();
6824 }
6825
6826 void
6827 Editor::split_region_at_transients ()
6828 {
6829         AnalysisFeatureList positions;
6830
6831         RegionSelection rs = get_regions_from_selection_and_entered ();
6832
6833         if (!_session || rs.empty()) {
6834                 return;
6835         }
6836
6837         begin_reversible_command (_("split regions"));
6838
6839         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6840
6841                 RegionSelection::iterator tmp;
6842
6843                 tmp = i;
6844                 ++tmp;
6845
6846                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6847
6848                 if (ar) {
6849                         ar->transients (positions);
6850                         split_region_at_points ((*i)->region(), positions, true);
6851                         positions.clear ();
6852                 }
6853
6854                 i = tmp;
6855         }
6856
6857         commit_reversible_command ();
6858
6859 }
6860
6861 void
6862 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6863 {
6864         bool use_rhythmic_rodent = false;
6865
6866         boost::shared_ptr<Playlist> pl = r->playlist();
6867
6868         list<boost::shared_ptr<Region> > new_regions;
6869
6870         if (!pl) {
6871                 return;
6872         }
6873
6874         if (positions.empty()) {
6875                 return;
6876         }
6877
6878         if (positions.size() > 20 && can_ferret) {
6879                 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);
6880                 MessageDialog msg (msgstr,
6881                                    false,
6882                                    Gtk::MESSAGE_INFO,
6883                                    Gtk::BUTTONS_OK_CANCEL);
6884
6885                 if (can_ferret) {
6886                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6887                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6888                 } else {
6889                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
6890                 }
6891
6892                 msg.set_title (_("Excessive split?"));
6893                 msg.present ();
6894
6895                 int response = msg.run();
6896                 msg.hide ();
6897
6898                 switch (response) {
6899                 case RESPONSE_OK:
6900                         break;
6901                 case RESPONSE_APPLY:
6902                         use_rhythmic_rodent = true;
6903                         break;
6904                 default:
6905                         return;
6906                 }
6907         }
6908
6909         if (use_rhythmic_rodent) {
6910                 show_rhythm_ferret ();
6911                 return;
6912         }
6913
6914         AnalysisFeatureList::const_iterator x;
6915
6916         pl->clear_changes ();
6917         pl->clear_owned_changes ();
6918
6919         x = positions.begin();
6920
6921         if (x == positions.end()) {
6922                 return;
6923         }
6924
6925         pl->freeze ();
6926         pl->remove_region (r);
6927
6928         framepos_t pos = 0;
6929
6930         framepos_t rstart = r->first_frame ();
6931         framepos_t rend = r->last_frame ();
6932
6933         while (x != positions.end()) {
6934
6935                 /* deal with positons that are out of scope of present region bounds */
6936                 if (*x <= rstart || *x > rend) {
6937                         ++x;
6938                         continue;
6939                 }
6940
6941                 /* file start = original start + how far we from the initial position ?  */
6942
6943                 framepos_t file_start = r->start() + pos;
6944
6945                 /* length = next position - current position */
6946
6947                 framepos_t len = (*x) - pos - rstart;
6948
6949                 /* XXX we do we really want to allow even single-sample regions?
6950                  * shouldn't we have some kind of lower limit on region size?
6951                  */
6952
6953                 if (len <= 0) {
6954                         break;
6955                 }
6956
6957                 string new_name;
6958
6959                 if (RegionFactory::region_name (new_name, r->name())) {
6960                         break;
6961                 }
6962
6963                 /* do NOT announce new regions 1 by one, just wait till they are all done */
6964
6965                 PropertyList plist;
6966
6967                 plist.add (ARDOUR::Properties::start, file_start);
6968                 plist.add (ARDOUR::Properties::length, len);
6969                 plist.add (ARDOUR::Properties::name, new_name);
6970                 plist.add (ARDOUR::Properties::layer, 0);
6971                 // TODO set transients_offset
6972
6973                 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6974                 /* because we set annouce to false, manually add the new region to the
6975                  * RegionFactory map
6976                  */
6977                 RegionFactory::map_add (nr);
6978
6979                 pl->add_region (nr, rstart + pos);
6980
6981                 if (select_new) {
6982                         new_regions.push_front(nr);
6983                 }
6984
6985                 pos += len;
6986                 ++x;
6987         }
6988
6989         string new_name;
6990
6991         RegionFactory::region_name (new_name, r->name());
6992
6993         /* Add the final region */
6994         PropertyList plist;
6995
6996         plist.add (ARDOUR::Properties::start, r->start() + pos);
6997         plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6998         plist.add (ARDOUR::Properties::name, new_name);
6999         plist.add (ARDOUR::Properties::layer, 0);
7000
7001         boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7002         /* because we set annouce to false, manually add the new region to the
7003            RegionFactory map
7004         */
7005         RegionFactory::map_add (nr);
7006         pl->add_region (nr, r->position() + pos);
7007
7008         if (select_new) {
7009                 new_regions.push_front(nr);
7010         }
7011
7012         pl->thaw ();
7013
7014         /* We might have removed regions, which alters other regions' layering_index,
7015            so we need to do a recursive diff here.
7016         */
7017         vector<Command*> cmds;
7018         pl->rdiff (cmds);
7019         _session->add_commands (cmds);
7020
7021         _session->add_command (new StatefulDiffCommand (pl));
7022
7023         if (select_new) {
7024
7025                 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7026                         set_selected_regionview_from_region_list ((*i), Selection::Add);
7027                 }
7028         }
7029 }
7030
7031 void
7032 Editor::place_transient()
7033 {
7034         if (!_session) {
7035                 return;
7036         }
7037
7038         RegionSelection rs = get_regions_from_selection_and_edit_point ();
7039
7040         if (rs.empty()) {
7041                 return;
7042         }
7043
7044         framepos_t where = get_preferred_edit_position();
7045
7046         begin_reversible_command (_("place transient"));
7047
7048         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7049                 (*r)->region()->add_transient(where);
7050         }
7051
7052         commit_reversible_command ();
7053 }
7054
7055 void
7056 Editor::remove_transient(ArdourCanvas::Item* item)
7057 {
7058         if (!_session) {
7059                 return;
7060         }
7061
7062         ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7063         assert (_line);
7064
7065         AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7066         _arv->remove_transient (*(float*) _line->get_data ("position"));
7067 }
7068
7069 void
7070 Editor::snap_regions_to_grid ()
7071 {
7072         list <boost::shared_ptr<Playlist > > used_playlists;
7073
7074         RegionSelection rs = get_regions_from_selection_and_entered ();
7075
7076         if (!_session || rs.empty()) {
7077                 return;
7078         }
7079
7080         begin_reversible_command (_("snap regions to grid"));
7081
7082         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7083
7084                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7085
7086                 if (!pl->frozen()) {
7087                         /* we haven't seen this playlist before */
7088
7089                         /* remember used playlists so we can thaw them later */
7090                         used_playlists.push_back(pl);
7091                         pl->freeze();
7092                 }
7093                 (*r)->region()->clear_changes ();
7094
7095                 MusicFrame start ((*r)->region()->first_frame (), 0);
7096                 snap_to (start);
7097                 (*r)->region()->set_position (start.frame, start.division);
7098                 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7099         }
7100
7101         while (used_playlists.size() > 0) {
7102                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7103                 (*i)->thaw();
7104                 used_playlists.pop_front();
7105         }
7106
7107         commit_reversible_command ();
7108 }
7109
7110 void
7111 Editor::close_region_gaps ()
7112 {
7113         list <boost::shared_ptr<Playlist > > used_playlists;
7114
7115         RegionSelection rs = get_regions_from_selection_and_entered ();
7116
7117         if (!_session || rs.empty()) {
7118                 return;
7119         }
7120
7121         Dialog dialog (_("Close Region Gaps"));
7122
7123         Table table (2, 3);
7124         table.set_spacings (12);
7125         table.set_border_width (12);
7126         Label* l = manage (left_aligned_label (_("Crossfade length")));
7127         table.attach (*l, 0, 1, 0, 1);
7128
7129         SpinButton spin_crossfade (1, 0);
7130         spin_crossfade.set_range (0, 15);
7131         spin_crossfade.set_increments (1, 1);
7132         spin_crossfade.set_value (5);
7133         table.attach (spin_crossfade, 1, 2, 0, 1);
7134
7135         table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7136
7137         l = manage (left_aligned_label (_("Pull-back length")));
7138         table.attach (*l, 0, 1, 1, 2);
7139
7140         SpinButton spin_pullback (1, 0);
7141         spin_pullback.set_range (0, 100);
7142         spin_pullback.set_increments (1, 1);
7143         spin_pullback.set_value(30);
7144         table.attach (spin_pullback, 1, 2, 1, 2);
7145
7146         table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7147
7148         dialog.get_vbox()->pack_start (table);
7149         dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7150         dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7151         dialog.show_all ();
7152
7153         if (dialog.run () == RESPONSE_CANCEL) {
7154                 return;
7155         }
7156
7157         framepos_t crossfade_len = spin_crossfade.get_value();
7158         framepos_t pull_back_frames = spin_pullback.get_value();
7159
7160         crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7161         pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7162
7163         /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7164
7165         begin_reversible_command (_("close region gaps"));
7166
7167         int idx = 0;
7168         boost::shared_ptr<Region> last_region;
7169
7170         rs.sort_by_position_and_track();
7171
7172         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7173
7174                 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7175
7176                 if (!pl->frozen()) {
7177                         /* we haven't seen this playlist before */
7178
7179                         /* remember used playlists so we can thaw them later */
7180                         used_playlists.push_back(pl);
7181                         pl->freeze();
7182                 }
7183
7184                 framepos_t position = (*r)->region()->position();
7185
7186                 if (idx == 0 || position < last_region->position()){
7187                         last_region = (*r)->region();
7188                         idx++;
7189                         continue;
7190                 }
7191
7192                 (*r)->region()->clear_changes ();
7193                 (*r)->region()->trim_front( (position - pull_back_frames));
7194
7195                 last_region->clear_changes ();
7196                 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7197
7198                 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7199                 _session->add_command (new StatefulDiffCommand (last_region));
7200
7201                 last_region = (*r)->region();
7202                 idx++;
7203         }
7204
7205         while (used_playlists.size() > 0) {
7206                 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7207                 (*i)->thaw();
7208                 used_playlists.pop_front();
7209         }
7210
7211         commit_reversible_command ();
7212 }
7213
7214 void
7215 Editor::tab_to_transient (bool forward)
7216 {
7217         AnalysisFeatureList positions;
7218
7219         RegionSelection rs = get_regions_from_selection_and_entered ();
7220
7221         if (!_session) {
7222                 return;
7223         }
7224
7225         framepos_t pos = _session->audible_frame ();
7226
7227         if (!selection->tracks.empty()) {
7228
7229                 /* don't waste time searching for transients in duplicate playlists.
7230                  */
7231
7232                 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7233
7234                 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7235
7236                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7237
7238                         if (rtv) {
7239                                 boost::shared_ptr<Track> tr = rtv->track();
7240                                 if (tr) {
7241                                         boost::shared_ptr<Playlist> pl = tr->playlist ();
7242                                         if (pl) {
7243                                                 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7244
7245                                                 if (result >= 0) {
7246                                                         positions.push_back (result);
7247                                                 }
7248                                         }
7249                                 }
7250                         }
7251                 }
7252
7253         } else {
7254
7255                 if (rs.empty()) {
7256                         return;
7257                 }
7258
7259                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7260                         (*r)->region()->get_transients (positions);
7261                 }
7262         }
7263
7264         TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7265
7266         if (forward) {
7267                 AnalysisFeatureList::iterator x;
7268
7269                 for (x = positions.begin(); x != positions.end(); ++x) {
7270                         if ((*x) > pos) {
7271                                 break;
7272                         }
7273                 }
7274
7275                 if (x != positions.end ()) {
7276                         _session->request_locate (*x);
7277                 }
7278
7279         } else {
7280                 AnalysisFeatureList::reverse_iterator x;
7281
7282                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7283                         if ((*x) < pos) {
7284                                 break;
7285                         }
7286                 }
7287
7288                 if (x != positions.rend ()) {
7289                         _session->request_locate (*x);
7290                 }
7291         }
7292 }
7293
7294 void
7295 Editor::playhead_forward_to_grid ()
7296 {
7297         if (!_session) {
7298                 return;
7299         }
7300
7301         MusicFrame pos (playhead_cursor->current_frame (), 0);
7302
7303         if (pos.frame < max_framepos - 1) {
7304                 pos.frame += 2;
7305                 snap_to_internal (pos, RoundUpAlways, false, true);
7306                 _session->request_locate (pos.frame);
7307         }
7308 }
7309
7310
7311 void
7312 Editor::playhead_backward_to_grid ()
7313 {
7314         if (!_session) {
7315                 return;
7316         }
7317
7318         MusicFrame pos  (playhead_cursor->current_frame (), 0);
7319
7320         if (pos.frame > 2) {
7321                 pos.frame -= 2;
7322                 snap_to_internal (pos, RoundDownAlways, false, true);
7323                 _session->request_locate (pos.frame);
7324         }
7325 }
7326
7327 void
7328 Editor::set_track_height (Height h)
7329 {
7330         TrackSelection& ts (selection->tracks);
7331
7332         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7333                 (*x)->set_height_enum (h);
7334         }
7335 }
7336
7337 void
7338 Editor::toggle_tracks_active ()
7339 {
7340         TrackSelection& ts (selection->tracks);
7341         bool first = true;
7342         bool target = false;
7343
7344         if (ts.empty()) {
7345                 return;
7346         }
7347
7348         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7349                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7350
7351                 if (rtv) {
7352                         if (first) {
7353                                 target = !rtv->_route->active();
7354                                 first = false;
7355                         }
7356                         rtv->_route->set_active (target, this);
7357                 }
7358         }
7359 }
7360
7361 void
7362 Editor::remove_tracks ()
7363 {
7364         /* this will delete GUI objects that may be the subject of an event
7365            handler in which this method is called. Defer actual deletion to the
7366            next idle callback, when all event handling is finished.
7367         */
7368         Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7369 }
7370
7371 bool
7372 Editor::idle_remove_tracks ()
7373 {
7374         Session::StateProtector sp (_session);
7375         _remove_tracks ();
7376         return false; /* do not call again */
7377 }
7378
7379 void
7380 Editor::_remove_tracks ()
7381 {
7382         TrackSelection& ts (selection->tracks);
7383
7384         if (ts.empty()) {
7385                 return;
7386         }
7387
7388         vector<string> choices;
7389         string prompt;
7390         int ntracks = 0;
7391         int nbusses = 0;
7392         const char* trackstr;
7393         const char* busstr;
7394         vector<boost::shared_ptr<Route> > routes;
7395         bool special_bus = false;
7396
7397         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7398                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7399                 if (!rtv) {
7400                         continue;
7401                 }
7402                 if (rtv->is_track()) {
7403                         ntracks++;
7404                 } else {
7405                         nbusses++;
7406                 }
7407                 routes.push_back (rtv->_route);
7408
7409                 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7410                         special_bus = true;
7411                 }
7412         }
7413
7414         if (special_bus && !Config->get_allow_special_bus_removal()) {
7415                 MessageDialog msg (_("That would be bad news ...."),
7416                                    false,
7417                                    Gtk::MESSAGE_INFO,
7418                                    Gtk::BUTTONS_OK);
7419                 msg.set_secondary_text (string_compose (_(
7420                                                                 "Removing the master or monitor bus is such a bad idea\n\
7421 that %1 is not going to allow it.\n\
7422 \n\
7423 If you really want to do this sort of thing\n\
7424 edit your ardour.rc file to set the\n\
7425 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7426
7427                 msg.present ();
7428                 msg.run ();
7429                 return;
7430         }
7431
7432         if (ntracks + nbusses == 0) {
7433                 return;
7434         }
7435
7436         trackstr = P_("track", "tracks", ntracks);
7437         busstr = P_("bus", "busses", nbusses);
7438
7439         if (ntracks) {
7440                 if (nbusses) {
7441                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7442                                                     "(You may also lose the playlists associated with the %2)\n\n"
7443                                                     "This action cannot be undone, and the session file will be overwritten!"),
7444                                                   ntracks, trackstr, nbusses, busstr);
7445                 } else {
7446                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
7447                                                     "(You may also lose the playlists associated with the %2)\n\n"
7448                                                     "This action cannot be undone, and the session file will be overwritten!"),
7449                                                   ntracks, trackstr);
7450                 }
7451         } else if (nbusses) {
7452                 prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
7453                                             "This action cannot be undone, and the session file will be overwritten"),
7454                                           nbusses, busstr);
7455         }
7456
7457         choices.push_back (_("No, do nothing."));
7458         if (ntracks + nbusses > 1) {
7459                 choices.push_back (_("Yes, remove them."));
7460         } else {
7461                 choices.push_back (_("Yes, remove it."));
7462         }
7463
7464         string title;
7465         if (ntracks) {
7466                 title = string_compose (_("Remove %1"), trackstr);
7467         } else {
7468                 title = string_compose (_("Remove %1"), busstr);
7469         }
7470
7471         Choice prompter (title, prompt, choices);
7472
7473         if (prompter.run () != 1) {
7474                 return;
7475         }
7476
7477         if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7478                 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7479                  * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7480                  * likely because deletion requires selection) this will call
7481                  * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7482                  * It's likewise likely that the route that has just been displayed in the
7483                  * Editor-Mixer will be next in line for deletion.
7484                  *
7485                  * So simply switch to the master-bus (if present)
7486                  */
7487                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7488                         if ((*i)->stripable ()->is_master ()) {
7489                                 set_selected_mixer_strip (*(*i));
7490                                 break;
7491                         }
7492                 }
7493         }
7494
7495         {
7496                 PresentationInfo::ChangeSuspender cs;
7497                 DisplaySuspender ds;
7498
7499                 boost::shared_ptr<RouteList> rl (new RouteList);
7500                 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7501                         rl->push_back (*x);
7502                 }
7503                 _session->remove_routes (rl);
7504         }
7505         /* TrackSelection and RouteList leave scope,
7506          * destructors are called,
7507          * diskstream drops references, save_state is called (again for every track)
7508          */
7509 }
7510
7511 void
7512 Editor::do_insert_time ()
7513 {
7514         if (selection->tracks.empty()) {
7515                 return;
7516         }
7517
7518         InsertRemoveTimeDialog d (*this);
7519         int response = d.run ();
7520
7521         if (response != RESPONSE_OK) {
7522                 return;
7523         }
7524
7525         if (d.distance() == 0) {
7526                 return;
7527         }
7528
7529         insert_time (
7530                 d.position(),
7531                 d.distance(),
7532                 d.intersected_region_action (),
7533                 d.all_playlists(),
7534                 d.move_glued(),
7535                 d.move_markers(),
7536                 d.move_glued_markers(),
7537                 d.move_locked_markers(),
7538                 d.move_tempos()
7539                 );
7540 }
7541
7542 void
7543 Editor::insert_time (
7544         framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7545         bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7546         )
7547 {
7548
7549         if (Config->get_edit_mode() == Lock) {
7550                 return;
7551         }
7552         bool in_command = false;
7553
7554         TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7555
7556         for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7557
7558                 /* regions */
7559
7560                 /* don't operate on any playlist more than once, which could
7561                  * happen if "all playlists" is enabled, but there is more
7562                  * than 1 track using playlists "from" a given track.
7563                  */
7564
7565                 set<boost::shared_ptr<Playlist> > pl;
7566
7567                 if (all_playlists) {
7568                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7569                         if (rtav && rtav->track ()) {
7570                                 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7571                                 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7572                                         pl.insert (*p);
7573                                 }
7574                         }
7575                 } else {
7576                         if ((*x)->playlist ()) {
7577                                 pl.insert ((*x)->playlist ());
7578                         }
7579                 }
7580
7581                 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7582
7583                         (*i)->clear_changes ();
7584                         (*i)->clear_owned_changes ();
7585
7586                         if (!in_command) {
7587                                 begin_reversible_command (_("insert time"));
7588                                 in_command = true;
7589                         }
7590
7591                         if (opt == SplitIntersected) {
7592                                 /* non musical split */
7593                                 (*i)->split (MusicFrame (pos, 0));
7594                         }
7595
7596                         (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7597
7598                         vector<Command*> cmds;
7599                         (*i)->rdiff (cmds);
7600                         _session->add_commands (cmds);
7601
7602                         _session->add_command (new StatefulDiffCommand (*i));
7603                 }
7604
7605                 /* automation */
7606                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7607                 if (rtav) {
7608                         if (!in_command) {
7609                                 begin_reversible_command (_("insert time"));
7610                                 in_command = true;
7611                         }
7612                         rtav->route ()->shift (pos, frames);
7613                 }
7614         }
7615
7616         /* markers */
7617         if (markers_too) {
7618                 bool moved = false;
7619                 const int32_t divisions = get_grid_music_divisions (0);
7620                 XMLNode& before (_session->locations()->get_state());
7621                 Locations::LocationList copy (_session->locations()->list());
7622
7623                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7624
7625                         Locations::LocationList::const_iterator tmp;
7626
7627                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7628                                 bool const was_locked = (*i)->locked ();
7629                                 if (locked_markers_too) {
7630                                         (*i)->unlock ();
7631                                 }
7632
7633                                 if ((*i)->start() >= pos) {
7634                                         // move end first, in case we're moving by more than the length of the range
7635                                         if (!(*i)->is_mark()) {
7636                                                 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7637                                         }
7638                                         (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7639                                         moved = true;
7640                                 }
7641
7642                                 if (was_locked) {
7643                                         (*i)->lock ();
7644                                 }
7645                         }
7646                 }
7647
7648                 if (moved) {
7649                         if (!in_command) {
7650                                 begin_reversible_command (_("insert time"));
7651                                 in_command = true;
7652                         }
7653                         XMLNode& after (_session->locations()->get_state());
7654                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7655                 }
7656         }
7657
7658         if (tempo_too) {
7659                 if (!in_command) {
7660                         begin_reversible_command (_("insert time"));
7661                         in_command = true;
7662                 }
7663                 XMLNode& before (_session->tempo_map().get_state());
7664                 _session->tempo_map().insert_time (pos, frames);
7665                 XMLNode& after (_session->tempo_map().get_state());
7666                 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7667         }
7668
7669         if (in_command) {
7670                 commit_reversible_command ();
7671         }
7672 }
7673
7674 void
7675 Editor::do_remove_time ()
7676 {
7677         if (selection->tracks.empty()) {
7678                 return;
7679         }
7680
7681         InsertRemoveTimeDialog d (*this, true);
7682
7683         int response = d.run ();
7684
7685         if (response != RESPONSE_OK) {
7686                 return;
7687         }
7688
7689         framecnt_t distance = d.distance();
7690
7691         if (distance == 0) {
7692                 return;
7693         }
7694
7695         remove_time (
7696                 d.position(),
7697                 distance,
7698                 SplitIntersected,
7699                 d.move_glued(),
7700                 d.move_markers(),
7701                 d.move_glued_markers(),
7702                 d.move_locked_markers(),
7703                 d.move_tempos()
7704         );
7705 }
7706
7707 void
7708 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7709                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7710 {
7711         if (Config->get_edit_mode() == Lock) {
7712                 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7713                 return;
7714         }
7715         bool in_command = false;
7716
7717         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7718                 /* regions */
7719                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7720
7721                 if (pl) {
7722
7723                         XMLNode &before = pl->get_state();
7724
7725                         if (!in_command) {
7726                                 begin_reversible_command (_("remove time"));
7727                                 in_command = true;
7728                         }
7729
7730                         std::list<AudioRange> rl;
7731                         AudioRange ar(pos, pos+frames, 0);
7732                         rl.push_back(ar);
7733                         pl->cut (rl);
7734                         pl->shift (pos, -frames, true, ignore_music_glue);
7735
7736                         XMLNode &after = pl->get_state();
7737
7738                         _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7739                 }
7740
7741                 /* automation */
7742                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7743                 if (rtav) {
7744                         if (!in_command) {
7745                                 begin_reversible_command (_("remove time"));
7746                                 in_command = true;
7747                         }
7748                         rtav->route ()->shift (pos, -frames);
7749                 }
7750         }
7751
7752         const int32_t divisions = get_grid_music_divisions (0);
7753         std::list<Location*> loc_kill_list;
7754
7755         /* markers */
7756         if (markers_too) {
7757                 bool moved = false;
7758                 XMLNode& before (_session->locations()->get_state());
7759                 Locations::LocationList copy (_session->locations()->list());
7760
7761                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7762                         if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7763
7764                                 bool const was_locked = (*i)->locked ();
7765                                 if (locked_markers_too) {
7766                                         (*i)->unlock ();
7767                                 }
7768
7769                                 if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
7770                                         if ((*i)->end() >= pos
7771                                         && (*i)->end() < pos+frames
7772                                         && (*i)->start() >= pos
7773                                         && (*i)->end() < pos+frames) {  // range is completely enclosed;  kill it
7774                                                 moved = true;
7775                                                 loc_kill_list.push_back(*i);
7776                                         } else {  // only start or end is included, try to do the right thing
7777                                                 // move start before moving end, to avoid trying to move the end to before the start
7778                                                 // if we're removing more time than the length of the range
7779                                                 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7780                                                         // start is within cut
7781                                                         (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
7782                                                         moved = true;
7783                                                 } else if ((*i)->start() >= pos+frames) {
7784                                                         // start (and thus entire range) lies beyond end of cut
7785                                                         (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7786                                                         moved = true;
7787                                                 }
7788                                                 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7789                                                         // end is inside cut
7790                                                         (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
7791                                                         moved = true;
7792                                                 } else if ((*i)->end() >= pos+frames) {
7793                                                         // end is beyond end of cut
7794                                                         (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7795                                                         moved = true;
7796                                                 }
7797
7798                                         }
7799                                 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7800                                         loc_kill_list.push_back(*i);
7801                                         moved = true;
7802                                 } else if ((*i)->start() >= pos) {
7803                                         (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7804                                         moved = true;
7805                                 }
7806
7807                                 if (was_locked) {
7808                                         (*i)->lock ();
7809                                 }
7810                         }
7811                 }
7812
7813                 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7814                         _session->locations()->remove( *i );
7815                 }
7816
7817                 if (moved) {
7818                         if (!in_command) {
7819                                 begin_reversible_command (_("remove time"));
7820                                 in_command = true;
7821                         }
7822                         XMLNode& after (_session->locations()->get_state());
7823                         _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7824                 }
7825         }
7826
7827         if (tempo_too) {
7828                 XMLNode& before (_session->tempo_map().get_state());
7829
7830                 if (_session->tempo_map().remove_time (pos, frames) ) {
7831                         if (!in_command) {
7832                                 begin_reversible_command (_("remove time"));
7833                                 in_command = true;
7834                         }
7835                         XMLNode& after (_session->tempo_map().get_state());
7836                         _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7837                 }
7838         }
7839
7840         if (in_command) {
7841                 commit_reversible_command ();
7842         }
7843 }
7844
7845 void
7846 Editor::fit_selection ()
7847 {
7848         if (!selection->tracks.empty()) {
7849                 fit_tracks (selection->tracks);
7850         } else {
7851                 TrackViewList tvl;
7852
7853                 /* no selected tracks - use tracks with selected regions */
7854
7855                 if (!selection->regions.empty()) {
7856                         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7857                                 tvl.push_back (&(*r)->get_time_axis_view ());
7858                         }
7859
7860                         if (!tvl.empty()) {
7861                                 fit_tracks (tvl);
7862                         }
7863                 } else if (internal_editing()) {
7864                         /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7865                          * the entered track
7866                          */
7867                         if (entered_track) {
7868                                 tvl.push_back (entered_track);
7869                                 fit_tracks (tvl);
7870                         }
7871                 }
7872         }
7873 }
7874
7875 void
7876 Editor::fit_tracks (TrackViewList & tracks)
7877 {
7878         if (tracks.empty()) {
7879                 return;
7880         }
7881
7882         uint32_t child_heights = 0;
7883         int visible_tracks = 0;
7884
7885         for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7886
7887                 if (!(*t)->marked_for_display()) {
7888                         continue;
7889                 }
7890
7891                 child_heights += (*t)->effective_height() - (*t)->current_height();
7892                 ++visible_tracks;
7893         }
7894
7895         /* compute the per-track height from:
7896          *
7897          * total canvas visible height
7898          *  - height that will be taken by visible children of selected tracks
7899          *  - height of the ruler/hscroll area
7900          */
7901         uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7902         double first_y_pos = DBL_MAX;
7903
7904         if (h < TimeAxisView::preset_height (HeightSmall)) {
7905                 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7906                 /* too small to be displayed */
7907                 return;
7908         }
7909
7910         undo_visual_stack.push_back (current_visual_state (true));
7911         PBD::Unwinder<bool> nsv (no_save_visual, true);
7912
7913         /* build a list of all tracks, including children */
7914
7915         TrackViewList all;
7916         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7917                 all.push_back (*i);
7918                 TimeAxisView::Children c = (*i)->get_child_list ();
7919                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7920                         all.push_back (j->get());
7921                 }
7922         }
7923
7924
7925         // find selection range.
7926         // if someone knows how to user TrackViewList::iterator for this
7927         // I'm all ears.
7928         int selected_top = -1;
7929         int selected_bottom = -1;
7930         int i = 0;
7931         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7932                 if ((*t)->marked_for_display ()) {
7933                         if (tracks.contains(*t)) {
7934                                 if (selected_top == -1) {
7935                                         selected_top = i;
7936                                 }
7937                                 selected_bottom = i;
7938                         }
7939                 }
7940         }
7941
7942         i = 0;
7943         for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7944                 if ((*t)->marked_for_display ()) {
7945                         if (tracks.contains(*t)) {
7946                                 (*t)->set_height (h);
7947                                 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7948                         } else {
7949                                 if (i > selected_top && i < selected_bottom) {
7950                                         hide_track_in_display (*t);
7951                                 }
7952                         }
7953                 }
7954         }
7955
7956         /*
7957            set the controls_layout height now, because waiting for its size
7958            request signal handler will cause the vertical adjustment setting to fail
7959         */
7960
7961         controls_layout.property_height () = _full_canvas_height;
7962         vertical_adjustment.set_value (first_y_pos);
7963
7964         redo_visual_stack.push_back (current_visual_state (true));
7965
7966         visible_tracks_selector.set_text (_("Sel"));
7967 }
7968
7969 void
7970 Editor::save_visual_state (uint32_t n)
7971 {
7972         while (visual_states.size() <= n) {
7973                 visual_states.push_back (0);
7974         }
7975
7976         if (visual_states[n] != 0) {
7977                 delete visual_states[n];
7978         }
7979
7980         visual_states[n] = current_visual_state (true);
7981         gdk_beep ();
7982 }
7983
7984 void
7985 Editor::goto_visual_state (uint32_t n)
7986 {
7987         if (visual_states.size() <= n) {
7988                 return;
7989         }
7990
7991         if (visual_states[n] == 0) {
7992                 return;
7993         }
7994
7995         use_visual_state (*visual_states[n]);
7996 }
7997
7998 void
7999 Editor::start_visual_state_op (uint32_t n)
8000 {
8001         save_visual_state (n);
8002
8003         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8004         char buf[32];
8005         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8006         pup->set_text (buf);
8007         pup->touch();
8008 }
8009
8010 void
8011 Editor::cancel_visual_state_op (uint32_t n)
8012 {
8013         goto_visual_state (n);
8014 }
8015
8016 void
8017 Editor::toggle_region_mute ()
8018 {
8019         if (_ignore_region_action) {
8020                 return;
8021         }
8022
8023         RegionSelection rs = get_regions_from_selection_and_entered ();
8024
8025         if (rs.empty ()) {
8026                 return;
8027         }
8028
8029         if (rs.size() > 1) {
8030                 begin_reversible_command (_("mute regions"));
8031         } else {
8032                 begin_reversible_command (_("mute region"));
8033         }
8034
8035         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8036
8037                 (*i)->region()->playlist()->clear_changes ();
8038                 (*i)->region()->set_muted (!(*i)->region()->muted ());
8039                 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8040
8041         }
8042
8043         commit_reversible_command ();
8044 }
8045
8046 void
8047 Editor::combine_regions ()
8048 {
8049         /* foreach track with selected regions, take all selected regions
8050            and join them into a new region containing the subregions (as a
8051            playlist)
8052         */
8053
8054         typedef set<RouteTimeAxisView*> RTVS;
8055         RTVS tracks;
8056
8057         if (selection->regions.empty()) {
8058                 return;
8059         }
8060
8061         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8062                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8063
8064                 if (rtv) {
8065                         tracks.insert (rtv);
8066                 }
8067         }
8068
8069         begin_reversible_command (_("combine regions"));
8070
8071         vector<RegionView*> new_selection;
8072
8073         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8074                 RegionView* rv;
8075
8076                 if ((rv = (*i)->combine_regions ()) != 0) {
8077                         new_selection.push_back (rv);
8078                 }
8079         }
8080
8081         selection->clear_regions ();
8082         for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8083                 selection->add (*i);
8084         }
8085
8086         commit_reversible_command ();
8087 }
8088
8089 void
8090 Editor::uncombine_regions ()
8091 {
8092         typedef set<RouteTimeAxisView*> RTVS;
8093         RTVS tracks;
8094
8095         if (selection->regions.empty()) {
8096                 return;
8097         }
8098
8099         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8100                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8101
8102                 if (rtv) {
8103                         tracks.insert (rtv);
8104                 }
8105         }
8106
8107         begin_reversible_command (_("uncombine regions"));
8108
8109         for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8110                 (*i)->uncombine_regions ();
8111         }
8112
8113         commit_reversible_command ();
8114 }
8115
8116 void
8117 Editor::toggle_midi_input_active (bool flip_others)
8118 {
8119         bool onoff = false;
8120         boost::shared_ptr<RouteList> rl (new RouteList);
8121
8122         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8123                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8124
8125                 if (!rtav) {
8126                         continue;
8127                 }
8128
8129                 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8130
8131                 if (mt) {
8132                         rl->push_back (rtav->route());
8133                         onoff = !mt->input_active();
8134                 }
8135         }
8136
8137         _session->set_exclusive_input_active (rl, onoff, flip_others);
8138 }
8139
8140 static bool ok_fine (GdkEventAny*) { return true; }
8141
8142 void
8143 Editor::lock ()
8144 {
8145         if (!lock_dialog) {
8146                 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8147
8148                 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8149                 lock_dialog->get_vbox()->pack_start (*padlock);
8150                 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8151
8152                 ArdourButton* b = manage (new ArdourButton);
8153                 b->set_name ("lock button");
8154                 b->set_text (_("Click to unlock"));
8155                 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8156                 lock_dialog->get_vbox()->pack_start (*b);
8157
8158                 lock_dialog->get_vbox()->show_all ();
8159                 lock_dialog->set_size_request (200, 200);
8160         }
8161
8162         delete _main_menu_disabler;
8163         _main_menu_disabler = new MainMenuDisabler;
8164
8165         lock_dialog->present ();
8166
8167         lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8168 }
8169
8170 void
8171 Editor::unlock ()
8172 {
8173         lock_dialog->hide ();
8174
8175         delete _main_menu_disabler;
8176         _main_menu_disabler = 0;
8177
8178         if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8179                 start_lock_event_timing ();
8180         }
8181 }
8182
8183 void
8184 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8185 {
8186         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8187 }
8188
8189 void
8190 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8191 {
8192         Timers::TimerSuspender t;
8193         label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8194         Gtkmm2ext::UI::instance()->flush_pending (1);
8195 }
8196
8197 void
8198 Editor::bring_all_sources_into_session ()
8199 {
8200         if (!_session) {
8201                 return;
8202         }
8203
8204         Gtk::Label msg;
8205         ArdourDialog w (_("Moving embedded files into session folder"));
8206         w.get_vbox()->pack_start (msg);
8207         w.present ();
8208
8209         /* flush all pending GUI events because we're about to start copying
8210          * files
8211          */
8212
8213         Timers::TimerSuspender t;
8214         Gtkmm2ext::UI::instance()->flush_pending (3);
8215
8216         cerr << " Do it\n";
8217
8218         _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8219 }