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