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