show loop and punch locations properly in location ui, reorder some dialog buttons.
[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 #include <unistd.h>
21
22 #include <cstdlib>
23 #include <cmath>
24 #include <string>
25 #include <map>
26
27 #include <pbd/error.h>
28 #include <pbd/basename.h>
29 #include <pbd/pthread_utils.h>
30 #include <pbd/memento_command.h>
31 #include <pbd/whitespace.h>
32
33 #include <gtkmm2ext/utils.h>
34 #include <gtkmm2ext/choice.h>
35 #include <gtkmm2ext/window_title.h>
36
37 #include <ardour/audioengine.h>
38 #include <ardour/session.h>
39 #include <ardour/audioplaylist.h>
40 #include <ardour/audioregion.h>
41 #include <ardour/audio_diskstream.h>
42 #include <ardour/utils.h>
43 #include <ardour/location.h>
44 #include <ardour/named_selection.h>
45 #include <ardour/audio_track.h>
46 #include <ardour/audiofilesource.h>
47 #include <ardour/audioplaylist.h>
48 #include <ardour/region_factory.h>
49 #include <ardour/playlist_factory.h>
50 #include <ardour/reverse.h>
51 #include <ardour/transient_detector.h>
52 #include <ardour/dB.h>
53
54 #include "ardour_ui.h"
55 #include "editor.h"
56 #include "time_axis_view.h"
57 #include "route_time_axis.h"
58 #include "audio_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "streamview.h"
61 #include "audio_region_view.h"
62 #include "rgb_macros.h"
63 #include "selection_templates.h"
64 #include "selection.h"
65 #include "editing.h"
66 #include "gtk-custom-hruler.h"
67 #include "gui_thread.h"
68 #include "keyboard.h"
69 #include "utils.h"
70
71 #include "i18n.h"
72
73 using namespace std;
74 using namespace ARDOUR;
75 using namespace PBD;
76 using namespace sigc;
77 using namespace Gtk;
78 using namespace Gtkmm2ext;
79 using namespace Editing;
80
81 /***********************************************************************
82   Editor operations
83  ***********************************************************************/
84
85 void
86 Editor::undo (uint32_t n)
87 {
88         if (session) {
89                 session->undo (n);
90         }
91 }
92
93 void
94 Editor::redo (uint32_t n)
95 {
96         if (session) {
97                 session->redo (n);
98         }
99 }
100
101 void
102 Editor::split_region ()
103 {
104         split_region_at (get_preferred_edit_position());
105 }
106
107 void
108 Editor::split_region_at (nframes_t where)
109 {
110         split_regions_at (where, selection->regions);
111 }
112
113 void
114 Editor::split_regions_at (nframes_t where, RegionSelection& regions)
115 {
116         if (regions.empty()) {
117                 return;
118         }
119
120         begin_reversible_command (_("split"));
121
122         // if splitting a single region, and snap-to is using
123         // region boundaries, don't pay attention to them
124
125         if (regions.size() == 1) {
126                 switch (snap_type) {
127                 case SnapToRegionStart:
128                 case SnapToRegionSync:
129                 case SnapToRegionEnd:
130                         break;
131                 default:
132                         snap_to (where);
133                 }
134         } else {
135                 snap_to (where);
136         }
137                 
138
139         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
140
141                 RegionSelection::iterator tmp;
142
143                 /* XXX this test needs to be more complicated, to make sure we really
144                    have something to split.
145                 */
146                 
147                 if (!(*a)->region()->covers (where)) {
148                         ++a;
149                         continue;
150                 }
151
152                 tmp = a;
153                 ++tmp;
154
155                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
156
157                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
158
159                 if (arv) {
160                         _new_regionviews_show_envelope = arv->envelope_visible();
161                 }
162                 
163                 if (pl) {
164                         XMLNode &before = pl->get_state();
165                         pl->split_region ((*a)->region(), where);
166                         XMLNode &after = pl->get_state();
167                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
168                 }
169
170                 a = tmp;
171         }
172         
173         commit_reversible_command ();
174         _new_regionviews_show_envelope = false;
175 }
176
177 void
178 Editor::remove_clicked_region ()
179 {
180         if (clicked_audio_trackview == 0 || clicked_regionview == 0) {
181                 return;
182         }
183
184         boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
185         
186         begin_reversible_command (_("remove region"));
187         XMLNode &before = playlist->get_state();
188         playlist->remove_region (clicked_regionview->region());
189         XMLNode &after = playlist->get_state();
190         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
191         commit_reversible_command ();
192 }
193
194 void
195 Editor::destroy_clicked_region ()
196 {
197         uint32_t selected = selection->regions.size();
198
199         if (!session || !selected) {
200                 return;
201         }
202
203         vector<string> choices;
204         string prompt;
205         
206         prompt  = string_compose (_(" This is destructive, will possibly delete audio files\n\
207 It cannot be undone\n\
208 Do you really want to destroy %1 ?"),
209                            (selected > 1 ? 
210                             _("these regions") : _("this region")));
211
212         choices.push_back (_("No, do nothing."));
213
214         if (selected > 1) {
215                 choices.push_back (_("Yes, destroy them."));
216         } else {
217                 choices.push_back (_("Yes, destroy it."));
218         }
219
220         Gtkmm2ext::Choice prompter (prompt, choices);
221         
222         if (prompter.run() == 0) { /* first choice */
223                 return;
224         }
225
226         if (selected) {
227                 list<boost::shared_ptr<Region> > r;
228
229                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
230                         r.push_back ((*i)->region());
231                 }
232
233                 session->destroy_regions (r);
234         } 
235 }
236
237 boost::shared_ptr<Region>
238 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
239 {
240         RegionView* rv;
241         boost::shared_ptr<Region> region;
242         nframes_t start = 0;
243
244         if (selection->time.start () == selection->time.end_frame ()) {
245                 
246                 /* no current selection-> is there a selected regionview? */
247
248                 if (selection->regions.empty()) {
249                         return region;
250                 }
251
252         } 
253
254         if (!selection->regions.empty()) {
255
256                 rv = *(selection->regions.begin());
257                 (*tv) = &rv->get_time_axis_view();
258                 region = rv->region();
259
260         } else if (!selection->tracks.empty()) {
261
262                 (*tv) = selection->tracks.front();
263
264                 RouteTimeAxisView* rtv;
265
266                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
267                         boost::shared_ptr<Playlist> pl;
268                         
269                         if ((pl = rtv->playlist()) == 0) {
270                                 return region;
271                         }
272                         
273                         region = pl->top_region_at (start);
274                 }
275         } 
276         
277         return region;
278 }
279         
280 void
281 Editor::extend_selection_to_end_of_region (bool next)
282 {
283         TimeAxisView *tv;
284         boost::shared_ptr<Region> region;
285         nframes_t start;
286
287         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
288                 return;
289         }
290
291         if (region && selection->time.start () == selection->time.end_frame ()) {
292                 start = region->position();
293         } else {
294                 start = selection->time.start ();
295         }
296
297         /* Try to leave the selection with the same route if possible */
298
299         if ((tv = selection->time.track) == 0) {
300                 return;
301         }
302
303         begin_reversible_command (_("extend selection"));
304         selection->set (tv, start, region->position() + region->length());
305         commit_reversible_command ();
306 }
307
308 void
309 Editor::extend_selection_to_start_of_region (bool previous)
310 {
311         TimeAxisView *tv;
312         boost::shared_ptr<Region> region;
313         nframes_t end;
314
315         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
316                 return;
317         }
318
319         if (region && selection->time.start () == selection->time.end_frame ()) {
320                 end = region->position() + region->length();
321         } else {
322                 end = selection->time.end_frame ();
323         }
324
325         /* Try to leave the selection with the same route if possible */
326         
327         if ((tv = selection->time.track) == 0) {
328                 return;
329         }
330
331         begin_reversible_command (_("extend selection"));
332         selection->set (tv, region->position(), end);
333         commit_reversible_command ();
334 }
335
336 bool
337 Editor::nudge_forward_release (GdkEventButton* ev)
338 {
339         if (ev->state & Keyboard::PrimaryModifier) {
340                 nudge_forward (false, true);
341         } else {
342                 nudge_forward (false, false);
343         }
344         return false;
345 }
346
347 bool
348 Editor::nudge_backward_release (GdkEventButton* ev)
349 {
350         if (ev->state & Keyboard::PrimaryModifier) {
351                 nudge_backward (false, true);
352         } else {
353                 nudge_backward (false, false);
354         }
355         return false;
356 }
357
358
359 void
360 Editor::nudge_forward (bool next, bool force_playhead)
361 {
362         nframes_t distance;
363         nframes_t next_distance;
364
365         if (!session) return;
366         
367         if (!force_playhead && !selection->regions.empty()) {
368
369                 begin_reversible_command (_("nudge regions forward"));
370
371                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
372                         boost::shared_ptr<Region> r ((*i)->region());
373                         
374                         distance = get_nudge_distance (r->position(), next_distance);
375
376                         if (next) {
377                                 distance = next_distance;
378                         }
379
380                         XMLNode &before = r->playlist()->get_state();
381                         r->set_position (r->position() + distance, this);
382                         XMLNode &after = r->playlist()->get_state();
383                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
384                 }
385
386                 commit_reversible_command ();
387
388                 
389         } else if (!force_playhead && !selection->markers.empty()) {
390
391                 bool is_start;
392                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
393
394                 if (loc) {
395
396                         begin_reversible_command (_("nudge location forward"));
397
398                         XMLNode& before (loc->get_state());
399
400                         if (is_start) {
401                                 distance = get_nudge_distance (loc->start(), next_distance);
402                                 if (next) {
403                                         distance = next_distance;
404                                 }
405                                 if (max_frames - distance > loc->start() + loc->length()) {
406                                         loc->set_start (loc->start() + distance);
407                                 } else {
408                                         loc->set_start (max_frames - loc->length());
409                                 }
410                         } else {
411                                 distance = get_nudge_distance (loc->end(), next_distance);
412                                 if (next) {
413                                         distance = next_distance;
414                                 }
415                                 if (max_frames - distance > loc->end()) {
416                                         loc->set_end (loc->end() + distance);
417                                 } else {
418                                         loc->set_end (max_frames);
419                                 }
420                         }
421                         XMLNode& after (loc->get_state());
422                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
423                         commit_reversible_command ();
424                 }
425                 
426         } else {
427                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
428                 session->request_locate (playhead_cursor->current_frame + distance);
429         }
430 }
431                 
432 void
433 Editor::nudge_backward (bool next, bool force_playhead)
434 {
435         nframes_t distance;
436         nframes_t next_distance;
437
438         if (!session) return;
439         
440         if (!force_playhead && !selection->regions.empty()) {
441
442                 begin_reversible_command (_("nudge regions backward"));
443
444                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
445                         boost::shared_ptr<Region> r ((*i)->region());
446
447                         distance = get_nudge_distance (r->position(), next_distance);
448                         
449                         if (next) {
450                                 distance = next_distance;
451                         }
452
453                         XMLNode &before = r->playlist()->get_state();
454                         
455                         if (r->position() > distance) {
456                                 r->set_position (r->position() - distance, this);
457                         } else {
458                                 r->set_position (0, this);
459                         }
460                         XMLNode &after = r->playlist()->get_state();
461                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
462                 }
463
464                 commit_reversible_command ();
465
466         } else if (!force_playhead && !selection->markers.empty()) {
467
468                 bool is_start;
469                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
470
471                 if (loc) {
472
473                         begin_reversible_command (_("nudge location forward"));
474                         XMLNode& before (loc->get_state());
475
476                         if (is_start) {
477                                 distance = get_nudge_distance (loc->start(), next_distance);
478                                 if (next) {
479                                         distance = next_distance;
480                                 }
481                                 if (distance < loc->start()) {
482                                         loc->set_start (loc->start() - distance);
483                                 } else {
484                                         loc->set_start (0);
485                                 }
486                         } else {
487                                 distance = get_nudge_distance (loc->end(), next_distance);
488
489                                 if (next) {
490                                         distance = next_distance;
491                                 }
492
493                                 if (distance < loc->end() - loc->length()) {
494                                         loc->set_end (loc->end() - distance);
495                                 } else {
496                                         loc->set_end (loc->length());
497                                 }
498                         }
499
500                         XMLNode& after (loc->get_state());
501                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
502                 }
503                 
504         } else {
505
506                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
507
508                 if (playhead_cursor->current_frame > distance) {
509                         session->request_locate (playhead_cursor->current_frame - distance);
510                 } else {
511                         session->goto_start();
512                 }
513         }
514 }
515
516 void
517 Editor::nudge_forward_capture_offset ()
518 {
519         nframes_t distance;
520
521         if (!session) return;
522         
523         if (!selection->regions.empty()) {
524
525                 begin_reversible_command (_("nudge forward"));
526
527                 distance = session->worst_output_latency();
528
529                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
530                         boost::shared_ptr<Region> r ((*i)->region());
531                         
532                         XMLNode &before = r->playlist()->get_state();
533                         r->set_position (r->position() + distance, this);
534                         XMLNode &after = r->playlist()->get_state();
535                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
536                 }
537
538                 commit_reversible_command ();
539
540         } 
541 }
542                 
543 void
544 Editor::nudge_backward_capture_offset ()
545 {
546         nframes_t distance;
547
548         if (!session) return;
549         
550         if (!selection->regions.empty()) {
551
552                 begin_reversible_command (_("nudge forward"));
553
554                 distance = session->worst_output_latency();
555
556                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
557                         boost::shared_ptr<Region> r ((*i)->region());
558
559                         XMLNode &before = r->playlist()->get_state();
560                         
561                         if (r->position() > distance) {
562                                 r->set_position (r->position() - distance, this);
563                         } else {
564                                 r->set_position (0, this);
565                         }
566                         XMLNode &after = r->playlist()->get_state();
567                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
568                 }
569
570                 commit_reversible_command ();
571         }
572 }
573
574 /* DISPLAY MOTION */
575
576 void
577 Editor::move_to_start ()
578 {
579         session->goto_start ();
580 }
581
582 void
583 Editor::move_to_end ()
584 {
585
586         session->request_locate (session->current_end_frame());
587 }
588
589 void
590 Editor::build_region_boundary_cache ()
591 {
592         nframes_t pos = 0;
593         vector<RegionPoint> interesting_points;
594         boost::shared_ptr<Region> r;
595         TrackViewList tracks;
596         bool at_end = false;
597
598         region_boundary_cache.clear ();
599
600         if (session == 0) {
601                 return;
602         }
603         
604         switch (snap_type) {
605         case SnapToRegionStart:
606                 interesting_points.push_back (Start);
607                 break;
608         case SnapToRegionEnd:
609                 interesting_points.push_back (End);
610                 break;  
611         case SnapToRegionSync:
612                 interesting_points.push_back (SyncPoint);
613                 break;  
614         case SnapToRegionBoundary:
615                 interesting_points.push_back (Start);
616                 interesting_points.push_back (End);
617                 break;  
618         default:
619                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
620                 /*NOTREACHED*/
621                 return;
622         }
623         
624         TimeAxisView *ontrack = 0;
625         TrackViewList tlist;
626
627         if (!selection->tracks.empty()) {
628                 tlist = selection->tracks;
629         } else {
630                 tlist = track_views;
631         }
632
633         while (pos < session->current_end_frame() && !at_end) {
634
635                 nframes_t rpos;
636                 nframes_t lpos = max_frames;
637
638                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
639
640                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
641                                 if (*p == interesting_points.back()) {
642                                         at_end = true;
643                                 }
644                                 /* move to next point type */
645                                 continue;
646                         }
647
648                         switch (*p) {
649                         case Start:
650                                 rpos = r->first_frame();
651                                 break;
652
653                         case End:
654                                 rpos = r->last_frame();
655                                 break;  
656
657                         case SyncPoint:
658                                 rpos = r->adjust_to_sync (r->first_frame());
659                                 break;
660
661                         default:
662                                 break;
663                         }
664                         
665                         float speed = 1.0f;
666                         AudioTimeAxisView *atav;
667                         
668                         if (ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
669                                 if (atav->get_diskstream() != 0) {
670                                         speed = atav->get_diskstream()->speed();
671                                 }
672                         }
673                         
674                         rpos = track_frame_to_session_frame (rpos, speed);
675
676                         if (rpos < lpos) {
677                                 lpos = rpos;
678                         }
679
680                         /* prevent duplicates, but we don't use set<> because we want to be able
681                            to sort later.
682                         */
683
684                         vector<nframes_t>::iterator ri; 
685                         
686                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
687                                 if (*ri == rpos) {
688                                         break;
689                                 }
690                         }
691
692                         if (ri == region_boundary_cache.end()) {
693                                 region_boundary_cache.push_back (rpos);
694                         }
695                 }
696
697                 pos = lpos + 1;
698         }
699
700         /* finally sort to be sure that the order is correct */
701
702         sort (region_boundary_cache.begin(), region_boundary_cache.end());
703 }
704
705 boost::shared_ptr<Region>
706 Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
707 {
708         TrackViewList::iterator i;
709         nframes_t closest = max_frames;
710         boost::shared_ptr<Region> ret;
711         nframes_t rpos = 0;
712
713         float track_speed;
714         nframes_t track_frame;
715         AudioTimeAxisView *atav;
716
717         for (i = tracks.begin(); i != tracks.end(); ++i) {
718
719                 nframes_t distance;
720                 boost::shared_ptr<Region> r;
721                 
722                 track_speed = 1.0f;
723                 if ( (atav = dynamic_cast<AudioTimeAxisView*>(*i)) != 0 ) {
724                         if (atav->get_diskstream()!=0)
725                                 track_speed = atav->get_diskstream()->speed();
726                 }
727
728                 track_frame = session_frame_to_track_frame(frame, track_speed);
729
730                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
731                         continue;
732                 }
733
734                 switch (point) {
735                 case Start:
736                         rpos = r->first_frame ();
737                         break;
738
739                 case End:
740                         rpos = r->last_frame ();
741                         break;
742
743                 case SyncPoint:
744                         rpos = r->adjust_to_sync (r->first_frame());
745                         break;
746                 }
747
748                 // rpos is a "track frame", converting it to "session frame"
749                 rpos = track_frame_to_session_frame(rpos, track_speed);
750
751                 if (rpos > frame) {
752                         distance = rpos - frame;
753                 } else {
754                         distance = frame - rpos;
755                 }
756
757                 if (distance < closest) {
758                         closest = distance;
759                         if (ontrack != 0)
760                                 *ontrack = (*i);
761                         ret = r;
762                 }
763         }
764
765         return ret;
766 }
767
768 nframes64_t
769 Editor::find_next_region_boundary (nframes64_t pos, int32_t dir, const TrackViewList& tracks)
770 {
771         nframes64_t distance = max_frames;
772         nframes64_t current_nearest = -1;
773
774         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
775                 nframes64_t contender;
776                 nframes64_t d;
777
778                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
779
780                 if (!rtv) {
781                         continue;
782                 }
783
784                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
785                         continue;
786                 }
787
788                 d = ::llabs (pos - contender);
789
790                 if (d < distance) {
791                         current_nearest = contender;
792                         distance = d;
793                 }
794         }
795         
796         return current_nearest;
797 }
798
799 void
800 Editor::cursor_to_region_boundary (Cursor* cursor, int32_t dir)
801 {
802         nframes64_t pos = cursor->current_frame;
803         nframes64_t target;
804
805         if (!session) {
806                 return;
807         }
808
809         // so we don't find the current region again..
810         if (dir > 0 || pos > 0) {
811                 pos += dir;
812         }
813
814         if (!selection->tracks.empty()) {
815                 
816                 target = find_next_region_boundary (pos, dir, selection->tracks);
817                 
818         } else {
819                 
820                 target = find_next_region_boundary (pos, dir, track_views);
821         }
822         
823         if (target < 0) {
824                 return;
825         }
826
827
828         if (cursor == playhead_cursor) {
829                 session->request_locate (target);
830         } else {
831                 cursor->set_position (target);
832         }
833 }
834
835 void
836 Editor::cursor_to_next_region_boundary (Cursor* cursor)
837 {
838         cursor_to_region_boundary (cursor, 1);
839 }
840
841 void
842 Editor::cursor_to_previous_region_boundary (Cursor* cursor)
843 {
844         cursor_to_region_boundary (cursor, -1);
845 }
846
847 void
848 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
849 {
850         boost::shared_ptr<Region> r;
851         nframes_t pos = cursor->current_frame;
852
853         if (!session) {
854                 return;
855         }
856
857         TimeAxisView *ontrack = 0;
858
859         // so we don't find the current region again..
860         if (dir>0 || pos>0)
861                 pos+=dir;
862
863         if (!selection->tracks.empty()) {
864                 
865                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
866                 
867         } else if (clicked_trackview) {
868                 
869                 TrackViewList t;
870                 t.push_back (clicked_trackview);
871                 
872                 r = find_next_region (pos, point, dir, t, &ontrack);
873                 
874         } else {
875                 
876                 r = find_next_region (pos, point, dir, track_views, &ontrack);
877         }
878
879         if (r == 0) {
880                 return;
881         }
882         
883         switch (point){
884         case Start:
885                 pos = r->first_frame ();
886                 break;
887
888         case End:
889                 pos = r->last_frame ();
890                 break;
891
892         case SyncPoint:
893                 pos = r->adjust_to_sync (r->first_frame());
894                 break;  
895         }
896         
897         float speed = 1.0f;
898         AudioTimeAxisView *atav;
899
900         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
901                 if (atav->get_diskstream() != 0) {
902                         speed = atav->get_diskstream()->speed();
903                 }
904         }
905
906         pos = track_frame_to_session_frame(pos, speed);
907         
908         if (cursor == playhead_cursor) {
909                 session->request_locate (pos);
910         } else {
911                 cursor->set_position (pos);
912         }
913 }
914
915 void
916 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
917 {
918         cursor_to_region_point (cursor, point, 1);
919 }
920
921 void
922 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
923 {
924         cursor_to_region_point (cursor, point, -1);
925 }
926
927 void
928 Editor::cursor_to_selection_start (Cursor *cursor)
929 {
930         nframes_t pos = 0;
931         switch (mouse_mode) {
932         case MouseObject:
933                 if (!selection->regions.empty()) {
934                         pos = selection->regions.start();
935                 }
936                 break;
937
938         case MouseRange:
939                 if (!selection->time.empty()) {
940                         pos = selection->time.start ();
941                 }
942                 break;
943
944         default:
945                 return;
946         }
947
948         if (cursor == playhead_cursor) {
949                 session->request_locate (pos);
950         } else {
951                 cursor->set_position (pos);
952         }
953 }
954
955 void
956 Editor::cursor_to_selection_end (Cursor *cursor)
957 {
958         nframes_t pos = 0;
959
960         switch (mouse_mode) {
961         case MouseObject:
962                 if (!selection->regions.empty()) {
963                         pos = selection->regions.end_frame();
964                 }
965                 break;
966
967         case MouseRange:
968                 if (!selection->time.empty()) {
969                         pos = selection->time.end_frame ();
970                 }
971                 break;
972
973         default:
974                 return;
975         }
976
977         if (cursor == playhead_cursor) {
978                 session->request_locate (pos);
979         } else {
980                 cursor->set_position (pos);
981         }
982 }
983
984 void
985 Editor::selected_marker_to_region_boundary (int32_t dir)
986 {
987         nframes64_t target;
988         Location* loc;
989         bool ignored;
990
991         if (!session) {
992                 return;
993         }
994
995         if (selection->markers.empty()) {
996                 nframes64_t mouse;
997                 bool ignored;
998
999                 if (!mouse_frame (mouse, ignored)) {
1000                         return;
1001                 }
1002                 
1003                 add_location_mark (mouse);
1004         }
1005
1006         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1007                 return;
1008         }
1009
1010         nframes64_t pos = loc->start();
1011
1012         // so we don't find the current region again..
1013         if (dir > 0 || pos > 0) {
1014                 pos += dir;
1015         }
1016
1017         if (!selection->tracks.empty()) {
1018                 
1019                 target = find_next_region_boundary (pos, dir, selection->tracks);
1020                 
1021         } else {
1022                 
1023                 target = find_next_region_boundary (pos, dir, track_views);
1024         }
1025         
1026         if (target < 0) {
1027                 return;
1028         }
1029
1030         loc->move_to (target);
1031 }
1032
1033 void
1034 Editor::selected_marker_to_next_region_boundary ()
1035 {
1036         selected_marker_to_region_boundary (1);
1037 }
1038
1039 void
1040 Editor::selected_marker_to_previous_region_boundary ()
1041 {
1042         selected_marker_to_region_boundary (-1);
1043 }
1044
1045 void
1046 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1047 {
1048         boost::shared_ptr<Region> r;
1049         nframes_t pos;
1050         Location* loc;
1051         bool ignored;
1052
1053         if (!session || selection->markers.empty()) {
1054                 return;
1055         }
1056
1057         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1058                 return;
1059         }
1060
1061         TimeAxisView *ontrack = 0;
1062
1063         pos = loc->start();
1064
1065         // so we don't find the current region again..
1066         if (dir>0 || pos>0)
1067                 pos+=dir;
1068
1069         if (!selection->tracks.empty()) {
1070                 
1071                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1072                 
1073         } else if (clicked_trackview) {
1074                 
1075                 TrackViewList t;
1076                 t.push_back (clicked_trackview);
1077                 
1078                 r = find_next_region (pos, point, dir, t, &ontrack);
1079                 
1080         } else {
1081                 
1082                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1083         }
1084
1085         if (r == 0) {
1086                 return;
1087         }
1088         
1089         switch (point){
1090         case Start:
1091                 pos = r->first_frame ();
1092                 break;
1093
1094         case End:
1095                 pos = r->last_frame ();
1096                 break;
1097
1098         case SyncPoint:
1099                 pos = r->adjust_to_sync (r->first_frame());
1100                 break;  
1101         }
1102         
1103         float speed = 1.0f;
1104         AudioTimeAxisView *atav;
1105
1106         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
1107                 if (atav->get_diskstream() != 0) {
1108                         speed = atav->get_diskstream()->speed();
1109                 }
1110         }
1111
1112         pos = track_frame_to_session_frame(pos, speed);
1113
1114         loc->move_to (pos);
1115 }
1116
1117 void
1118 Editor::selected_marker_to_next_region_point (RegionPoint point)
1119 {
1120         selected_marker_to_region_point (point, 1);
1121 }
1122
1123 void
1124 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1125 {
1126         selected_marker_to_region_point (point, -1);
1127 }
1128
1129 void
1130 Editor::selected_marker_to_selection_start ()
1131 {
1132         nframes_t pos = 0;
1133         Location* loc;
1134         bool ignored;
1135
1136         if (!session || selection->markers.empty()) {
1137                 return;
1138         }
1139
1140         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1141                 return;
1142         }
1143
1144         switch (mouse_mode) {
1145         case MouseObject:
1146                 if (!selection->regions.empty()) {
1147                         pos = selection->regions.start();
1148                 }
1149                 break;
1150
1151         case MouseRange:
1152                 if (!selection->time.empty()) {
1153                         pos = selection->time.start ();
1154                 }
1155                 break;
1156
1157         default:
1158                 return;
1159         }
1160
1161         loc->move_to (pos);
1162 }
1163
1164 void
1165 Editor::selected_marker_to_selection_end ()
1166 {
1167         nframes_t pos = 0;
1168         Location* loc;
1169         bool ignored;
1170
1171         if (!session || selection->markers.empty()) {
1172                 return;
1173         }
1174
1175         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1176                 return;
1177         }
1178
1179         switch (mouse_mode) {
1180         case MouseObject:
1181                 if (!selection->regions.empty()) {
1182                         pos = selection->regions.end_frame();
1183                 }
1184                 break;
1185
1186         case MouseRange:
1187                 if (!selection->time.empty()) {
1188                         pos = selection->time.end_frame ();
1189                 }
1190                 break;
1191
1192         default:
1193                 return;
1194         }
1195
1196         loc->move_to (pos);
1197 }
1198
1199 void
1200 Editor::scroll_playhead (bool forward)
1201 {
1202         nframes_t pos = playhead_cursor->current_frame;
1203         nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
1204
1205         if (forward) {
1206                 if (pos == max_frames) {
1207                         return;
1208                 }
1209
1210                 if (pos < max_frames - delta) {
1211                         pos += delta ;
1212                 } else {
1213                         pos = max_frames;
1214                 } 
1215
1216         } else {
1217
1218                 if (pos == 0) {
1219                         return;
1220                 } 
1221
1222                 if (pos > delta) {
1223                         pos -= delta;
1224                 } else {
1225                         pos = 0;
1226                 }
1227         }
1228
1229         session->request_locate (pos);
1230 }
1231
1232 void
1233 Editor::playhead_backward ()
1234 {
1235         nframes_t pos;
1236         nframes_t cnt;
1237         float prefix;
1238         bool was_floating;
1239
1240         if (get_prefix (prefix, was_floating)) {
1241                 cnt = 1;
1242         } else {
1243                 if (was_floating) {
1244                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1245                 } else {
1246                         cnt = (nframes_t) prefix;
1247                 }
1248         }
1249
1250         pos = playhead_cursor->current_frame;
1251
1252         if ((nframes_t) pos < cnt) {
1253                 pos = 0;
1254         } else {
1255                 pos -= cnt;
1256         }
1257         
1258         /* XXX this is completely insane. with the current buffering
1259            design, we'll force a complete track buffer flush and
1260            reload, just to move 1 sample !!!
1261         */
1262
1263         session->request_locate (pos);
1264 }
1265
1266 void
1267 Editor::playhead_forward ()
1268 {
1269         nframes_t pos;
1270         nframes_t cnt;
1271         bool was_floating;
1272         float prefix;
1273
1274         if (get_prefix (prefix, was_floating)) {
1275                 cnt = 1;
1276         } else {
1277                 if (was_floating) {
1278                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1279                 } else {
1280                         cnt = (nframes_t) floor (prefix);
1281                 }
1282         }
1283
1284         pos = playhead_cursor->current_frame;
1285         
1286         /* XXX this is completely insane. with the current buffering
1287            design, we'll force a complete track buffer flush and
1288            reload, just to move 1 sample !!!
1289         */
1290
1291         session->request_locate (pos+cnt);
1292 }
1293
1294 void
1295 Editor::cursor_align (bool playhead_to_edit)
1296 {
1297         if (!session) {
1298                 return;
1299         }
1300
1301         if (playhead_to_edit) {
1302
1303                 if (selection->markers.empty()) {
1304                         return;
1305                 }
1306                 
1307                 session->request_locate (selection->markers.front()->position(), session->transport_rolling());
1308         
1309         } else {
1310
1311                 /* move selected markers to playhead */
1312                 
1313                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1314                         bool ignored;
1315                         
1316                         Location* loc = find_location_from_marker (*i, ignored);
1317                         
1318                         if (loc->is_mark()) {
1319                                 loc->set_start (playhead_cursor->current_frame);
1320                         } else {
1321                                 loc->set (playhead_cursor->current_frame,
1322                                           playhead_cursor->current_frame + loc->length());
1323                         }
1324                 }
1325         }
1326 }
1327
1328 void
1329 Editor::edit_cursor_backward ()
1330 {
1331         nframes64_t pos;
1332         nframes64_t cnt;
1333         float prefix;
1334         bool was_floating;
1335
1336         if (get_prefix (prefix, was_floating)) {
1337                 cnt = 1;
1338         } else {
1339                 if (was_floating) {
1340                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1341                 } else {
1342                         cnt = (nframes_t) prefix;
1343                 }
1344         }
1345
1346         if ((pos = get_preferred_edit_position()) < 0) {
1347                 return;
1348         }
1349
1350         if (pos < cnt) {
1351                 pos = 0;
1352         } else {
1353                 pos -= cnt;
1354         }
1355         
1356         // EDIT CURSOR edit_cursor->set_position (pos);
1357 }
1358
1359 void
1360 Editor::edit_cursor_forward ()
1361 {
1362         //nframes_t pos;
1363         nframes_t cnt;
1364         bool was_floating;
1365         float prefix;
1366
1367         if (get_prefix (prefix, was_floating)) {
1368                 cnt = 1;
1369         } else {
1370                 if (was_floating) {
1371                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1372                 } else {
1373                         cnt = (nframes_t) floor (prefix);
1374                 }
1375         }
1376
1377         // pos = edit_cursor->current_frame;
1378         // EDIT CURSOR edit_cursor->set_position (pos+cnt);
1379 }
1380
1381 void
1382 Editor::goto_frame ()
1383 {
1384         float prefix;
1385         bool was_floating;
1386         nframes_t frame;
1387
1388         if (get_prefix (prefix, was_floating)) {
1389                 return;
1390         }
1391
1392         if (was_floating) {
1393                 frame = (nframes_t) floor (prefix * session->frame_rate());
1394         } else {
1395                 frame = (nframes_t) floor (prefix);
1396         }
1397
1398         session->request_locate (frame);
1399 }
1400
1401 void
1402 Editor::scroll_backward (float pages)
1403 {
1404         nframes_t frame;
1405         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1406         bool was_floating;
1407         float prefix;
1408         nframes_t cnt;
1409         
1410         if (get_prefix (prefix, was_floating)) {
1411                 cnt = (nframes_t) floor (pages * one_page);
1412         } else {
1413                 if (was_floating) {
1414                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1415                 } else {
1416                         cnt = (nframes_t) floor (prefix * one_page);
1417                 }
1418         }
1419
1420         if (leftmost_frame < cnt) {
1421                 frame = 0;
1422         } else {
1423                 frame = leftmost_frame - cnt;
1424         }
1425
1426         reset_x_origin (frame);
1427 }
1428
1429 void
1430 Editor::scroll_forward (float pages)
1431 {
1432         nframes_t frame;
1433         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1434         bool was_floating;
1435         float prefix;
1436         nframes_t cnt;
1437         
1438         if (get_prefix (prefix, was_floating)) {
1439                 cnt = (nframes_t) floor (pages * one_page);
1440         } else {
1441                 if (was_floating) {
1442                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1443                 } else {
1444                         cnt = (nframes_t) floor (prefix * one_page);
1445                 }
1446         }
1447
1448         if (max_frames - cnt < leftmost_frame) {
1449                 frame = max_frames - cnt;
1450         } else {
1451                 frame = leftmost_frame + cnt;
1452         }
1453
1454         reset_x_origin (frame);
1455 }
1456
1457 void
1458 Editor::scroll_tracks_down ()
1459 {
1460         float prefix;
1461         bool was_floating;
1462         int cnt;
1463
1464         if (get_prefix (prefix, was_floating)) {
1465                 cnt = 1;
1466         } else {
1467                 cnt = (int) floor (prefix);
1468         }
1469
1470         double vert_value = vertical_adjustment.get_value() + (cnt *
1471                 vertical_adjustment.get_page_size());
1472         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1473                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1474         }
1475         vertical_adjustment.set_value (vert_value);
1476 }
1477
1478 void
1479 Editor::scroll_tracks_up ()
1480 {
1481         float prefix;
1482         bool was_floating;
1483         int cnt;
1484
1485         if (get_prefix (prefix, was_floating)) {
1486                 cnt = 1;
1487         } else {
1488                 cnt = (int) floor (prefix);
1489         }
1490
1491         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1492 }
1493
1494 void
1495 Editor::scroll_tracks_down_line ()
1496 {
1497
1498         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1499         double vert_value = adj->get_value() + 20;
1500
1501         if (vert_value>adj->get_upper() - canvas_height) {
1502                 vert_value = adj->get_upper() - canvas_height;
1503         }
1504         adj->set_value (vert_value);
1505 }
1506
1507 void
1508 Editor::scroll_tracks_up_line ()
1509 {
1510         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1511         adj->set_value (adj->get_value() - 20);
1512 }
1513
1514 /* ZOOM */
1515
1516 void
1517 Editor::temporal_zoom_step (bool coarser)
1518 {
1519         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1520
1521         double nfpu;
1522
1523         nfpu = frames_per_unit;
1524         
1525         if (coarser) { 
1526                 nfpu *= 1.61803399;
1527         } else { 
1528                 nfpu = max(1.0,(nfpu/1.61803399));
1529         }
1530
1531         temporal_zoom (nfpu);
1532 }       
1533
1534 void
1535 Editor::temporal_zoom (gdouble fpu)
1536 {
1537         if (!session) return;
1538         
1539         nframes64_t current_page = current_page_frames();
1540         nframes64_t current_leftmost = leftmost_frame;
1541         nframes64_t current_rightmost;
1542         nframes64_t current_center;
1543         nframes64_t new_page_size;
1544         nframes64_t half_page_size;
1545         nframes64_t leftmost_after_zoom = 0;
1546         nframes64_t where;
1547         bool in_track_canvas;
1548         double nfpu;
1549         double l;
1550
1551         /* XXX this limit is also in ::set_frames_per_unit() */
1552
1553         if (frames_per_unit <= 2.0 && fpu <= frames_per_unit) {
1554                 return;
1555         }
1556
1557         nfpu = fpu;
1558         
1559         new_page_size = (nframes_t) floor (canvas_width * nfpu);
1560         half_page_size = new_page_size / 2;
1561
1562         switch (zoom_focus) {
1563         case ZoomFocusLeft:
1564                 leftmost_after_zoom = current_leftmost;
1565                 break;
1566                 
1567         case ZoomFocusRight:
1568                 current_rightmost = leftmost_frame + current_page;
1569                 if (current_rightmost < new_page_size) {
1570                         leftmost_after_zoom = 0;
1571                 } else {
1572                         leftmost_after_zoom = current_rightmost - new_page_size;
1573                 }
1574                 break;
1575                 
1576         case ZoomFocusCenter:
1577                 current_center = current_leftmost + (current_page/2); 
1578                 if (current_center < half_page_size) {
1579                         leftmost_after_zoom = 0;
1580                 } else {
1581                         leftmost_after_zoom = current_center - half_page_size;
1582                 }
1583                 break;
1584                 
1585         case ZoomFocusPlayhead:
1586                 /* try to keep the playhead in the same place */
1587
1588                 where = playhead_cursor->current_frame;
1589                 
1590                 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1591
1592                 if (l < 0) {
1593                         leftmost_after_zoom = 0;
1594                 } else if (l > max_frames) { 
1595                         leftmost_after_zoom = max_frames - new_page_size;
1596                 } else {
1597                         leftmost_after_zoom = (nframes64_t) l;
1598                 }
1599                 break;
1600
1601         case ZoomFocusMouse:
1602                 /* try to keep the mouse over the same point in the display */
1603
1604                 if (!mouse_frame (where, in_track_canvas)) {
1605                         /* use playhead instead */
1606                         where = playhead_cursor->current_frame;
1607
1608                         if (where < half_page_size) {
1609                                 leftmost_after_zoom = 0;
1610                         } else {
1611                                 leftmost_after_zoom = where - half_page_size;
1612                         }
1613
1614                 } else {
1615
1616                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1617
1618                         if (l < 0) {
1619                                 leftmost_after_zoom = 0;
1620                         } else if (l > max_frames) { 
1621                                 leftmost_after_zoom = max_frames - new_page_size;
1622                         } else {
1623                                 leftmost_after_zoom = (nframes64_t) l;
1624                         }
1625                 }
1626
1627                 break;
1628
1629         case ZoomFocusEdit:
1630                 /* try to keep the edit point in the same place */
1631                 where = get_preferred_edit_position ();
1632
1633                 if (where > 0) {
1634
1635                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1636
1637                         if (l < 0) {
1638                                 leftmost_after_zoom = 0;
1639                         } else if (l > max_frames) { 
1640                                 leftmost_after_zoom = max_frames - new_page_size;
1641                         } else {
1642                                 leftmost_after_zoom = (nframes64_t) l;
1643                         }
1644
1645                 } else {
1646                         /* edit point not defined */
1647                         return;
1648                 }
1649                 break;
1650                 
1651         }
1652  
1653         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1654
1655         reposition_and_zoom (leftmost_after_zoom, nfpu);
1656 }       
1657
1658 void
1659 Editor::temporal_zoom_region ()
1660 {
1661
1662         nframes64_t start = max_frames;
1663         nframes64_t end = 0;
1664
1665         ensure_entered_region_selected (true);
1666
1667         if (selection->regions.empty()) {
1668                 return;
1669         }
1670
1671         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1672                 if ((*i)->region()->position() < start) {
1673                         start = (*i)->region()->position();
1674                 }
1675                 if ((*i)->region()->last_frame() + 1 > end) {
1676                         end = (*i)->region()->last_frame() + 1;
1677                 }
1678         }
1679
1680         /* now comes an "interesting" hack ... make sure we leave a little space
1681            at each end of the editor so that the zoom doesn't fit the region
1682            precisely to the screen.
1683         */
1684
1685         GdkScreen* screen = gdk_screen_get_default ();
1686         gint pixwidth = gdk_screen_get_width (screen);
1687         gint mmwidth = gdk_screen_get_width_mm (screen);
1688         double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1689         double one_centimeter_in_pixels = pix_per_mm * 10.0;
1690         nframes_t extra_samples = unit_to_frame (one_centimeter_in_pixels);
1691         
1692         if (start > extra_samples) {
1693                 start -= extra_samples;
1694         } else {
1695                 start = 0;
1696         } 
1697
1698         if (max_frames - extra_samples > end) {
1699                 end += extra_samples;
1700         } else {
1701                 end = max_frames;
1702         }
1703
1704         temporal_zoom_by_frame (start, end, "zoom to region");
1705         zoomed_to_region = true;
1706 }
1707
1708 void
1709 Editor::toggle_zoom_region ()
1710 {
1711         if (zoomed_to_region) {
1712                 swap_visual_state ();
1713         } else {
1714                 temporal_zoom_region ();
1715         }
1716 }
1717
1718 void
1719 Editor::temporal_zoom_selection ()
1720 {
1721         if (!selection) return;
1722         
1723         if (selection->time.empty()) {
1724                 return;
1725         }
1726
1727         nframes_t start = selection->time[clicked_selection].start;
1728         nframes_t end = selection->time[clicked_selection].end;
1729
1730         temporal_zoom_by_frame (start, end, "zoom to selection");
1731 }
1732
1733 void
1734 Editor::temporal_zoom_session ()
1735 {
1736         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session));
1737
1738         if (session) {
1739                 temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session");
1740         }
1741 }
1742
1743 void
1744 Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & op)
1745 {
1746         if (!session) return;
1747
1748         if ((start == 0 && end == 0) || end < start) {
1749                 return;
1750         }
1751
1752         nframes_t range = end - start;
1753
1754         double new_fpu = (double)range / (double)canvas_width;
1755 //      double p2 = 1.0;
1756
1757 //      while (p2 < new_fpu) {
1758 //              p2 *= 2.0;
1759 //      }
1760 //      new_fpu = p2;
1761         
1762         nframes_t new_page = (nframes_t) floor (canvas_width * new_fpu);
1763         nframes_t middle = (nframes_t) floor( (double)start + ((double)range / 2.0f ));
1764         nframes_t new_leftmost = (nframes_t) floor( (double)middle - ((double)new_page/2.0f));
1765
1766         if (new_leftmost > middle) new_leftmost = 0;
1767
1768 //      begin_reversible_command (op);
1769 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1770 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1771 //      commit_reversible_command ();
1772
1773         reposition_and_zoom (new_leftmost, new_fpu);
1774 }
1775
1776 void 
1777 Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame)
1778 {
1779         if (!session) return;
1780         
1781         double range_before = frame - leftmost_frame;
1782         double new_fpu;
1783         
1784         new_fpu = frames_per_unit;
1785         
1786         if (coarser) { 
1787                 new_fpu *= 1.61803399;
1788                 range_before *= 1.61803399;
1789         } else { 
1790                 new_fpu = max(1.0,(new_fpu/1.61803399));
1791                 range_before /= 1.61803399;
1792         }
1793
1794         if (new_fpu == frames_per_unit) return;
1795
1796         nframes_t new_leftmost = frame - (nframes_t)range_before;
1797
1798         if (new_leftmost > frame) new_leftmost = 0;
1799
1800 //      begin_reversible_command (_("zoom to frame"));
1801 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1802 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1803 //      commit_reversible_command ();
1804
1805         reposition_and_zoom (new_leftmost, new_fpu);
1806 }
1807
1808 void
1809 Editor::add_location_from_selection ()
1810 {
1811         string rangename;
1812
1813         if (selection->time.empty()) {
1814                 return;
1815         }
1816
1817         if (session == 0 || clicked_trackview == 0) {
1818                 return;
1819         }
1820
1821         nframes_t start = selection->time[clicked_selection].start;
1822         nframes_t end = selection->time[clicked_selection].end;
1823
1824         session->locations()->next_available_name(rangename,"selection");
1825         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1826
1827         session->begin_reversible_command (_("add marker"));
1828         XMLNode &before = session->locations()->get_state();
1829         session->locations()->add (location, true);
1830         XMLNode &after = session->locations()->get_state();
1831         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1832         session->commit_reversible_command ();
1833 }
1834
1835 void
1836 Editor::add_location_mark (nframes64_t where)
1837 {
1838         string markername;
1839
1840         select_new_marker = true;
1841
1842         session->locations()->next_available_name(markername,"mark");
1843         Location *location = new Location (where, where, markername, Location::IsMark);
1844         session->begin_reversible_command (_("add marker"));
1845         XMLNode &before = session->locations()->get_state();
1846         session->locations()->add (location, true);
1847         XMLNode &after = session->locations()->get_state();
1848         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1849         session->commit_reversible_command ();
1850 }
1851
1852 void
1853 Editor::add_location_from_playhead_cursor ()
1854 {
1855         add_location_mark (session->audible_frame());
1856 }
1857
1858 void
1859 Editor::add_location_from_audio_region ()
1860 {
1861         if (selection->regions.empty()) {
1862                 return;
1863         }
1864
1865         RegionView* rv = *(selection->regions.begin());
1866         boost::shared_ptr<Region> region = rv->region();
1867         
1868         Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1869         session->begin_reversible_command (_("add marker"));
1870         XMLNode &before = session->locations()->get_state();
1871         session->locations()->add (location, true);
1872         XMLNode &after = session->locations()->get_state();
1873         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1874         session->commit_reversible_command ();
1875 }
1876
1877 void
1878 Editor::amplitude_zoom_step (bool in)
1879 {
1880         gdouble zoom = 1.0;
1881
1882         if (in) {
1883                 zoom *= 2.0;
1884         } else {
1885                 if (zoom > 2.0) {
1886                         zoom /= 2.0;
1887                 } else {
1888                         zoom = 1.0;
1889                 }
1890         }
1891
1892 #ifdef FIX_FOR_CANVAS
1893         /* XXX DO SOMETHING */
1894 #endif
1895 }       
1896
1897
1898 /* DELETION */
1899
1900
1901 void
1902 Editor::delete_sample_forward ()
1903 {
1904 }
1905
1906 void
1907 Editor::delete_sample_backward ()
1908 {
1909 }
1910
1911 void
1912 Editor::delete_screen ()
1913 {
1914 }
1915
1916 /* SEARCH */
1917
1918 void
1919 Editor::search_backwards ()
1920 {
1921         /* what ? */
1922 }
1923
1924 void
1925 Editor::search_forwards ()
1926 {
1927         /* what ? */
1928 }
1929
1930 /* MARKS */
1931
1932 void
1933 Editor::jump_forward_to_mark ()
1934 {
1935         if (!session) {
1936                 return;
1937         }
1938         
1939         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1940
1941         if (location) {
1942                 session->request_locate (location->start(), session->transport_rolling());
1943         } else {
1944                 session->request_locate (session->current_end_frame());
1945         }
1946 }
1947
1948 void
1949 Editor::jump_backward_to_mark ()
1950 {
1951         if (!session) {
1952                 return;
1953         }
1954
1955         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1956         
1957         if (location) {
1958                 session->request_locate (location->start(), session->transport_rolling());
1959         } else {
1960                 session->goto_start ();
1961         }
1962 }
1963
1964 void
1965 Editor::set_mark ()
1966 {
1967         nframes_t pos;
1968         float prefix;
1969         bool was_floating;
1970         string markername;
1971
1972         if (get_prefix (prefix, was_floating)) {
1973                 pos = session->audible_frame ();
1974         } else {
1975                 if (was_floating) {
1976                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1977                 } else {
1978                         pos = (nframes_t) floor (prefix);
1979                 }
1980         }
1981
1982         session->locations()->next_available_name(markername,"mark");
1983         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1984 }
1985
1986 void
1987 Editor::clear_markers ()
1988 {
1989         if (session) {
1990                 session->begin_reversible_command (_("clear markers"));
1991                 XMLNode &before = session->locations()->get_state();
1992                 session->locations()->clear_markers ();
1993                 XMLNode &after = session->locations()->get_state();
1994                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1995                 session->commit_reversible_command ();
1996         }
1997 }
1998
1999 void
2000 Editor::clear_ranges ()
2001 {
2002         if (session) {
2003                 session->begin_reversible_command (_("clear ranges"));
2004                 XMLNode &before = session->locations()->get_state();
2005                 
2006                 Location * looploc = session->locations()->auto_loop_location();
2007                 Location * punchloc = session->locations()->auto_punch_location();
2008                 
2009                 session->locations()->clear_ranges ();
2010                 // re-add these
2011                 if (looploc) session->locations()->add (looploc);
2012                 if (punchloc) session->locations()->add (punchloc);
2013                 
2014                 XMLNode &after = session->locations()->get_state();
2015                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2016                 session->commit_reversible_command ();
2017         }
2018 }
2019
2020 void
2021 Editor::clear_locations ()
2022 {
2023         session->begin_reversible_command (_("clear locations"));
2024         XMLNode &before = session->locations()->get_state();
2025         session->locations()->clear ();
2026         XMLNode &after = session->locations()->get_state();
2027         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2028         session->commit_reversible_command ();
2029         session->locations()->clear ();
2030 }
2031
2032 void
2033 Editor::unhide_markers ()
2034 {
2035         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2036                 Location *l = (*i).first;
2037                 if (l->is_hidden() && l->is_mark()) {
2038                         l->set_hidden(false, this);
2039                 }
2040         }
2041 }
2042
2043 void
2044 Editor::unhide_ranges ()
2045 {
2046         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2047                 Location *l = (*i).first;
2048                 if (l->is_hidden() && l->is_range_marker()) { 
2049                         l->set_hidden(false, this);
2050                 }
2051         }
2052 }
2053
2054 /* INSERT/REPLACE */
2055
2056 void
2057 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
2058 {
2059         double wx, wy;
2060         double cx, cy;
2061         TimeAxisView *tv;
2062         nframes_t where;
2063         AudioTimeAxisView *atv = 0;
2064         boost::shared_ptr<Playlist> playlist;
2065         
2066         track_canvas.window_to_world (x, y, wx, wy);
2067         wx += horizontal_adjustment.get_value();
2068         wy += vertical_adjustment.get_value();
2069
2070         GdkEvent event;
2071         event.type = GDK_BUTTON_RELEASE;
2072         event.button.x = wx;
2073         event.button.y = wy;
2074         
2075         where = event_frame (&event, &cx, &cy);
2076
2077         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
2078                 /* clearly outside canvas area */
2079                 return;
2080         }
2081         
2082         if ((tv = trackview_by_y_position (cy)) == 0) {
2083                 return;
2084         }
2085         
2086         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
2087                 return;
2088         }
2089
2090         if ((playlist = atv->playlist()) == 0) {
2091                 return;
2092         }
2093         
2094         snap_to (where);
2095         
2096         begin_reversible_command (_("insert dragged region"));
2097         XMLNode &before = playlist->get_state();
2098         playlist->add_region (RegionFactory::create (region), where, 1.0);
2099         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2100         commit_reversible_command ();
2101 }
2102
2103 void
2104 Editor::insert_region_list_selection (float times)
2105 {
2106         RouteTimeAxisView *tv = 0;
2107         boost::shared_ptr<Playlist> playlist;
2108
2109         if (clicked_audio_trackview != 0) {
2110                 tv = clicked_audio_trackview;
2111         } else if (!selection->tracks.empty()) {
2112                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2113                         return;
2114                 }
2115         } else if (entered_track != 0) {
2116                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2117                         return;
2118                 }
2119         } else {
2120                 return;
2121         }
2122
2123         if ((playlist = tv->playlist()) == 0) {
2124                 return;
2125         }
2126         
2127         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2128         
2129         if (selected->count_selected_rows() != 1) {
2130                 return;
2131         }
2132         
2133         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
2134
2135         /* only one row selected, so rows.begin() is it */
2136
2137         TreeIter iter;
2138
2139         if ((iter = region_list_model->get_iter (*rows.begin()))) {
2140
2141                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
2142                 
2143                 begin_reversible_command (_("insert region"));
2144                 XMLNode &before = playlist->get_state();
2145                 playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times);
2146                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2147                 commit_reversible_command ();
2148         } 
2149 }
2150
2151 /* BUILT-IN EFFECTS */
2152
2153 void
2154 Editor::reverse_selection ()
2155 {
2156
2157 }
2158
2159 /* GAIN ENVELOPE EDITING */
2160
2161 void
2162 Editor::edit_envelope ()
2163 {
2164 }
2165
2166 /* PLAYBACK */
2167
2168 void
2169 Editor::transition_to_rolling (bool fwd)
2170 {
2171         if (!session) {
2172                 return;
2173         }
2174
2175         switch (Config->get_slave_source()) {
2176         case None:
2177         case JACK:
2178                 break;
2179         default:
2180                 /* transport controlled by the master */
2181                 return;
2182         }
2183
2184         if (session->is_auditioning()) {
2185                 session->cancel_audition ();
2186                 return;
2187         }
2188         
2189         session->request_transport_speed (fwd ? 1.0f : -1.0f);
2190 }
2191
2192 void
2193 Editor::toggle_playback (bool with_abort)
2194 {
2195         if (!session) {
2196                 return;
2197         }
2198
2199         switch (Config->get_slave_source()) {
2200         case None:
2201         case JACK:
2202                 break;
2203         default:
2204                 /* transport controlled by the master */
2205                 return;
2206         }
2207
2208         if (session->is_auditioning()) {
2209                 session->cancel_audition ();
2210                 return;
2211         }
2212         
2213         if (session->transport_rolling()) {
2214                 session->request_stop (with_abort);
2215                 if (session->get_play_loop()) {
2216                         session->request_play_loop (false);
2217                 }
2218         } else {
2219                 session->request_transport_speed (1.0f);
2220         }
2221 }
2222
2223 void
2224 Editor::play_from_start ()
2225 {
2226         session->request_locate (session->current_start_frame(), true);
2227 }
2228
2229 void
2230 Editor::play_from_edit_point ()
2231 {
2232         session->request_locate (get_preferred_edit_position(), true);
2233 }
2234
2235 void
2236 Editor::play_from_edit_point_and_return ()
2237 {
2238         nframes64_t start_frame;
2239         nframes64_t return_frame;
2240
2241         /* don't reset the return frame if its already set */
2242
2243         if ((return_frame = session->requested_return_frame()) < 0) {
2244                 return_frame = session->audible_frame();
2245         }
2246
2247         start_frame = get_preferred_edit_position (true);
2248
2249         if (start_frame >= 0) {
2250                 session->request_roll_at_and_return (start_frame, return_frame);
2251         }
2252 }
2253
2254 void
2255 Editor::play_selection ()
2256 {
2257         if (selection->time.empty()) {
2258                 return;
2259         }
2260
2261         session->request_play_range (true);
2262 }
2263
2264 void
2265 Editor::loop_selected_region ()
2266 {
2267         if (!selection->regions.empty()) {
2268                 RegionView *rv = *(selection->regions.begin());
2269                 Location* tll;
2270
2271                 if ((tll = transport_loop_location()) != 0)  {
2272
2273                         tll->set (rv->region()->position(), rv->region()->last_frame());
2274                         
2275                         // enable looping, reposition and start rolling
2276
2277                         session->request_play_loop (true);
2278                         session->request_locate (tll->start(), false);
2279                         session->request_transport_speed (1.0f);
2280                 }
2281         }
2282 }
2283
2284 void
2285 Editor::play_location (Location& location)
2286 {
2287         if (location.start() <= location.end()) {
2288                 return;
2289         }
2290
2291         session->request_bounded_roll (location.start(), location.end());
2292 }
2293
2294 void
2295 Editor::loop_location (Location& location)
2296 {
2297         if (location.start() <= location.end()) {
2298                 return;
2299         }
2300
2301         Location* tll;
2302
2303         if ((tll = transport_loop_location()) != 0) {
2304                 tll->set (location.start(), location.end());
2305
2306                 // enable looping, reposition and start rolling
2307                 session->request_play_loop (true);
2308                 session->request_locate (tll->start(), true);
2309         }
2310 }
2311
2312 void
2313 Editor::raise_region ()
2314 {
2315         selection->foreach_region (&Region::raise);
2316 }
2317
2318 void
2319 Editor::raise_region_to_top ()
2320 {
2321         selection->foreach_region (&Region::raise_to_top);
2322 }
2323
2324 void
2325 Editor::lower_region ()
2326 {
2327         selection->foreach_region (&Region::lower);
2328 }
2329
2330 void
2331 Editor::lower_region_to_bottom ()
2332 {
2333         selection->foreach_region (&Region::lower_to_bottom);
2334 }
2335
2336 void
2337 Editor::edit_region ()
2338 {
2339         if (clicked_regionview == 0) {
2340                 return;
2341         }
2342         
2343         clicked_regionview->show_region_editor ();
2344 }
2345
2346 void
2347 Editor::rename_region()
2348 {
2349         if (selection->regions.empty()) {
2350                 return;
2351         }
2352
2353         WindowTitle title (Glib::get_application_name());
2354         title += _("Rename Region");
2355
2356         ArdourDialog d (*this, title.get_string(), true, false);
2357         Entry entry;
2358         Label label (_("New name:"));
2359         HBox hbox;
2360
2361         hbox.set_spacing (6);
2362         hbox.pack_start (label, false, false);
2363         hbox.pack_start (entry, true, true);
2364
2365         d.get_vbox()->set_border_width (12);
2366         d.get_vbox()->pack_start (hbox, false, false);
2367
2368         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2369         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2370
2371         d.set_size_request (300, -1);
2372         d.set_position (Gtk::WIN_POS_MOUSE);
2373
2374         entry.set_text (selection->regions.front()->region()->name());
2375         entry.select_region (0, -1);
2376
2377         entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK));
2378         
2379         d.show_all ();
2380         
2381         entry.grab_focus();
2382
2383         int ret = d.run();
2384
2385         d.hide ();
2386
2387         if (ret == RESPONSE_OK) {
2388                 std::string str = entry.get_text();
2389                 strip_whitespace_edges (str);
2390                 if (!str.empty()) {
2391                         selection->regions.front()->region()->set_name (str);
2392                         redisplay_regions ();
2393                 }
2394         }
2395 }
2396
2397 void
2398 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2399 {
2400         if (session->is_auditioning()) {
2401                 session->cancel_audition ();
2402         } 
2403
2404         // note: some potential for creativity here, because region doesn't
2405         // have to belong to the playlist that Route is handling
2406
2407         // bool was_soloed = route.soloed();
2408
2409         route.set_solo (true, this);
2410         
2411         session->request_bounded_roll (region->position(), region->position() + region->length());
2412         
2413         /* XXX how to unset the solo state ? */
2414 }
2415
2416 void
2417 Editor::play_edit_range ()
2418 {
2419         nframes64_t start, end;
2420
2421         if (get_edit_op_range (start, end)) {
2422                 session->request_bounded_roll (start, end);
2423         }
2424 }
2425
2426 void
2427 Editor::play_selected_region ()
2428 {
2429         nframes64_t start = max_frames;
2430         nframes64_t end = 0;
2431
2432         ExclusiveRegionSelection esr (*this, entered_regionview);
2433
2434         if (selection->regions.empty()) {
2435                 return;
2436         }
2437
2438         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2439                 if ((*i)->region()->position() < start) {
2440                         start = (*i)->region()->position();
2441                 }
2442                 if ((*i)->region()->last_frame() + 1 > end) {
2443                         end = (*i)->region()->last_frame() + 1;
2444                 }
2445         }
2446
2447         session->request_stop ();
2448         session->request_bounded_roll (start, end);
2449 }
2450
2451 void
2452 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2453 {
2454         session->audition_region (region);
2455 }
2456
2457 void
2458 Editor::build_interthread_progress_window ()
2459 {
2460         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2461
2462         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2463         
2464         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2465         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2466
2467         // GTK2FIX: this button needs a modifiable label
2468
2469         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2470         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2471
2472         interthread_cancel_button.add (interthread_cancel_label);
2473
2474         interthread_progress_window->set_default_size (200, 100);
2475 }
2476
2477 void
2478 Editor::interthread_cancel_clicked ()
2479 {
2480         if (current_interthread_info) {
2481                 current_interthread_info->cancel = true;
2482         }
2483 }
2484
2485 void
2486 Editor::region_from_selection ()
2487 {
2488         if (clicked_trackview == 0) {
2489                 return;
2490         }
2491
2492         if (selection->time.empty()) {
2493                 return;
2494         }
2495
2496         nframes_t start = selection->time[clicked_selection].start;
2497         nframes_t end = selection->time[clicked_selection].end;
2498
2499         nframes_t selection_cnt = end - start + 1;
2500         
2501         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2502                 boost::shared_ptr<AudioRegion> current;
2503                 boost::shared_ptr<Region> current_r;
2504                 boost::shared_ptr<Playlist> pl;
2505
2506                 nframes_t internal_start;
2507                 string new_name;
2508
2509                 if ((pl = (*i)->playlist()) == 0) {
2510                         continue;
2511                 }
2512
2513                 if ((current_r = pl->top_region_at (start)) == 0) {
2514                         continue;
2515                 }
2516
2517                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2518                 // FIXME: audio only
2519                 if (current != 0) {
2520                         internal_start = start - current->position();
2521                         session->region_name (new_name, current->name(), true);
2522                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2523                 }
2524         }
2525 }       
2526
2527 void
2528 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2529 {
2530         if (selection->time.empty() || selection->tracks.empty()) {
2531                 return;
2532         }
2533
2534         nframes_t start = selection->time[clicked_selection].start;
2535         nframes_t end = selection->time[clicked_selection].end;
2536         
2537         sort_track_selection ();
2538
2539         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2540
2541                 boost::shared_ptr<AudioRegion> current;
2542                 boost::shared_ptr<Region> current_r;
2543                 boost::shared_ptr<Playlist> playlist;
2544                 nframes_t internal_start;
2545                 string new_name;
2546
2547                 if ((playlist = (*i)->playlist()) == 0) {
2548                         continue;
2549                 }
2550
2551                 if ((current_r = playlist->top_region_at(start)) == 0) {
2552                         continue;
2553                 }
2554
2555                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2556                         continue;
2557                 }
2558         
2559                 internal_start = start - current->position();
2560                 session->region_name (new_name, current->name(), true);
2561                 
2562                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2563         }
2564 }
2565
2566 void
2567 Editor::split_multichannel_region ()
2568 {
2569         if (selection->regions.empty()) {
2570                 return;
2571         }
2572
2573         vector<boost::shared_ptr<AudioRegion> > v;
2574
2575         for (list<RegionView*>::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2576
2577                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2578                 
2579                 if (!arv || arv->audio_region()->n_channels() < 2) {
2580                         continue;
2581                 }
2582
2583                 (arv)->audio_region()->separate_by_channel (*session, v);
2584         }
2585 }
2586
2587 void
2588 Editor::new_region_from_selection ()
2589 {
2590         region_from_selection ();
2591         cancel_selection ();
2592 }
2593
2594 static void
2595 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2596 {
2597         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2598         case OverlapNone:
2599                 break;
2600         default:
2601                 rs->push_back (rv);
2602         }
2603 }
2604
2605 void
2606 Editor::separate_regions_between (const TimeSelection& ts)
2607 {
2608         bool in_command = false;
2609         boost::shared_ptr<Playlist> playlist;
2610         RegionSelection new_selection;
2611         TrackSelection tmptracks;
2612
2613         if (selection->tracks.empty()) {
2614                 
2615                 /* use tracks with selected regions */
2616
2617                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2618                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2619
2620                         if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) {
2621                                 tmptracks.push_back (tv);
2622                         }
2623                 }
2624
2625                 if (tmptracks.empty()) {
2626                         /* no regions selected: use all tracks */
2627                         tmptracks = track_views;
2628                 }
2629
2630         } else {
2631
2632                 tmptracks = selection->tracks;
2633
2634         }
2635
2636         sort_track_selection (&tmptracks);
2637
2638         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2639
2640                 AudioTimeAxisView* atv;
2641
2642                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2643
2644                         if (atv->is_audio_track()) {
2645
2646                                 /* no edits to destructive tracks */
2647
2648                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2649                                         continue;
2650                                 }
2651                                         
2652                                 if ((playlist = atv->playlist()) != 0) {
2653
2654
2655                                         XMLNode *before;
2656                                         bool got_some;
2657
2658                                         before = &(playlist->get_state());
2659                                         got_some = false;
2660
2661                                         /* XXX need to consider musical time selections here at some point */
2662
2663                                         double speed = atv->get_diskstream()->speed();
2664
2665
2666                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2667
2668                                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2669                                                 latest_regionviews.clear ();
2670
2671                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2672
2673                                                 c.disconnect ();
2674
2675                                                 if (!latest_regionviews.empty()) {
2676                                                         
2677                                                         got_some = true;
2678
2679                                                         atv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2680                                                         
2681                                                         if (!in_command) {
2682                                                                 begin_reversible_command (_("separate"));
2683                                                                 in_command = true;
2684                                                         }
2685                                                         
2686                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2687                                                         
2688                                                 } 
2689                                         }
2690
2691                                         if (!got_some) {
2692                                                 delete before;
2693                                         }
2694                                 }
2695                         }
2696                 }
2697         }
2698
2699         if (in_command) {
2700                 selection->set (new_selection);
2701                 set_mouse_mode (MouseObject);
2702
2703                 commit_reversible_command ();
2704         }
2705 }
2706
2707 void
2708 Editor::separate_region_from_selection ()
2709 {
2710         /* preferentially use *all* ranges in the time selection if we're in range mode
2711            to allow discontiguous operation, since get_edit_op_range() currently
2712            returns a single range.
2713         */
2714         if (mouse_mode == MouseRange && !selection->time.empty()) {
2715
2716                 separate_regions_between (selection->time);
2717
2718         } else {
2719
2720                 nframes64_t start;
2721                 nframes64_t end;
2722                 
2723                 if (get_edit_op_range (start, end)) {
2724                         
2725                         AudioRange ar (start, end, 1);
2726                         TimeSelection ts;
2727                         ts.push_back (ar);
2728
2729                         /* force track selection */
2730
2731                         ensure_entered_region_selected ();
2732                         
2733                         separate_regions_between (ts);
2734                 }
2735         }
2736 }
2737
2738 void
2739 Editor::separate_regions_using_location (Location& loc)
2740 {
2741         if (loc.is_mark()) {
2742                 return;
2743         }
2744
2745         AudioRange ar (loc.start(), loc.end(), 1);
2746         TimeSelection ts;
2747
2748         ts.push_back (ar);
2749
2750         separate_regions_between (ts);
2751 }
2752
2753 void
2754 Editor::crop_region_to_selection ()
2755 {
2756         ensure_entered_region_selected (true);
2757
2758         if (!selection->time.empty()) {
2759
2760                 crop_region_to (selection->time.start(), selection->time.end_frame());
2761
2762         } else {
2763
2764                 nframes64_t start;
2765                 nframes64_t end;
2766
2767                 if (get_edit_op_range (start, end)) {
2768                         crop_region_to (start, end);
2769                 }
2770         }
2771                 
2772 }               
2773
2774 void
2775 Editor::crop_region_to (nframes_t start, nframes_t end)
2776 {
2777         vector<boost::shared_ptr<Playlist> > playlists;
2778         boost::shared_ptr<Playlist> playlist;
2779         TrackSelection* ts;
2780
2781         if (selection->tracks.empty()) {
2782                 ts = &track_views;
2783         } else {
2784                 sort_track_selection ();
2785                 ts = &selection->tracks;
2786         }
2787         
2788         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2789                 
2790                 AudioTimeAxisView* atv;
2791                 
2792                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2793                         
2794                         if (atv->is_audio_track()) {
2795                                 
2796                                 /* no edits to destructive tracks */
2797
2798                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2799                                         continue;
2800                                 }
2801
2802                                 if ((playlist = atv->playlist()) != 0) {
2803                                         playlists.push_back (playlist);
2804                                 }
2805                         }
2806                 }
2807         }
2808
2809         if (playlists.empty()) {
2810                 return;
2811         }
2812                 
2813         nframes_t the_start;
2814         nframes_t the_end;
2815         nframes_t cnt;
2816         
2817         begin_reversible_command (_("trim to selection"));
2818         
2819         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2820                 
2821                 boost::shared_ptr<Region> region;
2822         
2823                 the_start = start;
2824         
2825                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2826                         continue;
2827                 }
2828                 
2829                 /* now adjust lengths to that we do the right thing
2830                    if the selection extends beyond the region
2831                 */
2832                 
2833                 the_start = max (the_start, region->position());
2834                 if (max_frames - the_start < region->length()) {
2835                         the_end = the_start + region->length() - 1;
2836                 } else {
2837                         the_end = max_frames;
2838                 }
2839                 the_end = min (end, the_end);
2840                 cnt = the_end - the_start + 1;
2841                 
2842                 XMLNode &before = (*i)->get_state();
2843                 region->trim_to (the_start, cnt, this);
2844                 XMLNode &after = (*i)->get_state();
2845                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2846         }
2847         
2848         commit_reversible_command ();
2849 }               
2850
2851 void
2852 Editor::region_fill_track ()
2853 {
2854         nframes_t end;
2855
2856         if (!session || selection->regions.empty()) {
2857                 return;
2858         }
2859
2860         end = session->current_end_frame ();
2861
2862         begin_reversible_command (_("region fill"));
2863
2864         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2865
2866                 boost::shared_ptr<Region> region ((*i)->region());
2867                 
2868                 // FIXME
2869                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2870                 if (!ar)
2871                         continue;
2872
2873                 boost::shared_ptr<Playlist> pl = region->playlist();
2874
2875                 if (end <= region->last_frame()) {
2876                         return;
2877                 }
2878
2879                 double times = (double) (end - region->last_frame()) / (double) region->length();
2880
2881                 if (times == 0) {
2882                         return;
2883                 }
2884
2885                 XMLNode &before = pl->get_state();
2886                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2887                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2888         }
2889
2890         commit_reversible_command ();
2891 }
2892
2893 void
2894 Editor::region_fill_selection ()
2895 {
2896         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2897                 return;
2898         }
2899
2900         if (selection->time.empty()) {
2901                 return;
2902         }
2903
2904
2905         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2906
2907         if (selected->count_selected_rows() != 1) {
2908                 return;
2909         }
2910
2911         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2912         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2913
2914         nframes_t start = selection->time[clicked_selection].start;
2915         nframes_t end = selection->time[clicked_selection].end;
2916
2917         boost::shared_ptr<Playlist> playlist; 
2918
2919         if (selection->tracks.empty()) {
2920                 return;
2921         }
2922
2923         nframes_t selection_length = end - start;
2924         float times = (float)selection_length / region->length();
2925         
2926         begin_reversible_command (_("fill selection"));
2927         
2928         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2929
2930                 if ((playlist = (*i)->playlist()) == 0) {
2931                         continue;
2932                 }               
2933                 
2934                 XMLNode &before = playlist->get_state();
2935                 playlist->add_region (RegionFactory::create (region), start, times);
2936                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2937         }
2938         
2939         commit_reversible_command ();                   
2940 }
2941
2942 void
2943 Editor::set_region_sync_from_edit_point ()
2944 {
2945         nframes64_t where = get_preferred_edit_position ();
2946         ensure_entered_region_selected (true);
2947         set_sync_point (where, selection->regions);
2948 }
2949
2950 void
2951 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
2952 {
2953         bool in_command = false;
2954
2955         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2956                 
2957                 if (!(*r)->region()->covers (where)) {
2958                         continue;
2959                 }
2960
2961                 boost::shared_ptr<Region> region ((*r)->region());
2962
2963                 if (!in_command) {
2964                         begin_reversible_command (_("set sync point"));
2965                         in_command = true;
2966                 }
2967
2968                 XMLNode &before = region->playlist()->get_state();
2969                 region->set_sync_position (get_preferred_edit_position());
2970                 XMLNode &after = region->playlist()->get_state();
2971                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2972         }
2973
2974         if (in_command) {
2975                 commit_reversible_command ();
2976         }
2977 }
2978
2979 void
2980 Editor::remove_region_sync ()
2981 {
2982         if (clicked_regionview) {
2983                 boost::shared_ptr<Region> region (clicked_regionview->region());
2984                 begin_reversible_command (_("remove sync"));
2985                 XMLNode &before = region->playlist()->get_state();
2986                 region->clear_sync_position ();
2987                 XMLNode &after = region->playlist()->get_state();
2988                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2989                 commit_reversible_command ();
2990         }
2991 }
2992
2993 void
2994 Editor::naturalize ()
2995 {
2996         if (selection->regions.empty()) {
2997                 return;
2998         }
2999         begin_reversible_command (_("naturalize"));
3000         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3001                 XMLNode &before = (*i)->region()->get_state();
3002                 (*i)->region()->move_to_natural_position (this);
3003                 XMLNode &after = (*i)->region()->get_state();
3004                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
3005         }
3006         commit_reversible_command ();
3007 }
3008
3009 void
3010 Editor::align (RegionPoint what)
3011 {
3012         ensure_entered_region_selected ();
3013
3014         nframes64_t where = get_preferred_edit_position();
3015
3016         if (!selection->regions.empty()) {
3017                 align_selection (what, where, selection->regions);
3018         } else {
3019
3020                 RegionSelection rs;
3021                 rs = get_regions_at (where, selection->tracks);
3022                 align_selection (what, where, rs);
3023         }
3024 }
3025
3026 void
3027 Editor::align_relative (RegionPoint what)
3028 {
3029         nframes64_t where = get_preferred_edit_position();
3030
3031         if (!selection->regions.empty()) {
3032                 align_selection_relative (what, where, selection->regions);
3033         } else {
3034
3035                 RegionSelection rs;
3036                 rs = get_regions_at (where, selection->tracks);
3037                 align_selection_relative (what, where, rs);
3038         }
3039 }
3040
3041 struct RegionSortByTime {
3042     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
3043             return a->region()->position() < b->region()->position();
3044     }
3045 };
3046
3047 void
3048 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
3049 {
3050         if (rs.empty()) {
3051                 return;
3052         }
3053
3054         nframes_t distance;
3055         nframes_t pos = 0;
3056         int dir;
3057
3058         list<RegionView*> sorted;
3059         rs.by_position (sorted);
3060         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3061
3062         switch (point) {
3063         case Start:
3064                 pos = position;
3065                 if (position > r->position()) {
3066                         distance = position - r->position();
3067                         dir = 1;
3068                 } else {
3069                         distance = r->position() - position;
3070                         dir = -1;
3071                 }
3072                 break;
3073                 
3074         case End:
3075                 if (position > r->last_frame()) {
3076                         distance = position - r->last_frame();
3077                         pos = r->position() + distance;
3078                         dir = 1;
3079                 } else {
3080                         distance = r->last_frame() - position;
3081                         pos = r->position() - distance;
3082                         dir = -1;
3083                 }
3084                 break;
3085
3086         case SyncPoint:
3087                 pos = r->adjust_to_sync (position);
3088                 if (pos > r->position()) {
3089                         distance = pos - r->position();
3090                         dir = 1;
3091                 } else {
3092                         distance = r->position() - pos;
3093                         dir = -1;
3094                 }
3095                 break;  
3096         }
3097
3098         if (pos == r->position()) {
3099                 return;
3100         }
3101
3102         begin_reversible_command (_("align selection (relative)"));
3103
3104         /* move first one specially */
3105
3106         XMLNode &before = r->playlist()->get_state();
3107         r->set_position (pos, this);
3108         XMLNode &after = r->playlist()->get_state();
3109         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
3110
3111         /* move rest by the same amount */
3112         
3113         RegionSelection::const_iterator i = rs.begin();
3114         ++i;
3115
3116         for (; i != rs.end(); ++i) {
3117
3118                 boost::shared_ptr<Region> region ((*i)->region());
3119
3120                 XMLNode &before = region->playlist()->get_state();
3121                 
3122                 if (dir > 0) {
3123                         region->set_position (region->position() + distance, this);
3124                 } else {
3125                         region->set_position (region->position() - distance, this);
3126                 }
3127
3128                 XMLNode &after = region->playlist()->get_state();
3129                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3130
3131         }
3132
3133         commit_reversible_command ();
3134 }
3135
3136 void
3137 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
3138 {
3139         if (rs.empty()) {
3140                 return;
3141         }
3142
3143         begin_reversible_command (_("align selection"));
3144
3145         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3146                 align_region_internal ((*i)->region(), point, position);
3147         }
3148
3149         commit_reversible_command ();
3150 }
3151
3152 void
3153 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3154 {
3155         begin_reversible_command (_("align region"));
3156         align_region_internal (region, point, position);
3157         commit_reversible_command ();
3158 }
3159
3160 void
3161 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3162 {
3163         XMLNode &before = region->playlist()->get_state();
3164
3165         switch (point) {
3166         case SyncPoint:
3167                 region->set_position (region->adjust_to_sync (position), this);
3168                 break;
3169
3170         case End:
3171                 if (position > region->length()) {
3172                         region->set_position (position - region->length(), this);
3173                 }
3174                 break;
3175
3176         case Start:
3177                 region->set_position (position, this);
3178                 break;
3179         }
3180
3181         XMLNode &after = region->playlist()->get_state();
3182         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3183 }       
3184
3185 void
3186 Editor::trim_region_to_loop ()
3187 {
3188         Location* loc = session->locations()->auto_loop_location();
3189         if (!loc) {
3190                 return;
3191         }
3192         trim_region_to_location (*loc, _("trim to loop"));
3193 }
3194
3195 void
3196 Editor::trim_region_to_punch ()
3197 {
3198         Location* loc = session->locations()->auto_punch_location();
3199         if (!loc) {
3200                 return;
3201         }
3202         trim_region_to_location (*loc, _("trim to punch"));
3203 }
3204
3205 void
3206 Editor::trim_region_to_location (const Location& loc, const char* str)
3207 {
3208         ExclusiveRegionSelection ers (*this, entered_regionview);
3209
3210         RegionSelection& rs (get_regions_for_action ());
3211
3212         begin_reversible_command (str);
3213
3214         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3215                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3216
3217                 if (!arv) {
3218                         continue;
3219                 }
3220
3221                 /* require region to span proposed trim */
3222
3223                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3224                 case OverlapInternal:
3225                         break;
3226                 default:
3227                         continue;
3228                 }
3229                                 
3230                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3231
3232                 if (!atav) {
3233                         return;
3234                 }
3235
3236                 float speed = 1.0;
3237                 nframes_t start;
3238                 nframes_t end;
3239
3240                 if (atav->get_diskstream() != 0) {
3241                         speed = atav->get_diskstream()->speed();
3242                 }
3243
3244                 start = session_frame_to_track_frame (loc.start(), speed);
3245                 end = session_frame_to_track_frame (loc.end(), speed);
3246
3247                 XMLNode &before = arv->region()->playlist()->get_state();
3248                 arv->region()->trim_to (start, (end - start), this);
3249                 XMLNode &after = arv->region()->playlist()->get_state();
3250                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3251         }
3252                 
3253         commit_reversible_command ();
3254 }
3255
3256 void
3257 Editor::trim_region_to_edit_point ()
3258 {
3259         ExclusiveRegionSelection ers (*this, entered_regionview);
3260
3261         RegionSelection& rs (get_regions_for_action ());
3262         nframes64_t where = get_preferred_edit_position();
3263
3264         begin_reversible_command (_("trim region start to edit point"));
3265
3266         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3267                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3268
3269                 if (!arv) {
3270                         continue;
3271                 }
3272
3273                 /* require region to cover trim */
3274
3275                 if (!arv->region()->covers (where)) {
3276                         continue;
3277                 }
3278
3279                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3280
3281                 if (!atav) {
3282                         return;
3283                 }
3284
3285                 float speed = 1.0;
3286
3287                 if (atav->get_diskstream() != 0) {
3288                         speed = atav->get_diskstream()->speed();
3289                 }
3290
3291                 XMLNode &before = arv->region()->playlist()->get_state();
3292                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3293                 XMLNode &after = arv->region()->playlist()->get_state();
3294                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3295         }
3296                 
3297         commit_reversible_command ();
3298 }
3299
3300 void
3301 Editor::trim_region_from_edit_point ()
3302 {
3303         ExclusiveRegionSelection ers (*this, entered_regionview);
3304
3305         RegionSelection& rs (get_regions_for_action ());
3306         nframes64_t where = get_preferred_edit_position();
3307
3308         begin_reversible_command (_("trim region end to edit point"));
3309
3310         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3311                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3312
3313                 if (!arv) {
3314                         continue;
3315                 }
3316
3317                 /* require region to cover trim */
3318
3319                 if (!arv->region()->covers (where)) {
3320                         continue;
3321                 }
3322
3323                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3324
3325                 if (!atav) {
3326                         return;
3327                 }
3328
3329                 float speed = 1.0;
3330
3331                 if (atav->get_diskstream() != 0) {
3332                         speed = atav->get_diskstream()->speed();
3333                 }
3334
3335                 XMLNode &before = arv->region()->playlist()->get_state();
3336                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3337                 XMLNode &after = arv->region()->playlist()->get_state();
3338                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3339         }
3340                 
3341         commit_reversible_command ();
3342 }
3343
3344 void
3345 Editor::unfreeze_route ()
3346 {
3347         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3348                 return;
3349         }
3350         
3351         clicked_audio_trackview->audio_track()->unfreeze ();
3352 }
3353
3354 void*
3355 Editor::_freeze_thread (void* arg)
3356 {
3357         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
3358         return static_cast<Editor*>(arg)->freeze_thread ();
3359 }
3360
3361 void*
3362 Editor::freeze_thread ()
3363 {
3364         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
3365         return 0;
3366 }
3367
3368 gint
3369 Editor::freeze_progress_timeout (void *arg)
3370 {
3371         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
3372         return !(current_interthread_info->done || current_interthread_info->cancel);
3373 }
3374
3375 void
3376 Editor::freeze_route ()
3377 {
3378         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3379                 return;
3380         }
3381         
3382         InterThreadInfo itt;
3383
3384         if (interthread_progress_window == 0) {
3385                 build_interthread_progress_window ();
3386         }
3387
3388         WindowTitle title(Glib::get_application_name());
3389         title += _("Freeze");
3390         interthread_progress_window->set_title (title.get_string());
3391         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3392         interthread_progress_window->show_all ();
3393         interthread_progress_bar.set_fraction (0.0f);
3394         interthread_progress_label.set_text ("");
3395         interthread_cancel_label.set_text (_("Cancel Freeze"));
3396         current_interthread_info = &itt;
3397
3398         interthread_progress_connection = 
3399           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3400
3401         itt.done = false;
3402         itt.cancel = false;
3403         itt.progress = 0.0f;
3404         
3405         pthread_attr_t attr;
3406         pthread_attr_init(&attr);
3407         pthread_attr_setstacksize(&attr, 500000);
3408
3409         pthread_create (&itt.thread, &attr, _freeze_thread, this);
3410
3411         pthread_attr_destroy(&attr);
3412
3413         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3414
3415         while (!itt.done && !itt.cancel) {
3416                 gtk_main_iteration ();
3417         }
3418
3419         interthread_progress_connection.disconnect ();
3420         interthread_progress_window->hide_all ();
3421         current_interthread_info = 0;
3422         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3423 }
3424
3425 void
3426 Editor::bounce_range_selection ()
3427 {
3428         if (selection->time.empty()) {
3429                 return;
3430         }
3431
3432         TrackSelection views = selection->tracks;
3433
3434         nframes_t start = selection->time[clicked_selection].start;
3435         nframes_t end = selection->time[clicked_selection].end;
3436         nframes_t cnt = end - start + 1;
3437
3438         begin_reversible_command (_("bounce range"));
3439
3440         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3441
3442                 AudioTimeAxisView* atv;
3443
3444                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
3445                         continue;
3446                 }
3447                 
3448                 boost::shared_ptr<Playlist> playlist;
3449                 
3450                 if ((playlist = atv->playlist()) == 0) {
3451                         return;
3452                 }
3453
3454                 InterThreadInfo itt;
3455                 
3456                 itt.done = false;
3457                 itt.cancel = false;
3458                 itt.progress = false;
3459
3460                 XMLNode &before = playlist->get_state();
3461                 atv->audio_track()->bounce_range (start, cnt, itt);
3462                 XMLNode &after = playlist->get_state();
3463                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3464         }
3465         
3466         commit_reversible_command ();
3467 }
3468
3469 void
3470 Editor::cut ()
3471 {
3472         cut_copy (Cut);
3473 }
3474
3475 void
3476 Editor::copy ()
3477 {
3478         cut_copy (Copy);
3479 }
3480
3481 void 
3482 Editor::cut_copy (CutCopyOp op)
3483 {
3484         /* only cancel selection if cut/copy is successful.*/
3485
3486         string opname;
3487
3488         switch (op) {
3489         case Cut:
3490                 opname = _("cut");
3491                 break;
3492         case Copy:
3493                 opname = _("copy");
3494                 break;
3495         case Clear:
3496                 opname = _("clear");
3497                 break;
3498         }
3499         
3500         cut_buffer->clear ();
3501
3502         if (entered_marker) {
3503
3504                 /* cut/delete op while pointing at a marker */
3505
3506                 bool ignored;
3507                 Location* loc = find_location_from_marker (entered_marker, ignored);
3508
3509                 if (session && loc) {
3510                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3511                 }
3512
3513                 return;
3514         }
3515
3516         switch (current_mouse_mode()) {
3517         case MouseObject: 
3518                 if (!selection->regions.empty() || !selection->points.empty()) {
3519
3520                         begin_reversible_command (opname + _(" objects"));
3521
3522                         if (!selection->regions.empty()) {
3523                                 cut_copy_regions (op);
3524                                 
3525                                 if (op == Cut) {
3526                                         selection->clear_regions ();
3527                                 }
3528                         }
3529
3530                         if (!selection->points.empty()) {
3531                                 cut_copy_points (op);
3532
3533                                 if (op == Cut) {
3534                                         selection->clear_points ();
3535                                 }
3536                         }
3537
3538                         commit_reversible_command ();   
3539                         break; // terminate case statement here
3540                 } 
3541                 if (!selection->time.empty()) {
3542                         /* don't cause suprises */
3543                         break;
3544                 }
3545                 // fall thru if there was nothing selected
3546                 
3547         case MouseRange:
3548                 if (selection->time.empty()) {
3549                         nframes64_t start, end;
3550                         if (!get_edit_op_range (start, end)) {
3551                                 return;
3552                         }
3553                         selection->set ((TimeAxisView*) 0, start, end);
3554                 }
3555                         
3556                 begin_reversible_command (opname + _(" range"));
3557                 cut_copy_ranges (op);
3558                 commit_reversible_command ();
3559                 
3560                 if (op == Cut) {
3561                         selection->clear_time ();
3562                 }
3563
3564                 break;
3565                 
3566         default:
3567                 break;
3568         }
3569 }
3570
3571 void
3572 Editor::cut_copy_points (CutCopyOp op)
3573 {
3574         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3575
3576                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3577
3578                 if (atv) {
3579                         atv->cut_copy_clear_objects (selection->points, op);
3580                 } 
3581         }
3582 }
3583
3584 struct PlaylistState {
3585     boost::shared_ptr<Playlist> playlist;
3586     XMLNode*  before;
3587 };
3588
3589 struct lt_playlist {
3590     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3591             return a.playlist < b.playlist;
3592     }
3593 };
3594         
3595 struct PlaylistMapping { 
3596     TimeAxisView* tv;
3597     boost::shared_ptr<AudioPlaylist> pl;
3598
3599     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3600 };
3601
3602 void
3603 Editor::cut_copy_regions (CutCopyOp op)
3604 {
3605         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3606            a map when we want ordered access to both elements. i think.
3607         */
3608
3609         vector<PlaylistMapping> pmap;
3610
3611         nframes_t first_position = max_frames;
3612         
3613         set<PlaylistState, lt_playlist> freezelist;
3614         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3615         
3616         /* get ordering correct before we cut/copy */
3617         
3618         selection->regions.sort_by_position_and_track ();
3619
3620         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3621
3622                 first_position = min ((*x)->region()->position(), first_position);
3623
3624                 if (op == Cut || op == Clear) {
3625                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3626
3627                         if (pl) {
3628
3629                                 PlaylistState before;
3630                                 before.playlist = pl;
3631                                 before.before = &pl->get_state();
3632                                 
3633                                 insert_result = freezelist.insert (before);
3634                                 
3635                                 if (insert_result.second) {
3636                                         pl->freeze ();
3637                                 }
3638                         }
3639                 }
3640
3641                 TimeAxisView* tv = &(*x)->get_trackview();
3642                 vector<PlaylistMapping>::iterator z;
3643
3644                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3645                         if ((*z).tv == tv) {
3646                                 break;
3647                         }
3648                 }
3649                 
3650                 if (z == pmap.end()) {
3651                         pmap.push_back (PlaylistMapping (tv));
3652                 }
3653         }
3654
3655         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3656
3657                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3658                 
3659                 if (!pl) {
3660                         /* impossible, but this handles it for the future */
3661                         continue;
3662                 }
3663
3664                 TimeAxisView& tv = (*x)->get_trackview();
3665                 boost::shared_ptr<AudioPlaylist> npl;
3666                 RegionSelection::iterator tmp;
3667                 
3668                 tmp = x;
3669                 ++tmp;
3670
3671                 vector<PlaylistMapping>::iterator z;
3672                 
3673                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3674                         if ((*z).tv == &tv) {
3675                                 break;
3676                         }
3677                 }
3678                 
3679                 assert (z != pmap.end());
3680                 
3681                 if (!(*z).pl) {
3682                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3683                         npl->freeze();
3684                         (*z).pl = npl;
3685                 } else {
3686                         npl = (*z).pl;
3687                 }
3688                 
3689                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3690                 boost::shared_ptr<Region> _xx;
3691                 
3692                 switch (op) {
3693                 case Cut:
3694                         if (!ar) break;
3695                         
3696                         _xx = RegionFactory::create ((*x)->region());
3697                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3698                         pl->remove_region (((*x)->region()));
3699                         break;
3700                         
3701                 case Copy:
3702                         if (!ar) break;
3703
3704                         /* copy region before adding, so we're not putting same object into two different playlists */
3705                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3706                         break;
3707                         
3708                 case Clear:
3709                         pl->remove_region (((*x)->region()));
3710                         break;
3711                 }
3712
3713                 x = tmp;
3714         }
3715         
3716         list<boost::shared_ptr<Playlist> > foo;
3717         
3718         /* the pmap is in the same order as the tracks in which selected regions occured */
3719         
3720         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3721                 (*i).pl->thaw();
3722                 foo.push_back ((*i).pl);
3723         }
3724         
3725
3726         if (!foo.empty()) {
3727                 cut_buffer->set (foo);
3728         }
3729
3730         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3731                 (*pl).playlist->thaw ();
3732                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3733         }
3734 }
3735
3736 void
3737 Editor::cut_copy_ranges (CutCopyOp op)
3738 {
3739         TrackSelection* ts;
3740
3741         if (selection->tracks.empty()) {
3742                 ts = &track_views;
3743         } else {
3744                 ts = &selection->tracks;
3745         }
3746
3747         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3748                 (*i)->cut_copy_clear (*selection, op);
3749         }
3750 }
3751
3752 void
3753 Editor::paste (float times)
3754 {
3755         paste_internal (get_preferred_edit_position(), times);
3756 }
3757
3758 void
3759 Editor::mouse_paste ()
3760 {
3761         nframes64_t where;
3762         bool ignored;
3763
3764         if (!mouse_frame (where, ignored)) {
3765                 return;
3766         }
3767
3768         snap_to (where);
3769         paste_internal (where, 1);
3770 }
3771
3772 void
3773 Editor::paste_internal (nframes_t position, float times)
3774 {
3775         bool commit = false;
3776
3777         if (cut_buffer->empty()) {
3778                 return;
3779         }
3780
3781         if (position == max_frames) {
3782                 position = get_preferred_edit_position();
3783         }
3784
3785         begin_reversible_command (_("paste"));
3786
3787         TrackSelection ts;
3788         TrackSelection::iterator i;
3789         size_t nth;
3790
3791         /* get everything in the correct order */
3792
3793
3794         if (!selection->tracks.empty()) {
3795                 sort_track_selection ();
3796                 ts = selection->tracks;
3797         } else if (entered_track) {
3798                 ts.push_back (entered_track);
3799         }
3800
3801         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3802
3803                 /* undo/redo is handled by individual tracks */
3804
3805                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3806                         commit = true;
3807                 }
3808         }
3809         
3810         if (commit) {
3811                 commit_reversible_command ();
3812         }
3813 }
3814
3815 void
3816 Editor::paste_named_selection (float times)
3817 {
3818         TrackSelection::iterator t;
3819
3820         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3821
3822         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3823                 return;
3824         }
3825
3826         TreeModel::iterator i = selected->get_selected();
3827         NamedSelection* ns = (*i)[named_selection_columns.selection];
3828
3829         list<boost::shared_ptr<Playlist> >::iterator chunk;
3830         list<boost::shared_ptr<Playlist> >::iterator tmp;
3831
3832         chunk = ns->playlists.begin();
3833                 
3834         begin_reversible_command (_("paste chunk"));
3835         
3836         sort_track_selection ();
3837
3838         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3839                 
3840                 AudioTimeAxisView* atv;
3841                 boost::shared_ptr<Playlist> pl;
3842                 boost::shared_ptr<AudioPlaylist> apl;
3843
3844                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3845                         continue;
3846                 }
3847
3848                 if ((pl = atv->playlist()) == 0) {
3849                         continue;
3850                 }
3851                 
3852                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3853                         continue;
3854                 }
3855
3856                 tmp = chunk;
3857                 ++tmp;
3858
3859                 XMLNode &before = apl->get_state();
3860                 apl->paste (*chunk, get_preferred_edit_position(), times);
3861                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3862
3863                 if (tmp != ns->playlists.end()) {
3864                         chunk = tmp;
3865                 }
3866         }
3867
3868         commit_reversible_command();
3869 }
3870
3871 void
3872 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3873 {
3874         boost::shared_ptr<Playlist> playlist; 
3875         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
3876         RegionSelection foo;
3877
3878         begin_reversible_command (_("duplicate region"));
3879
3880         selection->clear_regions ();
3881
3882         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3883
3884                 boost::shared_ptr<Region> r ((*i)->region());
3885
3886                 TimeAxisView& tv = (*i)->get_time_axis_view();
3887                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3888
3889                 latest_regionviews.clear ();
3890                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3891                 
3892                 playlist = (*i)->region()->playlist();
3893                 XMLNode &before = playlist->get_state();
3894                 playlist->duplicate (r, r->last_frame() + 1, times);
3895                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3896
3897                 c.disconnect ();
3898                 
3899                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3900         }
3901
3902         commit_reversible_command ();
3903
3904         if (!foo.empty()) {
3905                 selection->set (foo);
3906         }
3907 }
3908
3909 void
3910 Editor::duplicate_selection (float times)
3911 {
3912         if (selection->time.empty() || selection->tracks.empty()) {
3913                 return;
3914         }
3915
3916         boost::shared_ptr<Playlist> playlist; 
3917         vector<boost::shared_ptr<AudioRegion> > new_regions;
3918         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3919                 
3920         create_region_from_selection (new_regions);
3921
3922         if (new_regions.empty()) {
3923                 return;
3924         }
3925         
3926         begin_reversible_command (_("duplicate selection"));
3927
3928         ri = new_regions.begin();
3929
3930         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3931                 if ((playlist = (*i)->playlist()) == 0) {
3932                         continue;
3933                 }
3934                 XMLNode &before = playlist->get_state();
3935                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3936                 XMLNode &after = playlist->get_state();
3937                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3938
3939                 ++ri;
3940                 if (ri == new_regions.end()) {
3941                         --ri;
3942                 }
3943         }
3944
3945         commit_reversible_command ();
3946 }
3947
3948 void
3949 Editor::reset_point_selection ()
3950 {
3951         /* reset all selected points to the relevant default value */
3952
3953         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3954                 
3955                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3956                 
3957                 if (atv) {
3958                         atv->reset_objects (selection->points);
3959                 } 
3960         }
3961 }
3962
3963 void
3964 Editor::center_playhead ()
3965 {
3966         float page = canvas_width * frames_per_unit;
3967         center_screen_internal (playhead_cursor->current_frame, page);
3968 }
3969
3970 void
3971 Editor::center_edit_point ()
3972 {
3973         float page = canvas_width * frames_per_unit;
3974         center_screen_internal (get_preferred_edit_position(), page);
3975 }
3976
3977 void
3978 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3979 {
3980         begin_reversible_command (_("clear playlist"));
3981         XMLNode &before = playlist->get_state();
3982         playlist->clear ();
3983         XMLNode &after = playlist->get_state();
3984         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3985         commit_reversible_command ();
3986 }
3987
3988 void
3989 Editor::nudge_track (bool use_edit, bool forwards)
3990 {
3991         boost::shared_ptr<Playlist> playlist; 
3992         nframes_t distance;
3993         nframes_t next_distance;
3994         nframes_t start;
3995
3996         if (use_edit) {
3997                 start = get_preferred_edit_position();
3998         } else {
3999                 start = 0;
4000         }
4001
4002         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4003                 return;
4004         }
4005         
4006         if (selection->tracks.empty()) {
4007                 return;
4008         }
4009         
4010         begin_reversible_command (_("nudge track"));
4011         
4012         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4013
4014                 if ((playlist = (*i)->playlist()) == 0) {
4015                         continue;
4016                 }               
4017                 
4018                 XMLNode &before = playlist->get_state();
4019                 playlist->nudge_after (start, distance, forwards);
4020                 XMLNode &after = playlist->get_state();
4021                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4022         }
4023         
4024         commit_reversible_command ();                   
4025 }
4026
4027 void
4028 Editor::remove_last_capture ()
4029 {
4030         vector<string> choices;
4031         string prompt;
4032         
4033         if (!session) {
4034                 return;
4035         }
4036
4037         if (Config->get_verify_remove_last_capture()) {
4038                 prompt  = _("Do you really want to destroy the last capture?"
4039                             "\n(This is destructive and cannot be undone)");
4040
4041                 choices.push_back (_("No, do nothing."));
4042                 choices.push_back (_("Yes, destroy it."));
4043                 
4044                 Gtkmm2ext::Choice prompter (prompt, choices);
4045                 
4046                 if (prompter.run () == 1) {
4047                         session->remove_last_capture ();
4048                 }
4049
4050         } else {
4051                 session->remove_last_capture();
4052         }
4053 }
4054
4055 void
4056 Editor::normalize_region ()
4057 {
4058         if (!session) {
4059                 return;
4060         }
4061
4062         if (selection->regions.empty()) {
4063                 return;
4064         }
4065
4066         begin_reversible_command (_("normalize"));
4067
4068         track_canvas.get_window()->set_cursor (*wait_cursor);
4069         gdk_flush ();
4070
4071         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4072                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4073                 if (!arv)
4074                         continue;
4075                 XMLNode &before = arv->region()->get_state();
4076                 arv->audio_region()->normalize_to (0.0f);
4077                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4078         }
4079
4080         commit_reversible_command ();
4081         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4082 }
4083
4084
4085 void
4086 Editor::denormalize_region ()
4087 {
4088         if (!session) {
4089                 return;
4090         }
4091
4092         ExclusiveRegionSelection (*this, entered_regionview);
4093
4094         if (selection->regions.empty()) {
4095                 return;
4096         }
4097
4098         begin_reversible_command ("denormalize");
4099
4100         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4101                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4102                 if (!arv)
4103                         continue;
4104                 XMLNode &before = arv->region()->get_state();
4105                 arv->audio_region()->set_scale_amplitude (1.0f);
4106                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4107         }
4108
4109         commit_reversible_command ();
4110 }
4111
4112 void
4113 Editor::adjust_region_scale_amplitude (bool up)
4114 {
4115         if (!session) {
4116                 return;
4117         }
4118
4119         ExclusiveRegionSelection esr (*this, entered_regionview);
4120
4121         if (selection->regions.empty()) {
4122                 return;
4123         }
4124
4125         begin_reversible_command ("denormalize");
4126
4127         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4128                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4129                 if (!arv)
4130                         continue;
4131                 XMLNode &before = arv->region()->get_state();
4132                 
4133                 double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
4134                 
4135                 if (up) {
4136                         fraction += 0.05;
4137                         fraction = min (fraction, 1.0);
4138                 } else {
4139                         fraction -= 0.05;
4140                         fraction = max (fraction, 0.0);
4141                 }
4142
4143                 if (!up && fraction <= 0) {
4144                         continue;
4145                 }
4146
4147                 fraction = slider_position_to_gain (fraction);
4148                 fraction = coefficient_to_dB (fraction);
4149                 fraction = dB_to_coefficient (fraction);
4150
4151                 if (up && fraction >= 2.0) {
4152                         continue;
4153                 }
4154                 
4155                 arv->audio_region()->set_scale_amplitude (fraction);
4156                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4157         }
4158
4159         commit_reversible_command ();
4160 }
4161
4162
4163 void
4164 Editor::reverse_region ()
4165 {
4166         if (!session) {
4167                 return;
4168         }
4169
4170         Reverse rev (*session);
4171         apply_filter (rev, _("reverse regions"));
4172 }
4173
4174 void
4175 Editor::apply_filter (AudioFilter& filter, string command)
4176 {
4177         if (selection->regions.empty()) {
4178                 return;
4179         }
4180
4181         begin_reversible_command (command);
4182
4183         track_canvas.get_window()->set_cursor (*wait_cursor);
4184         gdk_flush ();
4185
4186         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
4187                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4188                 if (!arv)
4189                         continue;
4190
4191                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4192
4193                 RegionSelection::iterator tmp;
4194                 
4195                 tmp = r;
4196                 ++tmp;
4197
4198                 if (arv->audio_region()->apply (filter) == 0) {
4199
4200                         XMLNode &before = playlist->get_state();
4201                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4202                         XMLNode &after = playlist->get_state();
4203                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4204                 } else {
4205                         goto out;
4206                 }
4207
4208                 r = tmp;
4209         }
4210
4211         commit_reversible_command ();
4212         selection->regions.clear ();
4213
4214   out:
4215         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4216 }
4217
4218 void
4219 Editor::region_selection_op (void (Region::*pmf)(void))
4220 {
4221         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4222                 Region* region = (*i)->region().get();
4223                 (region->*pmf)();
4224         }
4225 }
4226
4227
4228 void
4229 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4230 {
4231         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4232                 Region* region = (*i)->region().get();
4233                 (region->*pmf)(arg);
4234         }
4235 }
4236
4237 void
4238 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4239 {
4240         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4241                 Region* region = (*i)->region().get();
4242                 (region->*pmf)(yn);
4243         }
4244 }
4245
4246 void
4247 Editor::external_edit_region ()
4248 {
4249         if (!clicked_regionview) {
4250                 return;
4251         }
4252
4253         /* more to come */
4254 }
4255
4256 void
4257 Editor::brush (nframes_t pos)
4258 {
4259         RegionSelection sel;
4260         snap_to (pos);
4261
4262         if (selection->regions.empty()) {
4263                 /* XXX get selection from region list */
4264         } else { 
4265                 sel = selection->regions;
4266         }
4267
4268         if (sel.empty()) {
4269                 return;
4270         }
4271
4272         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4273                 mouse_brush_insert_region ((*i), pos);
4274         }
4275 }
4276
4277 void
4278 Editor::reset_region_gain_envelopes ()
4279 {
4280         if (!session || selection->regions.empty()) {
4281                 return;
4282         }
4283
4284         session->begin_reversible_command (_("reset region gain"));
4285
4286         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4287                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4288                 if (arv) {
4289                         AutomationList& alist (arv->audio_region()->envelope());
4290                         XMLNode& before (alist.get_state());
4291
4292                         arv->audio_region()->set_default_envelope ();
4293                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
4294                 }
4295         }
4296
4297         session->commit_reversible_command ();
4298 }
4299
4300 void
4301 Editor::toggle_gain_envelope_visibility ()
4302 {
4303         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4304                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4305                 if (arv) {
4306                         arv->set_envelope_visible (!arv->envelope_visible());
4307                 }
4308         }
4309 }
4310
4311 void
4312 Editor::toggle_gain_envelope_active ()
4313 {
4314         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4315                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4316                 if (arv) {
4317                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4318                 }
4319         }
4320 }
4321
4322 void
4323 Editor::toggle_region_lock ()
4324 {
4325         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4326                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4327                 if (arv) {
4328                         arv->audio_region()->set_locked (!arv->audio_region()->locked());
4329                 }
4330         }
4331 }
4332
4333 void
4334 Editor::set_region_lock_style (Region::PositionLockStyle ps)
4335 {
4336         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4337                 (*i)->region()->set_position_lock_style (ps);
4338         }
4339 }
4340
4341
4342 void
4343 Editor::toggle_region_mute ()
4344 {
4345         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4346                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4347                 if (arv) {
4348                         arv->audio_region()->set_muted (!arv->audio_region()->muted());
4349                 }
4350         }
4351 }
4352
4353 void
4354 Editor::toggle_region_opaque ()
4355 {
4356         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4357                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4358                 if (arv) {
4359                         arv->audio_region()->set_opaque (!arv->audio_region()->opaque());
4360                 }
4361         }
4362 }
4363
4364 void
4365 Editor::set_fade_length (bool in)
4366 {
4367         ExclusiveRegionSelection esr (*this, entered_regionview);
4368
4369         /* we need a region to measure the offset from the start */
4370
4371         RegionView* rv;
4372
4373         if (!selection->regions.empty()) {
4374                 rv = selection->regions.front();
4375         } else if (entered_regionview) {
4376                 rv = entered_regionview;
4377         } else {
4378                 return;
4379         }
4380
4381         nframes64_t pos = get_preferred_edit_position();
4382         nframes_t len;
4383         char* cmd;
4384         
4385         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4386                 /* edit point is outside the relevant region */
4387                 return;
4388         }
4389
4390         if (in) {
4391                 if (pos <= rv->region()->position()) {
4392                         /* can't do it */
4393                         return;
4394                 }
4395                 len = pos - rv->region()->position();
4396                 cmd = _("set fade in length");
4397         } else {
4398                 if (pos >= rv->region()->last_frame()) {
4399                         /* can't do it */
4400                         return;
4401                 }
4402                 len = rv->region()->last_frame() - pos;
4403                 cmd = _("set fade out length");
4404         }
4405
4406         begin_reversible_command (cmd);
4407
4408         RegionSelection& rs (get_regions_for_action());
4409
4410         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4411                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4412
4413                 if (!tmp) {
4414                         return;
4415                 }
4416
4417                 AutomationList* alist;
4418                 if (in) {
4419                         alist = &tmp->audio_region()->fade_in();
4420                 } else {
4421                         alist = &tmp->audio_region()->fade_out();
4422                 }
4423
4424                 XMLNode &before = alist->get_state();
4425
4426                 if (in) {
4427                         tmp->audio_region()->set_fade_in_length (len);
4428                         tmp->audio_region()->set_fade_in_active (true);
4429                 } else {
4430                         tmp->audio_region()->set_fade_out_length (len);
4431                         tmp->audio_region()->set_fade_out_active (true);
4432                 }
4433                 
4434                 XMLNode &after = alist->get_state();
4435                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4436         }
4437
4438         commit_reversible_command ();
4439 }
4440
4441
4442 void
4443 Editor::toggle_fade_active (bool in)
4444 {
4445         ensure_entered_region_selected (true);
4446
4447         if (selection->regions.empty()) {
4448                 return;
4449         }
4450
4451         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4452         bool have_switch = false;
4453         bool yn;
4454
4455         begin_reversible_command (cmd);
4456
4457         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4458                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4459                 
4460                 if (!tmp) {
4461                         return;
4462                 }
4463
4464                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4465
4466                 /* make the behaviour consistent across all regions */
4467                 
4468                 if (!have_switch) {
4469                         if (in) {
4470                                 yn = region->fade_in_active();
4471                         } else {
4472                                 yn = region->fade_out_active();
4473                         }
4474                         have_switch = true;
4475                 }
4476
4477                 XMLNode &before = region->get_state();
4478                 if (in) {
4479                         region->set_fade_in_active (!yn);
4480                 } else {
4481                         region->set_fade_out_active (!yn);
4482                 }
4483                 XMLNode &after = region->get_state();
4484                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4485         }
4486
4487         commit_reversible_command ();
4488 }
4489
4490 void
4491 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4492 {
4493
4494         begin_reversible_command (_("set fade in shape"));
4495
4496         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4497                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4498
4499                 if (!tmp) {
4500                         return;
4501                 }
4502
4503                 AutomationList& alist = tmp->audio_region()->fade_in();
4504                 XMLNode &before = alist.get_state();
4505
4506                 tmp->audio_region()->set_fade_in_shape (shape);
4507                 
4508                 XMLNode &after = alist.get_state();
4509                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4510         }
4511
4512         commit_reversible_command ();
4513                 
4514 }
4515
4516 void
4517 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4518 {
4519         begin_reversible_command (_("set fade out shape"));
4520
4521         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4522                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4523
4524                 if (!tmp) {
4525                         return;
4526                 }
4527
4528                 AutomationList& alist = tmp->audio_region()->fade_out();
4529                 XMLNode &before = alist.get_state();
4530
4531                 tmp->audio_region()->set_fade_out_shape (shape);
4532                 
4533                 XMLNode &after = alist.get_state();
4534                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4535         }
4536
4537         commit_reversible_command ();
4538 }
4539
4540 void
4541 Editor::set_fade_in_active (bool yn)
4542 {
4543         begin_reversible_command (_("set fade in active"));
4544
4545         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4546                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4547
4548                 if (!tmp) {
4549                         return;
4550                 }
4551
4552
4553                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4554
4555                 XMLNode &before = ar->get_state();
4556
4557                 ar->set_fade_in_active (yn);
4558                 
4559                 XMLNode &after = ar->get_state();
4560                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4561         }
4562
4563         commit_reversible_command ();
4564 }
4565
4566 void
4567 Editor::set_fade_out_active (bool yn)
4568 {
4569         begin_reversible_command (_("set fade out active"));
4570
4571         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4572                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4573
4574                 if (!tmp) {
4575                         return;
4576                 }
4577
4578                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4579
4580                 XMLNode &before = ar->get_state();
4581
4582                 ar->set_fade_out_active (yn);
4583                 
4584                 XMLNode &after = ar->get_state();
4585                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4586         }
4587
4588         commit_reversible_command ();
4589 }
4590
4591
4592 /** Update crossfade visibility after its configuration has been changed */
4593 void
4594 Editor::update_xfade_visibility ()
4595 {
4596         _xfade_visibility = Config->get_xfades_visible ();
4597         
4598         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4599                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4600                 if (v) {
4601                         if (_xfade_visibility) {
4602                                 v->show_all_xfades ();
4603                         } else {
4604                                 v->hide_all_xfades ();
4605                         }
4606                 }
4607         }
4608 }
4609
4610 void
4611 Editor::set_edit_point ()
4612 {
4613         nframes64_t where;
4614         bool ignored;
4615
4616         if (!mouse_frame (where, ignored)) {
4617                 return;
4618         }
4619         
4620         snap_to (where);
4621
4622         if (selection->markers.empty()) {
4623                 
4624                 mouse_add_new_marker (where);
4625
4626         } else {
4627                 bool ignored;
4628
4629                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4630
4631                 if (loc) {
4632                         loc->move_to (where);
4633                 }
4634         }
4635 }
4636
4637 void
4638 Editor::set_playhead_cursor ()
4639 {
4640         if (entered_marker) {
4641                 session->request_locate (entered_marker->position(), session->transport_rolling());
4642         } else {
4643                 nframes64_t where;
4644                 bool ignored;
4645
4646                 if (!mouse_frame (where, ignored)) {
4647                         return;
4648                 }
4649                         
4650                 snap_to (where);
4651                 
4652                 if (session) {
4653                         session->request_locate (where, session->transport_rolling());
4654                 }
4655         }
4656 }
4657
4658 void
4659 Editor::split ()
4660 {
4661         ensure_entered_region_selected ();
4662
4663         nframes64_t where = get_preferred_edit_position();
4664
4665         if (!selection->regions.empty()) {
4666                 
4667                 split_regions_at (where, selection->regions);
4668
4669         } else {
4670                 
4671                 RegionSelection rs;
4672                 rs = get_regions_at (where, selection->tracks);
4673                 split_regions_at (where, rs);
4674         }
4675 }
4676
4677 void
4678 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4679 {
4680         if (entered_track && mouse_mode == MouseObject) {
4681                 if (!selection->tracks.empty()) {
4682                         if (!selection->selected (entered_track)) {
4683                                 selection->add (entered_track);
4684                         }
4685                 } else {
4686                         /* there is no selection, but this operation requires/prefers selected objects */
4687
4688                         if (op_really_wants_one_track_if_none_are_selected) {
4689                                 selection->set (entered_track);
4690                         }
4691                 }
4692         }
4693 }
4694
4695 void
4696 Editor::ensure_entered_region_selected (bool op_really_wants_one_region_if_none_are_selected)
4697 {
4698         if (!entered_regionview || mouse_mode != MouseObject) {
4699                 return;
4700         }
4701
4702         
4703         /* heuristic:
4704            
4705         - if there is no existing selection, don't change it. the operation will thus apply to "all"
4706         
4707         - if there is an existing selection, but the entered regionview isn't in it, add it. this
4708         avoids key-mouse ops on unselected regions from interfering with an existing selection,
4709         but also means that the operation will apply to the pointed-at region.
4710         */
4711         
4712         if (!selection->regions.empty()) {
4713                 if (!selection->selected (entered_regionview)) {
4714                         selection->add (entered_regionview);
4715                 }
4716         } else {
4717                 /* there is no selection, but this operation requires/prefers selected objects */
4718                 
4719                 if (op_really_wants_one_region_if_none_are_selected) {
4720                         selection->set (entered_regionview, false);
4721                 }
4722         }
4723 }
4724
4725 void
4726 Editor::trim_region_front ()
4727 {
4728         trim_region (true);
4729 }
4730
4731 void
4732 Editor::trim_region_back ()
4733 {
4734         trim_region (false);
4735 }
4736
4737 void
4738 Editor::trim_region (bool front)
4739 {
4740         ExclusiveRegionSelection ers (*this, entered_regionview);
4741
4742         nframes64_t where = get_preferred_edit_position();
4743         RegionSelection& rs = get_regions_for_action ();
4744
4745         if (rs.empty()) {
4746                 return;
4747         }
4748
4749         begin_reversible_command (front ? _("trim front") : _("trim back"));
4750
4751         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4752                 if (!(*i)->region()->locked()) {
4753                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4754                         XMLNode &before = pl->get_state();
4755                         if (front) {
4756                                 (*i)->region()->trim_front (where, this);       
4757                         } else {
4758                                 (*i)->region()->trim_end (where, this); 
4759                         }
4760                         XMLNode &after = pl->get_state();
4761                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4762                 }
4763         }
4764
4765         commit_reversible_command ();
4766 }
4767
4768 struct EditorOrderRouteSorter {
4769     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4770             /* use of ">" forces the correct sort order */
4771             return a->order_key ("editor") < b->order_key ("editor");
4772     }
4773 };
4774
4775 void
4776 Editor::select_next_route()
4777 {
4778         if (selection->tracks.empty()) {
4779                 selection->set (track_views.front());
4780                 return;
4781         }
4782
4783         TimeAxisView* current = selection->tracks.front();
4784
4785         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4786                 if (*i == current) {
4787                         ++i;
4788                         if (i != track_views.end()) {
4789                                 selection->set (*i);
4790                         } else {
4791                                 selection->set (*(track_views.begin()));
4792                         }
4793                         break;
4794                 }
4795         }
4796 }
4797
4798 void
4799 Editor::select_prev_route()
4800 {
4801         if (selection->tracks.empty()) {
4802                 selection->set (track_views.front());
4803                 return;
4804         }
4805
4806         TimeAxisView* current = selection->tracks.front();
4807
4808         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4809                 if (*i == current) {
4810                         ++i;
4811                         if (i != track_views.rend()) {
4812                                 selection->set (*i);
4813                         } else {
4814                                 selection->set (*(track_views.rbegin()));
4815                         }
4816                         break;
4817                 }
4818         }
4819 }
4820
4821 void
4822 Editor::set_loop_from_selection (bool play)
4823 {
4824         if (session == 0 || selection->time.empty()) {
4825                 return;
4826         }
4827
4828         nframes_t start = selection->time[clicked_selection].start;
4829         nframes_t end = selection->time[clicked_selection].end;
4830         
4831         set_loop_range (start, end,  _("set loop range from selection"));
4832
4833         if (play) {
4834                 session->request_play_loop (true);
4835                 session->request_locate (start, true);
4836         }
4837 }
4838
4839 void
4840 Editor::set_loop_from_edit_range (bool play)
4841 {
4842         if (session == 0) {
4843                 return;
4844         }
4845
4846         nframes64_t start;
4847         nframes64_t end;
4848         
4849         if (!get_edit_op_range (start, end)) {
4850                 return;
4851         }
4852
4853         set_loop_range (start, end,  _("set loop range from edit range"));
4854
4855         if (play) {
4856                 session->request_play_loop (true);
4857                 session->request_locate (start, true);
4858         }
4859 }
4860
4861 void
4862 Editor::set_loop_from_region (bool play)
4863 {
4864         nframes64_t start = max_frames;
4865         nframes64_t end = 0;
4866
4867         ExclusiveRegionSelection esr (*this, entered_regionview);
4868
4869         if (selection->regions.empty()) {
4870                 info << _("cannot set loop: no region selected") << endmsg;
4871                 return;
4872         }
4873
4874         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4875                 if ((*i)->region()->position() < start) {
4876                         start = (*i)->region()->position();
4877                 }
4878                 if ((*i)->region()->last_frame() + 1 > end) {
4879                         end = (*i)->region()->last_frame() + 1;
4880                 }
4881         }
4882
4883         set_loop_range (start, end, _("set loop range from region"));
4884
4885         if (play) {
4886                 session->request_play_loop (true);
4887                 session->request_locate (start, true);
4888         }
4889 }
4890
4891 void
4892 Editor::set_punch_from_selection ()
4893 {
4894         if (session == 0 || selection->time.empty()) {
4895                 return;
4896         }
4897
4898         nframes_t start = selection->time[clicked_selection].start;
4899         nframes_t end = selection->time[clicked_selection].end;
4900         
4901         set_punch_range (start, end,  _("set punch range from selection"));
4902 }
4903
4904 void
4905 Editor::set_punch_from_edit_range ()
4906 {
4907         if (session == 0) {
4908                 return;
4909         }
4910
4911         nframes64_t start;
4912         nframes64_t end;
4913         
4914         if (!get_edit_op_range (start, end)) {
4915                 return;
4916         }
4917
4918         set_punch_range (start, end,  _("set punch range from edit range"));
4919 }
4920
4921 void
4922 Editor::set_punch_from_region ()
4923 {
4924         nframes64_t start = max_frames;
4925         nframes64_t end = 0;
4926
4927         ExclusiveRegionSelection esr (*this, entered_regionview);
4928
4929         if (selection->regions.empty()) {
4930                 info << _("cannot set punch: no region selected") << endmsg;
4931                 return;
4932         }
4933
4934         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4935                 if ((*i)->region()->position() < start) {
4936                         start = (*i)->region()->position();
4937                 }
4938                 if ((*i)->region()->last_frame() + 1 > end) {
4939                         end = (*i)->region()->last_frame() + 1;
4940                 }
4941         }
4942
4943         set_punch_range (start, end, _("set punch range from region"));
4944 }
4945
4946 void
4947 Editor::pitch_shift_regions ()
4948 {
4949         ensure_entered_region_selected (true);
4950         
4951         if (selection->regions.empty()) {
4952                 return;
4953         }
4954
4955         pitch_shift (selection->regions, 1.2);
4956 }
4957         
4958 void
4959 Editor::use_region_as_bar ()
4960 {
4961         if (!session) {
4962                 return;
4963         }
4964
4965         ExclusiveRegionSelection esr (*this, entered_regionview);
4966
4967         if (selection->regions.empty()) {
4968                 return;
4969         }
4970
4971         RegionView* rv = selection->regions.front();
4972
4973         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
4974 }
4975
4976 void
4977 Editor::use_range_as_bar ()
4978 {
4979         nframes64_t start, end;
4980         if (get_edit_op_range (start, end)) {
4981                 define_one_bar (start, end);
4982         }
4983 }
4984
4985 void
4986 Editor::define_one_bar (nframes64_t start, nframes64_t end)
4987 {
4988         nframes64_t length = end - start;
4989         
4990         const Meter& m (session->tempo_map().meter_at (start));
4991
4992         /* length = 1 bar */
4993
4994         /* now we want frames per beat.
4995            we have frames per bar, and beats per bar, so ...
4996         */
4997
4998         double frames_per_beat = length / m.beats_per_bar();
4999         
5000         /* beats per minute = */
5001
5002         double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
5003
5004         /* now decide whether to:
5005
5006             (a) set global tempo 
5007             (b) add a new tempo marker
5008
5009         */
5010
5011         const TempoSection& t (session->tempo_map().tempo_section_at (start));
5012
5013         bool do_global = false;
5014
5015         if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) {
5016                 
5017                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5018                    at the start, or create a new marker
5019                 */
5020
5021                 vector<string> options;
5022                 options.push_back (_("Cancel"));
5023                 options.push_back (_("Add new marker"));
5024                 options.push_back (_("Set global tempo"));
5025                 Choice c (_("Do you want to set the global tempo or add new tempo marker?"),
5026                           options);
5027                 c.set_default_response (2);
5028
5029                 switch (c.run()) {
5030                 case 0:
5031                         return;
5032
5033                 case 2:
5034                         do_global = true;
5035                         break;
5036
5037                 default:
5038                         do_global = false;
5039                 }
5040
5041         } else {
5042
5043                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5044                    if the marker is at the region starter, change it, otherwise add
5045                    a new tempo marker 
5046                 */
5047         }
5048
5049         begin_reversible_command (_("set tempo from region"));
5050         XMLNode& before (session->tempo_map().get_state());
5051
5052         if (do_global) {
5053                 session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5054         } else if (t.frame() == start) {
5055                 session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5056         } else {
5057                 session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5058         }
5059
5060         XMLNode& after (session->tempo_map().get_state());
5061
5062         session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
5063         commit_reversible_command ();
5064 }
5065
5066 void
5067 Editor::split_region_at_transients ()
5068 {
5069         AnalysisFeatureList positions;
5070
5071         if (!session) {
5072                 return;
5073         }
5074
5075         ExclusiveRegionSelection esr (*this, entered_regionview);
5076
5077         if (selection->regions.empty()) {
5078                 return;
5079         }
5080
5081         session->begin_reversible_command (_("split regions"));
5082
5083         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ) {
5084
5085                 RegionSelection::iterator tmp;
5086
5087                 tmp = i;
5088                 ++tmp;
5089
5090                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5091                 
5092                 if (ar && (ar->get_transients (positions) == 0)) {
5093                         split_region_at_points ((*i)->region(), positions);
5094                         positions.clear ();
5095                 }
5096                 
5097                 i = tmp;
5098         }
5099
5100         session->commit_reversible_command ();
5101
5102 }
5103
5104 void
5105 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions)
5106 {
5107         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
5108         
5109         if (!ar) {
5110                 return;
5111         }
5112         
5113         boost::shared_ptr<Playlist> pl = ar->playlist();
5114         
5115         if (!pl) {
5116                 return;
5117         }
5118         
5119         if (positions.empty()) {
5120                 return;
5121         }
5122         
5123         AnalysisFeatureList::const_iterator x;  
5124         
5125         nframes64_t pos = ar->position();
5126
5127         XMLNode& before (pl->get_state());
5128         
5129         x = positions.begin();
5130         
5131         while (x != positions.end()) {
5132                 if ((*x) > pos) {
5133                         break;
5134                 }
5135                 ++x;
5136         }
5137         
5138         if (x == positions.end()) {
5139                 return;
5140         }
5141         
5142         pl->freeze ();
5143         pl->remove_region (ar);
5144         
5145         while (x != positions.end()) {
5146                 
5147                 /* file start = original start + how far we from the initial position ? 
5148                  */
5149                 
5150                 nframes64_t file_start = ar->start() + (pos - ar->position());
5151                 
5152                 /* length = next position - current position
5153                  */
5154                 
5155                 nframes64_t len = (*x) - pos;
5156
5157                 /* XXX we do we really want to allow even single-sample regions?
5158                    shouldn't we have some kind of lower limit on region size?
5159                 */
5160
5161                 if (len <= 0) {
5162                         break;
5163                 }
5164                 
5165                 string new_name;
5166                 
5167                 if (session->region_name (new_name, ar->name())) {
5168                         continue;
5169                 }
5170                 
5171                 pl->add_region (RegionFactory::create (ar->get_sources(), file_start, len, new_name), pos);
5172                 
5173                 pos += len;
5174                 ++x;
5175
5176                 if (*x > ar->last_frame()) {
5177                         break;
5178                 }
5179         } 
5180
5181         pl->thaw ();
5182         
5183         XMLNode& after (pl->get_state());
5184         
5185         session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
5186 }
5187
5188 void
5189 Editor::tab_to_transient (bool forward)
5190 {
5191         AnalysisFeatureList positions;
5192
5193         if (!session) {
5194                 return;
5195         }
5196
5197         nframes64_t pos = session->audible_frame ();
5198
5199         if (!selection->tracks.empty()) {
5200
5201                 for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
5202
5203                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
5204
5205                         if (rtv) {
5206                                 boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
5207                                 if (ds) {
5208                                         boost::shared_ptr<Playlist> pl = rtv->get_diskstream()->playlist ();
5209                                         if (pl) {
5210                                                 nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1);
5211                                                 
5212                                                 if (result >= 0) {
5213                                                         positions.push_back (result);
5214                                                 }
5215                                         }
5216                                 }
5217                         }
5218                 }
5219
5220         } else {
5221                 
5222                 ExclusiveRegionSelection esr (*this, entered_regionview);
5223         
5224                 if (selection->regions.empty()) {
5225                         return;
5226                 }
5227                 
5228                 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
5229                         (*r)->region()->get_transients (positions);
5230                 }
5231         }
5232
5233         TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0);
5234
5235         if (forward) {
5236                 AnalysisFeatureList::iterator x;
5237
5238                 for (x = positions.begin(); x != positions.end(); ++x) {
5239                         if ((*x) > pos) {
5240                                 break;
5241                         }
5242                 }
5243
5244                 if (x != positions.end ()) {
5245                         session->request_locate (*x);
5246                 }
5247
5248         } else {
5249                 AnalysisFeatureList::reverse_iterator x;
5250
5251                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
5252                         if ((*x) < pos) {
5253                                 break;
5254                         }
5255                 }
5256
5257                 if (x != positions.rend ()) {
5258                         session->request_locate (*x);
5259                 }
5260         }
5261 }