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