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