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