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