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