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