meet rhythm ferret: cute, furry and always on time (ardour build now requires fftw3...
[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 "audio_time_axis.h"
58 #include "automation_time_axis.h"
59 #include "streamview.h"
60 #include "audio_region_view.h"
61 #include "rgb_macros.h"
62 #include "selection_templates.h"
63 #include "selection.h"
64 #include "editing.h"
65 #include "gtk-custom-hruler.h"
66 #include "gui_thread.h"
67 #include "keyboard.h"
68 #include "utils.h"
69
70 #include "i18n.h"
71
72 using namespace std;
73 using namespace ARDOUR;
74 using namespace PBD;
75 using namespace sigc;
76 using namespace Gtk;
77 using namespace Gtkmm2ext;
78 using namespace Editing;
79
80 /***********************************************************************
81   Editor operations
82  ***********************************************************************/
83
84 void
85 Editor::undo (uint32_t n)
86 {
87         if (session) {
88                 session->undo (n);
89         }
90 }
91
92 void
93 Editor::redo (uint32_t n)
94 {
95         if (session) {
96                 session->redo (n);
97         }
98 }
99
100 void
101 Editor::split_region ()
102 {
103         split_region_at (get_preferred_edit_position());
104 }
105
106 void
107 Editor::split_region_at (nframes_t where)
108 {
109         split_regions_at (where, selection->regions);
110 }
111
112 void
113 Editor::split_regions_at (nframes_t where, RegionSelection& regions)
114 {
115         if (regions.empty()) {
116                 return;
117         }
118
119         begin_reversible_command (_("split"));
120
121         // if splitting a single region, and snap-to is using
122         // region boundaries, don't pay attention to them
123
124         if (regions.size() == 1) {
125                 switch (snap_type) {
126                 case SnapToRegionStart:
127                 case SnapToRegionSync:
128                 case SnapToRegionEnd:
129                         break;
130                 default:
131                         snap_to (where);
132                 }
133         } else {
134                 snap_to (where);
135         }
136                 
137
138         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
139
140                 RegionSelection::iterator tmp;
141
142                 /* XXX this test needs to be more complicated, to make sure we really
143                    have something to split.
144                 */
145                 
146                 if (!(*a)->region()->covers (where)) {
147                         ++a;
148                         continue;
149                 }
150
151                 tmp = a;
152                 ++tmp;
153
154                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
155
156                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
157
158                 if (arv) {
159                         _new_regionviews_show_envelope = arv->envelope_visible();
160                 }
161                 
162                 if (pl) {
163                         XMLNode &before = pl->get_state();
164                         pl->split_region ((*a)->region(), where);
165                         XMLNode &after = pl->get_state();
166                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
167                 }
168
169                 a = tmp;
170         }
171         
172         commit_reversible_command ();
173         _new_regionviews_show_envelope = false;
174 }
175
176 void
177 Editor::remove_clicked_region ()
178 {
179         if (clicked_audio_trackview == 0 || clicked_regionview == 0) {
180                 return;
181         }
182
183         boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
184         
185         begin_reversible_command (_("remove region"));
186         XMLNode &before = playlist->get_state();
187         playlist->remove_region (clicked_regionview->region());
188         XMLNode &after = playlist->get_state();
189         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
190         commit_reversible_command ();
191 }
192
193 void
194 Editor::destroy_clicked_region ()
195 {
196         uint32_t selected = selection->regions.size();
197
198         if (!session || !selected) {
199                 return;
200         }
201
202         vector<string> choices;
203         string prompt;
204         
205         prompt  = string_compose (_(" This is destructive, will possibly delete audio files\n\
206 It cannot be undone\n\
207 Do you really want to destroy %1 ?"),
208                            (selected > 1 ? 
209                             _("these regions") : _("this region")));
210
211         choices.push_back (_("No, do nothing."));
212
213         if (selected > 1) {
214                 choices.push_back (_("Yes, destroy them."));
215         } else {
216                 choices.push_back (_("Yes, destroy it."));
217         }
218
219         Gtkmm2ext::Choice prompter (prompt, choices);
220         
221         if (prompter.run() == 0) { /* first choice */
222                 return;
223         }
224
225         if (selected) {
226                 list<boost::shared_ptr<Region> > r;
227
228                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
229                         r.push_back ((*i)->region());
230                 }
231
232                 session->destroy_regions (r);
233         } 
234 }
235
236 boost::shared_ptr<Region>
237 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
238 {
239         RegionView* rv;
240         boost::shared_ptr<Region> region;
241         nframes_t start = 0;
242
243         if (selection->time.start () == selection->time.end_frame ()) {
244                 
245                 /* no current selection-> is there a selected regionview? */
246
247                 if (selection->regions.empty()) {
248                         return region;
249                 }
250
251         } 
252
253         if (!selection->regions.empty()) {
254
255                 rv = *(selection->regions.begin());
256                 (*tv) = &rv->get_time_axis_view();
257                 region = rv->region();
258
259         } else if (!selection->tracks.empty()) {
260
261                 (*tv) = selection->tracks.front();
262
263                 RouteTimeAxisView* rtv;
264
265                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
266                         boost::shared_ptr<Playlist> pl;
267                         
268                         if ((pl = rtv->playlist()) == 0) {
269                                 return region;
270                         }
271                         
272                         region = pl->top_region_at (start);
273                 }
274         } 
275         
276         return region;
277 }
278         
279 void
280 Editor::extend_selection_to_end_of_region (bool next)
281 {
282         TimeAxisView *tv;
283         boost::shared_ptr<Region> region;
284         nframes_t start;
285
286         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
287                 return;
288         }
289
290         if (region && selection->time.start () == selection->time.end_frame ()) {
291                 start = region->position();
292         } else {
293                 start = selection->time.start ();
294         }
295
296         /* Try to leave the selection with the same route if possible */
297
298         if ((tv = selection->time.track) == 0) {
299                 return;
300         }
301
302         begin_reversible_command (_("extend selection"));
303         selection->set (tv, start, region->position() + region->length());
304         commit_reversible_command ();
305 }
306
307 void
308 Editor::extend_selection_to_start_of_region (bool previous)
309 {
310         TimeAxisView *tv;
311         boost::shared_ptr<Region> region;
312         nframes_t end;
313
314         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
315                 return;
316         }
317
318         if (region && selection->time.start () == selection->time.end_frame ()) {
319                 end = region->position() + region->length();
320         } else {
321                 end = selection->time.end_frame ();
322         }
323
324         /* Try to leave the selection with the same route if possible */
325         
326         if ((tv = selection->time.track) == 0) {
327                 return;
328         }
329
330         begin_reversible_command (_("extend selection"));
331         selection->set (tv, region->position(), end);
332         commit_reversible_command ();
333 }
334
335 bool
336 Editor::nudge_forward_release (GdkEventButton* ev)
337 {
338         if (ev->state & Keyboard::PrimaryModifier) {
339                 nudge_forward (false, true);
340         } else {
341                 nudge_forward (false, false);
342         }
343         return false;
344 }
345
346 bool
347 Editor::nudge_backward_release (GdkEventButton* ev)
348 {
349         if (ev->state & Keyboard::PrimaryModifier) {
350                 nudge_backward (false, true);
351         } else {
352                 nudge_backward (false, false);
353         }
354         return false;
355 }
356
357
358 void
359 Editor::nudge_forward (bool next, bool force_playhead)
360 {
361         nframes_t distance;
362         nframes_t next_distance;
363
364         if (!session) return;
365         
366         if (!force_playhead && !selection->regions.empty()) {
367
368                 begin_reversible_command (_("nudge regions forward"));
369
370                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
371                         boost::shared_ptr<Region> r ((*i)->region());
372                         
373                         distance = get_nudge_distance (r->position(), next_distance);
374
375                         if (next) {
376                                 distance = next_distance;
377                         }
378
379                         XMLNode &before = r->playlist()->get_state();
380                         r->set_position (r->position() + distance, this);
381                         XMLNode &after = r->playlist()->get_state();
382                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
383                 }
384
385                 commit_reversible_command ();
386
387                 
388         } else if (!force_playhead && !selection->markers.empty()) {
389
390                 bool is_start;
391                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
392
393                 if (loc) {
394
395                         begin_reversible_command (_("nudge location forward"));
396
397                         XMLNode& before (loc->get_state());
398
399                         if (is_start) {
400                                 distance = get_nudge_distance (loc->start(), next_distance);
401                                 if (next) {
402                                         distance = next_distance;
403                                 }
404                                 if (max_frames - distance > loc->start() + loc->length()) {
405                                         loc->set_start (loc->start() + distance);
406                                 } else {
407                                         loc->set_start (max_frames - loc->length());
408                                 }
409                         } else {
410                                 distance = get_nudge_distance (loc->end(), next_distance);
411                                 if (next) {
412                                         distance = next_distance;
413                                 }
414                                 if (max_frames - distance > loc->end()) {
415                                         loc->set_end (loc->end() + distance);
416                                 } else {
417                                         loc->set_end (max_frames);
418                                 }
419                         }
420                         XMLNode& after (loc->get_state());
421                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
422                         commit_reversible_command ();
423                 }
424                 
425         } else {
426                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
427                 session->request_locate (playhead_cursor->current_frame + distance);
428         }
429 }
430                 
431 void
432 Editor::nudge_backward (bool next, bool force_playhead)
433 {
434         nframes_t distance;
435         nframes_t next_distance;
436
437         if (!session) return;
438         
439         if (!force_playhead && !selection->regions.empty()) {
440
441                 begin_reversible_command (_("nudge regions backward"));
442
443                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
444                         boost::shared_ptr<Region> r ((*i)->region());
445
446                         distance = get_nudge_distance (r->position(), next_distance);
447                         
448                         if (next) {
449                                 distance = next_distance;
450                         }
451
452                         XMLNode &before = r->playlist()->get_state();
453                         
454                         if (r->position() > distance) {
455                                 r->set_position (r->position() - distance, this);
456                         } else {
457                                 r->set_position (0, this);
458                         }
459                         XMLNode &after = r->playlist()->get_state();
460                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
461                 }
462
463                 commit_reversible_command ();
464
465         } else if (!force_playhead && !selection->markers.empty()) {
466
467                 bool is_start;
468                 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
469
470                 if (loc) {
471
472                         begin_reversible_command (_("nudge location forward"));
473                         XMLNode& before (loc->get_state());
474
475                         if (is_start) {
476                                 distance = get_nudge_distance (loc->start(), next_distance);
477                                 if (next) {
478                                         distance = next_distance;
479                                 }
480                                 if (distance < loc->start()) {
481                                         loc->set_start (loc->start() - distance);
482                                 } else {
483                                         loc->set_start (0);
484                                 }
485                         } else {
486                                 distance = get_nudge_distance (loc->end(), next_distance);
487
488                                 if (next) {
489                                         distance = next_distance;
490                                 }
491
492                                 if (distance < loc->end() - loc->length()) {
493                                         loc->set_end (loc->end() - distance);
494                                 } else {
495                                         loc->set_end (loc->length());
496                                 }
497                         }
498
499                         XMLNode& after (loc->get_state());
500                         session->add_command (new MementoCommand<Location>(*loc, &before, &after));
501                 }
502                 
503         } else {
504
505                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
506
507                 if (playhead_cursor->current_frame > distance) {
508                         session->request_locate (playhead_cursor->current_frame - distance);
509                 } else {
510                         session->goto_start();
511                 }
512         }
513 }
514
515 void
516 Editor::nudge_forward_capture_offset ()
517 {
518         nframes_t distance;
519
520         if (!session) return;
521         
522         if (!selection->regions.empty()) {
523
524                 begin_reversible_command (_("nudge forward"));
525
526                 distance = session->worst_output_latency();
527
528                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
529                         boost::shared_ptr<Region> r ((*i)->region());
530                         
531                         XMLNode &before = r->playlist()->get_state();
532                         r->set_position (r->position() + distance, this);
533                         XMLNode &after = r->playlist()->get_state();
534                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
535                 }
536
537                 commit_reversible_command ();
538
539         } 
540 }
541                 
542 void
543 Editor::nudge_backward_capture_offset ()
544 {
545         nframes_t distance;
546
547         if (!session) return;
548         
549         if (!selection->regions.empty()) {
550
551                 begin_reversible_command (_("nudge forward"));
552
553                 distance = session->worst_output_latency();
554
555                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
556                         boost::shared_ptr<Region> r ((*i)->region());
557
558                         XMLNode &before = r->playlist()->get_state();
559                         
560                         if (r->position() > distance) {
561                                 r->set_position (r->position() - distance, this);
562                         } else {
563                                 r->set_position (0, this);
564                         }
565                         XMLNode &after = r->playlist()->get_state();
566                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
567                 }
568
569                 commit_reversible_command ();
570         }
571 }
572
573 /* DISPLAY MOTION */
574
575 void
576 Editor::move_to_start ()
577 {
578         session->goto_start ();
579 }
580
581 void
582 Editor::move_to_end ()
583 {
584
585         session->request_locate (session->current_end_frame());
586 }
587
588 void
589 Editor::build_region_boundary_cache ()
590 {
591         nframes_t pos = 0;
592         vector<RegionPoint> interesting_points;
593         boost::shared_ptr<Region> r;
594         TrackViewList tracks;
595         bool at_end = false;
596
597         region_boundary_cache.clear ();
598
599         if (session == 0) {
600                 return;
601         }
602         
603         switch (snap_type) {
604         case SnapToRegionStart:
605                 interesting_points.push_back (Start);
606                 break;
607         case SnapToRegionEnd:
608                 interesting_points.push_back (End);
609                 break;  
610         case SnapToRegionSync:
611                 interesting_points.push_back (SyncPoint);
612                 break;  
613         case SnapToRegionBoundary:
614                 interesting_points.push_back (Start);
615                 interesting_points.push_back (End);
616                 break;  
617         default:
618                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
619                 /*NOTREACHED*/
620                 return;
621         }
622         
623         TimeAxisView *ontrack = 0;
624         TrackViewList tlist;
625
626         if (!selection->tracks.empty()) {
627                 tlist = selection->tracks;
628         } else {
629                 tlist = track_views;
630         }
631
632         while (pos < session->current_end_frame() && !at_end) {
633
634                 nframes_t rpos;
635                 nframes_t lpos = max_frames;
636
637                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
638
639                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
640                                 if (*p == interesting_points.back()) {
641                                         at_end = true;
642                                 }
643                                 /* move to next point type */
644                                 continue;
645                         }
646
647                         switch (*p) {
648                         case Start:
649                                 rpos = r->first_frame();
650                                 break;
651
652                         case End:
653                                 rpos = r->last_frame();
654                                 break;  
655
656                         case SyncPoint:
657                                 rpos = r->adjust_to_sync (r->first_frame());
658                                 break;
659
660                         default:
661                                 break;
662                         }
663                         
664                         float speed = 1.0f;
665                         AudioTimeAxisView *atav;
666                         
667                         if (ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
668                                 if (atav->get_diskstream() != 0) {
669                                         speed = atav->get_diskstream()->speed();
670                                 }
671                         }
672                         
673                         rpos = track_frame_to_session_frame (rpos, speed);
674
675                         if (rpos < lpos) {
676                                 lpos = rpos;
677                         }
678
679                         /* prevent duplicates, but we don't use set<> because we want to be able
680                            to sort later.
681                         */
682
683                         vector<nframes_t>::iterator ri; 
684                         
685                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
686                                 if (*ri == rpos) {
687                                         break;
688                                 }
689                         }
690
691                         if (ri == region_boundary_cache.end()) {
692                                 region_boundary_cache.push_back (rpos);
693                         }
694                 }
695
696                 pos = lpos + 1;
697         }
698
699         /* finally sort to be sure that the order is correct */
700
701         sort (region_boundary_cache.begin(), region_boundary_cache.end());
702 }
703
704 boost::shared_ptr<Region>
705 Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
706 {
707         TrackViewList::iterator i;
708         nframes_t closest = max_frames;
709         boost::shared_ptr<Region> ret;
710         nframes_t rpos = 0;
711
712         float track_speed;
713         nframes_t track_frame;
714         AudioTimeAxisView *atav;
715
716         for (i = tracks.begin(); i != tracks.end(); ++i) {
717
718                 nframes_t distance;
719                 boost::shared_ptr<Region> r;
720                 
721                 track_speed = 1.0f;
722                 if ( (atav = dynamic_cast<AudioTimeAxisView*>(*i)) != 0 ) {
723                         if (atav->get_diskstream()!=0)
724                                 track_speed = atav->get_diskstream()->speed();
725                 }
726
727                 track_frame = session_frame_to_track_frame(frame, track_speed);
728
729                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
730                         continue;
731                 }
732
733                 switch (point) {
734                 case Start:
735                         rpos = r->first_frame ();
736                         break;
737
738                 case End:
739                         rpos = r->last_frame ();
740                         break;
741
742                 case SyncPoint:
743                         rpos = r->adjust_to_sync (r->first_frame());
744                         break;
745                 }
746
747                 // rpos is a "track frame", converting it to "session frame"
748                 rpos = track_frame_to_session_frame(rpos, track_speed);
749
750                 if (rpos > frame) {
751                         distance = rpos - frame;
752                 } else {
753                         distance = frame - rpos;
754                 }
755
756                 if (distance < closest) {
757                         closest = distance;
758                         if (ontrack != 0)
759                                 *ontrack = (*i);
760                         ret = r;
761                 }
762         }
763
764         return ret;
765 }
766
767 nframes64_t
768 Editor::find_next_region_boundary (nframes64_t pos, int32_t dir, const TrackViewList& tracks)
769 {
770         nframes64_t distance = max_frames;
771         nframes64_t current_nearest = -1;
772
773         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
774                 nframes64_t contender;
775                 nframes64_t d;
776
777                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
778
779                 if (!rtv) {
780                         continue;
781                 }
782
783                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
784                         continue;
785                 }
786
787                 d = ::llabs (pos - contender);
788
789                 if (d < distance) {
790                         current_nearest = contender;
791                         distance = d;
792                 }
793         }
794         
795         return current_nearest;
796 }
797
798 void
799 Editor::cursor_to_region_boundary (Cursor* cursor, int32_t dir)
800 {
801         nframes64_t pos = cursor->current_frame;
802         nframes64_t target;
803
804         if (!session) {
805                 return;
806         }
807
808         // so we don't find the current region again..
809         if (dir > 0 || pos > 0) {
810                 pos += dir;
811         }
812
813         if (!selection->tracks.empty()) {
814                 
815                 target = find_next_region_boundary (pos, dir, selection->tracks);
816                 
817         } else {
818                 
819                 target = find_next_region_boundary (pos, dir, track_views);
820         }
821         
822         if (target < 0) {
823                 return;
824         }
825
826
827         if (cursor == playhead_cursor) {
828                 session->request_locate (target);
829         } else {
830                 cursor->set_position (target);
831         }
832 }
833
834 void
835 Editor::cursor_to_next_region_boundary (Cursor* cursor)
836 {
837         cursor_to_region_boundary (cursor, 1);
838 }
839
840 void
841 Editor::cursor_to_previous_region_boundary (Cursor* cursor)
842 {
843         cursor_to_region_boundary (cursor, -1);
844 }
845
846 void
847 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
848 {
849         boost::shared_ptr<Region> r;
850         nframes_t pos = cursor->current_frame;
851
852         if (!session) {
853                 return;
854         }
855
856         TimeAxisView *ontrack = 0;
857
858         // so we don't find the current region again..
859         if (dir>0 || pos>0)
860                 pos+=dir;
861
862         if (!selection->tracks.empty()) {
863                 
864                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
865                 
866         } else if (clicked_trackview) {
867                 
868                 TrackViewList t;
869                 t.push_back (clicked_trackview);
870                 
871                 r = find_next_region (pos, point, dir, t, &ontrack);
872                 
873         } else {
874                 
875                 r = find_next_region (pos, point, dir, track_views, &ontrack);
876         }
877
878         if (r == 0) {
879                 return;
880         }
881         
882         switch (point){
883         case Start:
884                 pos = r->first_frame ();
885                 break;
886
887         case End:
888                 pos = r->last_frame ();
889                 break;
890
891         case SyncPoint:
892                 pos = r->adjust_to_sync (r->first_frame());
893                 break;  
894         }
895         
896         float speed = 1.0f;
897         AudioTimeAxisView *atav;
898
899         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
900                 if (atav->get_diskstream() != 0) {
901                         speed = atav->get_diskstream()->speed();
902                 }
903         }
904
905         pos = track_frame_to_session_frame(pos, speed);
906         
907         if (cursor == playhead_cursor) {
908                 session->request_locate (pos);
909         } else {
910                 cursor->set_position (pos);
911         }
912 }
913
914 void
915 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
916 {
917         cursor_to_region_point (cursor, point, 1);
918 }
919
920 void
921 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
922 {
923         cursor_to_region_point (cursor, point, -1);
924 }
925
926 void
927 Editor::cursor_to_selection_start (Cursor *cursor)
928 {
929         nframes_t pos = 0;
930         switch (mouse_mode) {
931         case MouseObject:
932                 if (!selection->regions.empty()) {
933                         pos = selection->regions.start();
934                 }
935                 break;
936
937         case MouseRange:
938                 if (!selection->time.empty()) {
939                         pos = selection->time.start ();
940                 }
941                 break;
942
943         default:
944                 return;
945         }
946
947         if (cursor == playhead_cursor) {
948                 session->request_locate (pos);
949         } else {
950                 cursor->set_position (pos);
951         }
952 }
953
954 void
955 Editor::cursor_to_selection_end (Cursor *cursor)
956 {
957         nframes_t pos = 0;
958
959         switch (mouse_mode) {
960         case MouseObject:
961                 if (!selection->regions.empty()) {
962                         pos = selection->regions.end_frame();
963                 }
964                 break;
965
966         case MouseRange:
967                 if (!selection->time.empty()) {
968                         pos = selection->time.end_frame ();
969                 }
970                 break;
971
972         default:
973                 return;
974         }
975
976         if (cursor == playhead_cursor) {
977                 session->request_locate (pos);
978         } else {
979                 cursor->set_position (pos);
980         }
981 }
982
983 void
984 Editor::selected_marker_to_region_boundary (int32_t dir)
985 {
986         nframes64_t target;
987         Location* loc;
988         bool ignored;
989
990         if (!session) {
991                 return;
992         }
993
994         if (selection->markers.empty()) {
995                 nframes64_t mouse;
996                 bool ignored;
997
998                 if (!mouse_frame (mouse, ignored)) {
999                         return;
1000                 }
1001                 
1002                 add_location_mark (mouse);
1003         }
1004
1005         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1006                 return;
1007         }
1008
1009         nframes64_t pos = loc->start();
1010
1011         // so we don't find the current region again..
1012         if (dir > 0 || pos > 0) {
1013                 pos += dir;
1014         }
1015
1016         if (!selection->tracks.empty()) {
1017                 
1018                 target = find_next_region_boundary (pos, dir, selection->tracks);
1019                 
1020         } else {
1021                 
1022                 target = find_next_region_boundary (pos, dir, track_views);
1023         }
1024         
1025         if (target < 0) {
1026                 return;
1027         }
1028
1029         loc->move_to (target);
1030 }
1031
1032 void
1033 Editor::selected_marker_to_next_region_boundary ()
1034 {
1035         selected_marker_to_region_boundary (1);
1036 }
1037
1038 void
1039 Editor::selected_marker_to_previous_region_boundary ()
1040 {
1041         selected_marker_to_region_boundary (-1);
1042 }
1043
1044 void
1045 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1046 {
1047         boost::shared_ptr<Region> r;
1048         nframes_t pos;
1049         Location* loc;
1050         bool ignored;
1051
1052         if (!session || selection->markers.empty()) {
1053                 return;
1054         }
1055
1056         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1057                 return;
1058         }
1059
1060         TimeAxisView *ontrack = 0;
1061
1062         pos = loc->start();
1063
1064         // so we don't find the current region again..
1065         if (dir>0 || pos>0)
1066                 pos+=dir;
1067
1068         if (!selection->tracks.empty()) {
1069                 
1070                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1071                 
1072         } else if (clicked_trackview) {
1073                 
1074                 TrackViewList t;
1075                 t.push_back (clicked_trackview);
1076                 
1077                 r = find_next_region (pos, point, dir, t, &ontrack);
1078                 
1079         } else {
1080                 
1081                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1082         }
1083
1084         if (r == 0) {
1085                 return;
1086         }
1087         
1088         switch (point){
1089         case Start:
1090                 pos = r->first_frame ();
1091                 break;
1092
1093         case End:
1094                 pos = r->last_frame ();
1095                 break;
1096
1097         case SyncPoint:
1098                 pos = r->adjust_to_sync (r->first_frame());
1099                 break;  
1100         }
1101         
1102         float speed = 1.0f;
1103         AudioTimeAxisView *atav;
1104
1105         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
1106                 if (atav->get_diskstream() != 0) {
1107                         speed = atav->get_diskstream()->speed();
1108                 }
1109         }
1110
1111         pos = track_frame_to_session_frame(pos, speed);
1112
1113         loc->move_to (pos);
1114 }
1115
1116 void
1117 Editor::selected_marker_to_next_region_point (RegionPoint point)
1118 {
1119         selected_marker_to_region_point (point, 1);
1120 }
1121
1122 void
1123 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1124 {
1125         selected_marker_to_region_point (point, -1);
1126 }
1127
1128 void
1129 Editor::selected_marker_to_selection_start ()
1130 {
1131         nframes_t pos = 0;
1132         Location* loc;
1133         bool ignored;
1134
1135         if (!session || selection->markers.empty()) {
1136                 return;
1137         }
1138
1139         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1140                 return;
1141         }
1142
1143         switch (mouse_mode) {
1144         case MouseObject:
1145                 if (!selection->regions.empty()) {
1146                         pos = selection->regions.start();
1147                 }
1148                 break;
1149
1150         case MouseRange:
1151                 if (!selection->time.empty()) {
1152                         pos = selection->time.start ();
1153                 }
1154                 break;
1155
1156         default:
1157                 return;
1158         }
1159
1160         loc->move_to (pos);
1161 }
1162
1163 void
1164 Editor::selected_marker_to_selection_end ()
1165 {
1166         nframes_t pos = 0;
1167         Location* loc;
1168         bool ignored;
1169
1170         if (!session || selection->markers.empty()) {
1171                 return;
1172         }
1173
1174         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1175                 return;
1176         }
1177
1178         switch (mouse_mode) {
1179         case MouseObject:
1180                 if (!selection->regions.empty()) {
1181                         pos = selection->regions.end_frame();
1182                 }
1183                 break;
1184
1185         case MouseRange:
1186                 if (!selection->time.empty()) {
1187                         pos = selection->time.end_frame ();
1188                 }
1189                 break;
1190
1191         default:
1192                 return;
1193         }
1194
1195         loc->move_to (pos);
1196 }
1197
1198 void
1199 Editor::scroll_playhead (bool forward)
1200 {
1201         nframes_t pos = playhead_cursor->current_frame;
1202         nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
1203
1204         if (forward) {
1205                 if (pos == max_frames) {
1206                         return;
1207                 }
1208
1209                 if (pos < max_frames - delta) {
1210                         pos += delta ;
1211                 } else {
1212                         pos = max_frames;
1213                 } 
1214
1215         } else {
1216
1217                 if (pos == 0) {
1218                         return;
1219                 } 
1220
1221                 if (pos > delta) {
1222                         pos -= delta;
1223                 } else {
1224                         pos = 0;
1225                 }
1226         }
1227
1228         session->request_locate (pos);
1229 }
1230
1231 void
1232 Editor::playhead_backward ()
1233 {
1234         nframes_t pos;
1235         nframes_t cnt;
1236         float prefix;
1237         bool was_floating;
1238
1239         if (get_prefix (prefix, was_floating)) {
1240                 cnt = 1;
1241         } else {
1242                 if (was_floating) {
1243                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1244                 } else {
1245                         cnt = (nframes_t) prefix;
1246                 }
1247         }
1248
1249         pos = playhead_cursor->current_frame;
1250
1251         if ((nframes_t) pos < cnt) {
1252                 pos = 0;
1253         } else {
1254                 pos -= cnt;
1255         }
1256         
1257         /* XXX this is completely insane. with the current buffering
1258            design, we'll force a complete track buffer flush and
1259            reload, just to move 1 sample !!!
1260         */
1261
1262         session->request_locate (pos);
1263 }
1264
1265 void
1266 Editor::playhead_forward ()
1267 {
1268         nframes_t pos;
1269         nframes_t cnt;
1270         bool was_floating;
1271         float prefix;
1272
1273         if (get_prefix (prefix, was_floating)) {
1274                 cnt = 1;
1275         } else {
1276                 if (was_floating) {
1277                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1278                 } else {
1279                         cnt = (nframes_t) floor (prefix);
1280                 }
1281         }
1282
1283         pos = playhead_cursor->current_frame;
1284         
1285         /* XXX this is completely insane. with the current buffering
1286            design, we'll force a complete track buffer flush and
1287            reload, just to move 1 sample !!!
1288         */
1289
1290         session->request_locate (pos+cnt);
1291 }
1292
1293 void
1294 Editor::cursor_align (bool playhead_to_edit)
1295 {
1296         if (!session) {
1297                 return;
1298         }
1299
1300         if (playhead_to_edit) {
1301
1302                 if (selection->markers.empty()) {
1303                         return;
1304                 }
1305                 
1306                 session->request_locate (selection->markers.front()->position(), session->transport_rolling());
1307         
1308         } else {
1309
1310                 /* move selected markers to playhead */
1311                 
1312                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1313                         bool ignored;
1314                         
1315                         Location* loc = find_location_from_marker (*i, ignored);
1316                         
1317                         if (loc->is_mark()) {
1318                                 loc->set_start (playhead_cursor->current_frame);
1319                         } else {
1320                                 loc->set (playhead_cursor->current_frame,
1321                                           playhead_cursor->current_frame + loc->length());
1322                         }
1323                 }
1324         }
1325 }
1326
1327 void
1328 Editor::edit_cursor_backward ()
1329 {
1330         nframes64_t pos;
1331         nframes64_t cnt;
1332         float prefix;
1333         bool was_floating;
1334
1335         if (get_prefix (prefix, was_floating)) {
1336                 cnt = 1;
1337         } else {
1338                 if (was_floating) {
1339                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1340                 } else {
1341                         cnt = (nframes_t) prefix;
1342                 }
1343         }
1344
1345         if ((pos = get_preferred_edit_position()) < 0) {
1346                 return;
1347         }
1348
1349         if (pos < cnt) {
1350                 pos = 0;
1351         } else {
1352                 pos -= cnt;
1353         }
1354         
1355         // EDIT CURSOR edit_cursor->set_position (pos);
1356 }
1357
1358 void
1359 Editor::edit_cursor_forward ()
1360 {
1361         //nframes_t pos;
1362         nframes_t cnt;
1363         bool was_floating;
1364         float prefix;
1365
1366         if (get_prefix (prefix, was_floating)) {
1367                 cnt = 1;
1368         } else {
1369                 if (was_floating) {
1370                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1371                 } else {
1372                         cnt = (nframes_t) floor (prefix);
1373                 }
1374         }
1375
1376         // pos = edit_cursor->current_frame;
1377         // EDIT CURSOR edit_cursor->set_position (pos+cnt);
1378 }
1379
1380 void
1381 Editor::goto_frame ()
1382 {
1383         float prefix;
1384         bool was_floating;
1385         nframes_t frame;
1386
1387         if (get_prefix (prefix, was_floating)) {
1388                 return;
1389         }
1390
1391         if (was_floating) {
1392                 frame = (nframes_t) floor (prefix * session->frame_rate());
1393         } else {
1394                 frame = (nframes_t) floor (prefix);
1395         }
1396
1397         session->request_locate (frame);
1398 }
1399
1400 void
1401 Editor::scroll_backward (float pages)
1402 {
1403         nframes_t frame;
1404         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1405         bool was_floating;
1406         float prefix;
1407         nframes_t cnt;
1408         
1409         if (get_prefix (prefix, was_floating)) {
1410                 cnt = (nframes_t) floor (pages * one_page);
1411         } else {
1412                 if (was_floating) {
1413                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1414                 } else {
1415                         cnt = (nframes_t) floor (prefix * one_page);
1416                 }
1417         }
1418
1419         if (leftmost_frame < cnt) {
1420                 frame = 0;
1421         } else {
1422                 frame = leftmost_frame - cnt;
1423         }
1424
1425         reset_x_origin (frame);
1426 }
1427
1428 void
1429 Editor::scroll_forward (float pages)
1430 {
1431         nframes_t frame;
1432         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1433         bool was_floating;
1434         float prefix;
1435         nframes_t cnt;
1436         
1437         if (get_prefix (prefix, was_floating)) {
1438                 cnt = (nframes_t) floor (pages * one_page);
1439         } else {
1440                 if (was_floating) {
1441                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1442                 } else {
1443                         cnt = (nframes_t) floor (prefix * one_page);
1444                 }
1445         }
1446
1447         if (max_frames - cnt < leftmost_frame) {
1448                 frame = max_frames - cnt;
1449         } else {
1450                 frame = leftmost_frame + cnt;
1451         }
1452
1453         reset_x_origin (frame);
1454 }
1455
1456 void
1457 Editor::scroll_tracks_down ()
1458 {
1459         float prefix;
1460         bool was_floating;
1461         int cnt;
1462
1463         if (get_prefix (prefix, was_floating)) {
1464                 cnt = 1;
1465         } else {
1466                 cnt = (int) floor (prefix);
1467         }
1468
1469         double vert_value = vertical_adjustment.get_value() + (cnt *
1470                 vertical_adjustment.get_page_size());
1471         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1472                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1473         }
1474         vertical_adjustment.set_value (vert_value);
1475 }
1476
1477 void
1478 Editor::scroll_tracks_up ()
1479 {
1480         float prefix;
1481         bool was_floating;
1482         int cnt;
1483
1484         if (get_prefix (prefix, was_floating)) {
1485                 cnt = 1;
1486         } else {
1487                 cnt = (int) floor (prefix);
1488         }
1489
1490         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1491 }
1492
1493 void
1494 Editor::scroll_tracks_down_line ()
1495 {
1496
1497         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1498         double vert_value = adj->get_value() + 20;
1499
1500         if (vert_value>adj->get_upper() - canvas_height) {
1501                 vert_value = adj->get_upper() - canvas_height;
1502         }
1503         adj->set_value (vert_value);
1504 }
1505
1506 void
1507 Editor::scroll_tracks_up_line ()
1508 {
1509         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1510         adj->set_value (adj->get_value() - 20);
1511 }
1512
1513 /* ZOOM */
1514
1515 void
1516 Editor::temporal_zoom_step (bool coarser)
1517 {
1518         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1519
1520         double nfpu;
1521
1522         nfpu = frames_per_unit;
1523         
1524         if (coarser) { 
1525                 nfpu *= 1.61803399;
1526         } else { 
1527                 nfpu = max(1.0,(nfpu/1.61803399));
1528         }
1529
1530         temporal_zoom (nfpu);
1531 }       
1532
1533 void
1534 Editor::temporal_zoom (gdouble fpu)
1535 {
1536         if (!session) return;
1537         
1538         nframes64_t current_page = current_page_frames();
1539         nframes64_t current_leftmost = leftmost_frame;
1540         nframes64_t current_rightmost;
1541         nframes64_t current_center;
1542         nframes64_t new_page_size;
1543         nframes64_t half_page_size;
1544         nframes64_t leftmost_after_zoom = 0;
1545         nframes64_t where;
1546         bool in_track_canvas;
1547         double nfpu;
1548         double l;
1549
1550         /* XXX this limit is also in ::set_frames_per_unit() */
1551
1552         if (frames_per_unit <= 2.0 && fpu <= frames_per_unit) {
1553                 return;
1554         }
1555
1556         nfpu = fpu;
1557         
1558         new_page_size = (nframes_t) floor (canvas_width * nfpu);
1559         half_page_size = new_page_size / 2;
1560
1561         switch (zoom_focus) {
1562         case ZoomFocusLeft:
1563                 leftmost_after_zoom = current_leftmost;
1564                 break;
1565                 
1566         case ZoomFocusRight:
1567                 current_rightmost = leftmost_frame + current_page;
1568                 if (current_rightmost < new_page_size) {
1569                         leftmost_after_zoom = 0;
1570                 } else {
1571                         leftmost_after_zoom = current_rightmost - new_page_size;
1572                 }
1573                 break;
1574                 
1575         case ZoomFocusCenter:
1576                 current_center = current_leftmost + (current_page/2); 
1577                 if (current_center < half_page_size) {
1578                         leftmost_after_zoom = 0;
1579                 } else {
1580                         leftmost_after_zoom = current_center - half_page_size;
1581                 }
1582                 break;
1583                 
1584         case ZoomFocusPlayhead:
1585                 /* try to keep the playhead in the same place */
1586
1587                 where = playhead_cursor->current_frame;
1588                 
1589                 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1590
1591                 if (l < 0) {
1592                         leftmost_after_zoom = 0;
1593                 } else if (l > max_frames) { 
1594                         leftmost_after_zoom = max_frames - new_page_size;
1595                 } else {
1596                         leftmost_after_zoom = (nframes64_t) l;
1597                 }
1598                 break;
1599
1600         case ZoomFocusMouse:
1601                 /* try to keep the mouse over the same point in the display */
1602
1603                 if (!mouse_frame (where, in_track_canvas)) {
1604                         /* use playhead instead */
1605                         where = playhead_cursor->current_frame;
1606
1607                         if (where < half_page_size) {
1608                                 leftmost_after_zoom = 0;
1609                         } else {
1610                                 leftmost_after_zoom = where - half_page_size;
1611                         }
1612
1613                 } else {
1614
1615                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1616
1617                         if (l < 0) {
1618                                 leftmost_after_zoom = 0;
1619                         } else if (l > max_frames) { 
1620                                 leftmost_after_zoom = max_frames - new_page_size;
1621                         } else {
1622                                 leftmost_after_zoom = (nframes64_t) l;
1623                         }
1624                 }
1625
1626                 break;
1627
1628         case ZoomFocusEdit:
1629                 /* try to keep the edit point in the same place */
1630                 where = get_preferred_edit_position ();
1631
1632                 if (where > 0) {
1633
1634                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1635
1636                         if (l < 0) {
1637                                 leftmost_after_zoom = 0;
1638                         } else if (l > max_frames) { 
1639                                 leftmost_after_zoom = max_frames - new_page_size;
1640                         } else {
1641                                 leftmost_after_zoom = (nframes64_t) l;
1642                         }
1643
1644                 } else {
1645                         /* edit point not defined */
1646                         return;
1647                 }
1648                 break;
1649                 
1650         }
1651  
1652         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1653
1654         reposition_and_zoom (leftmost_after_zoom, nfpu);
1655 }       
1656
1657 void
1658 Editor::temporal_zoom_region ()
1659 {
1660
1661         nframes64_t start = max_frames;
1662         nframes64_t end = 0;
1663
1664         ensure_entered_region_selected (true);
1665
1666         if (selection->regions.empty()) {
1667                 info << _("cannot set loop: no region selected") << endmsg;
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                 cerr << "cutting in object mode\n";
3519                 if (!selection->regions.empty() || !selection->points.empty()) {
3520
3521                         begin_reversible_command (opname + _(" objects"));
3522
3523                         if (!selection->regions.empty()) {
3524                                 cerr << "have regions to cut" << endl;
3525                                 cut_copy_regions (op);
3526                                 
3527                                 if (op == Cut) {
3528                                         selection->clear_regions ();
3529                                 }
3530                         }
3531
3532                         if (!selection->points.empty()) {
3533                                 cut_copy_points (op);
3534
3535                                 if (op == Cut) {
3536                                         selection->clear_points ();
3537                                 }
3538                         }
3539
3540                         commit_reversible_command ();   
3541                         break; // terminate case statement here
3542                 } 
3543                 cerr << "nope, now cutting time range" << endl;
3544                 if (!selection->time.empty()) {
3545                         /* don't cause suprises */
3546                         break;
3547                 }
3548                 // fall thru if there was nothing selected
3549                 
3550         case MouseRange:
3551                 if (selection->time.empty()) {
3552                         nframes64_t start, end;
3553                         cerr << "no time selection, get edit op range" << endl;
3554                         if (!get_edit_op_range (start, end)) {
3555                                 cerr << "no edit op range" << endl;
3556                                 return;
3557                         }
3558                         selection->set ((TimeAxisView*) 0, start, end);
3559                 }
3560                         
3561                 begin_reversible_command (opname + _(" range"));
3562                 cut_copy_ranges (op);
3563                 commit_reversible_command ();
3564                 
3565                 if (op == Cut) {
3566                         selection->clear_time ();
3567                 }
3568
3569                 break;
3570                 
3571         default:
3572                 break;
3573         }
3574 }
3575
3576 void
3577 Editor::cut_copy_points (CutCopyOp op)
3578 {
3579         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3580
3581                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3582
3583                 if (atv) {
3584                         atv->cut_copy_clear_objects (selection->points, op);
3585                 } 
3586         }
3587 }
3588
3589 struct PlaylistState {
3590     boost::shared_ptr<Playlist> playlist;
3591     XMLNode*  before;
3592 };
3593
3594 struct lt_playlist {
3595     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3596             return a.playlist < b.playlist;
3597     }
3598 };
3599         
3600 struct PlaylistMapping { 
3601     TimeAxisView* tv;
3602     boost::shared_ptr<AudioPlaylist> pl;
3603
3604     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3605 };
3606
3607 void
3608 Editor::cut_copy_regions (CutCopyOp op)
3609 {
3610         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3611            a map when we want ordered access to both elements. i think.
3612         */
3613
3614         vector<PlaylistMapping> pmap;
3615
3616         nframes_t first_position = max_frames;
3617         
3618         set<PlaylistState, lt_playlist> freezelist;
3619         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3620         
3621         /* get ordering correct before we cut/copy */
3622         
3623         selection->regions.sort_by_position_and_track ();
3624
3625         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3626
3627                 first_position = min ((*x)->region()->position(), first_position);
3628
3629                 if (op == Cut || op == Clear) {
3630                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3631
3632                         if (pl) {
3633
3634                                 PlaylistState before;
3635                                 before.playlist = pl;
3636                                 before.before = &pl->get_state();
3637                                 
3638                                 insert_result = freezelist.insert (before);
3639                                 
3640                                 if (insert_result.second) {
3641                                         pl->freeze ();
3642                                 }
3643                         }
3644                 }
3645
3646                 TimeAxisView* tv = &(*x)->get_trackview();
3647                 vector<PlaylistMapping>::iterator z;
3648
3649                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3650                         if ((*z).tv == tv) {
3651                                 break;
3652                         }
3653                 }
3654                 
3655                 if (z == pmap.end()) {
3656                         pmap.push_back (PlaylistMapping (tv));
3657                 }
3658         }
3659
3660         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3661
3662                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3663                 
3664                 if (!pl) {
3665                         /* impossible, but this handles it for the future */
3666                         continue;
3667                 }
3668
3669                 TimeAxisView& tv = (*x)->get_trackview();
3670                 boost::shared_ptr<AudioPlaylist> npl;
3671                 RegionSelection::iterator tmp;
3672                 
3673                 tmp = x;
3674                 ++tmp;
3675
3676                 vector<PlaylistMapping>::iterator z;
3677                 
3678                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3679                         if ((*z).tv == &tv) {
3680                                 break;
3681                         }
3682                 }
3683                 
3684                 assert (z != pmap.end());
3685                 
3686                 if (!(*z).pl) {
3687                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3688                         npl->freeze();
3689                         (*z).pl = npl;
3690                 } else {
3691                         npl = (*z).pl;
3692                 }
3693                 
3694                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3695                 boost::shared_ptr<Region> _xx;
3696                 
3697                 switch (op) {
3698                 case Cut:
3699                         if (!ar) break;
3700                         
3701                         _xx = RegionFactory::create ((*x)->region());
3702                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3703                         pl->remove_region (((*x)->region()));
3704                         break;
3705                         
3706                 case Copy:
3707                         if (!ar) break;
3708
3709                         /* copy region before adding, so we're not putting same object into two different playlists */
3710                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3711                         break;
3712                         
3713                 case Clear:
3714                         pl->remove_region (((*x)->region()));
3715                         break;
3716                 }
3717
3718                 x = tmp;
3719         }
3720         
3721         list<boost::shared_ptr<Playlist> > foo;
3722         
3723         /* the pmap is in the same order as the tracks in which selected regions occured */
3724         
3725         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3726                 (*i).pl->thaw();
3727                 foo.push_back ((*i).pl);
3728         }
3729         
3730
3731         if (!foo.empty()) {
3732                 cut_buffer->set (foo);
3733         }
3734
3735         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3736                 (*pl).playlist->thaw ();
3737                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3738         }
3739 }
3740
3741 void
3742 Editor::cut_copy_ranges (CutCopyOp op)
3743 {
3744         TrackSelection* ts;
3745
3746         if (selection->tracks.empty()) {
3747                 ts = &track_views;
3748         } else {
3749                 ts = &selection->tracks;
3750         }
3751
3752         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3753                 (*i)->cut_copy_clear (*selection, op);
3754         }
3755 }
3756
3757 void
3758 Editor::paste (float times)
3759 {
3760         paste_internal (get_preferred_edit_position(), times);
3761 }
3762
3763 void
3764 Editor::mouse_paste ()
3765 {
3766         nframes64_t where;
3767         bool ignored;
3768
3769         if (!mouse_frame (where, ignored)) {
3770                 return;
3771         }
3772
3773         snap_to (where);
3774         paste_internal (where, 1);
3775 }
3776
3777 void
3778 Editor::paste_internal (nframes_t position, float times)
3779 {
3780         bool commit = false;
3781
3782         if (cut_buffer->empty()) {
3783                 return;
3784         }
3785
3786         if (position == max_frames) {
3787                 position = get_preferred_edit_position();
3788         }
3789
3790         begin_reversible_command (_("paste"));
3791
3792         TrackSelection ts;
3793         TrackSelection::iterator i;
3794         size_t nth;
3795
3796         /* get everything in the correct order */
3797
3798
3799         if (!selection->tracks.empty()) {
3800                 sort_track_selection ();
3801                 ts = selection->tracks;
3802         } else if (entered_track) {
3803                 ts.push_back (entered_track);
3804         }
3805
3806         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3807
3808                 /* undo/redo is handled by individual tracks */
3809
3810                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3811                         commit = true;
3812                 }
3813         }
3814         
3815         if (commit) {
3816                 commit_reversible_command ();
3817         }
3818 }
3819
3820 void
3821 Editor::paste_named_selection (float times)
3822 {
3823         TrackSelection::iterator t;
3824
3825         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3826
3827         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3828                 return;
3829         }
3830
3831         TreeModel::iterator i = selected->get_selected();
3832         NamedSelection* ns = (*i)[named_selection_columns.selection];
3833
3834         list<boost::shared_ptr<Playlist> >::iterator chunk;
3835         list<boost::shared_ptr<Playlist> >::iterator tmp;
3836
3837         chunk = ns->playlists.begin();
3838                 
3839         begin_reversible_command (_("paste chunk"));
3840         
3841         sort_track_selection ();
3842
3843         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3844                 
3845                 AudioTimeAxisView* atv;
3846                 boost::shared_ptr<Playlist> pl;
3847                 boost::shared_ptr<AudioPlaylist> apl;
3848
3849                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3850                         continue;
3851                 }
3852
3853                 if ((pl = atv->playlist()) == 0) {
3854                         continue;
3855                 }
3856                 
3857                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3858                         continue;
3859                 }
3860
3861                 tmp = chunk;
3862                 ++tmp;
3863
3864                 XMLNode &before = apl->get_state();
3865                 apl->paste (*chunk, get_preferred_edit_position(), times);
3866                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3867
3868                 if (tmp != ns->playlists.end()) {
3869                         chunk = tmp;
3870                 }
3871         }
3872
3873         commit_reversible_command();
3874 }
3875
3876 void
3877 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3878 {
3879         boost::shared_ptr<Playlist> playlist; 
3880         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
3881         RegionSelection foo;
3882
3883         begin_reversible_command (_("duplicate region"));
3884
3885         selection->clear_regions ();
3886
3887         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3888
3889                 boost::shared_ptr<Region> r ((*i)->region());
3890
3891                 TimeAxisView& tv = (*i)->get_time_axis_view();
3892                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3893
3894                 latest_regionviews.clear ();
3895                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3896                 
3897                 playlist = (*i)->region()->playlist();
3898                 XMLNode &before = playlist->get_state();
3899                 playlist->duplicate (r, r->last_frame() + 1, times);
3900                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3901
3902                 c.disconnect ();
3903                 
3904                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3905         }
3906
3907         commit_reversible_command ();
3908
3909         if (!foo.empty()) {
3910                 selection->set (foo);
3911         }
3912 }
3913
3914 void
3915 Editor::duplicate_selection (float times)
3916 {
3917         if (selection->time.empty() || selection->tracks.empty()) {
3918                 return;
3919         }
3920
3921         boost::shared_ptr<Playlist> playlist; 
3922         vector<boost::shared_ptr<AudioRegion> > new_regions;
3923         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3924                 
3925         create_region_from_selection (new_regions);
3926
3927         if (new_regions.empty()) {
3928                 return;
3929         }
3930         
3931         begin_reversible_command (_("duplicate selection"));
3932
3933         ri = new_regions.begin();
3934
3935         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3936                 if ((playlist = (*i)->playlist()) == 0) {
3937                         continue;
3938                 }
3939                 XMLNode &before = playlist->get_state();
3940                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3941                 XMLNode &after = playlist->get_state();
3942                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3943
3944                 ++ri;
3945                 if (ri == new_regions.end()) {
3946                         --ri;
3947                 }
3948         }
3949
3950         commit_reversible_command ();
3951 }
3952
3953 void
3954 Editor::reset_point_selection ()
3955 {
3956         /* reset all selected points to the relevant default value */
3957
3958         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3959                 
3960                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3961                 
3962                 if (atv) {
3963                         atv->reset_objects (selection->points);
3964                 } 
3965         }
3966 }
3967
3968 void
3969 Editor::center_playhead ()
3970 {
3971         float page = canvas_width * frames_per_unit;
3972         center_screen_internal (playhead_cursor->current_frame, page);
3973 }
3974
3975 void
3976 Editor::center_edit_point ()
3977 {
3978         float page = canvas_width * frames_per_unit;
3979         center_screen_internal (get_preferred_edit_position(), page);
3980 }
3981
3982 void
3983 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3984 {
3985         begin_reversible_command (_("clear playlist"));
3986         XMLNode &before = playlist->get_state();
3987         playlist->clear ();
3988         XMLNode &after = playlist->get_state();
3989         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3990         commit_reversible_command ();
3991 }
3992
3993 void
3994 Editor::nudge_track (bool use_edit, bool forwards)
3995 {
3996         boost::shared_ptr<Playlist> playlist; 
3997         nframes_t distance;
3998         nframes_t next_distance;
3999         nframes_t start;
4000
4001         if (use_edit) {
4002                 start = get_preferred_edit_position();
4003         } else {
4004                 start = 0;
4005         }
4006
4007         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4008                 return;
4009         }
4010         
4011         if (selection->tracks.empty()) {
4012                 return;
4013         }
4014         
4015         begin_reversible_command (_("nudge track"));
4016         
4017         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4018
4019                 if ((playlist = (*i)->playlist()) == 0) {
4020                         continue;
4021                 }               
4022                 
4023                 XMLNode &before = playlist->get_state();
4024                 playlist->nudge_after (start, distance, forwards);
4025                 XMLNode &after = playlist->get_state();
4026                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4027         }
4028         
4029         commit_reversible_command ();                   
4030 }
4031
4032 void
4033 Editor::remove_last_capture ()
4034 {
4035         vector<string> choices;
4036         string prompt;
4037         
4038         if (!session) {
4039                 return;
4040         }
4041
4042         if (Config->get_verify_remove_last_capture()) {
4043                 prompt  = _("Do you really want to destroy the last capture?"
4044                             "\n(This is destructive and cannot be undone)");
4045
4046                 choices.push_back (_("No, do nothing."));
4047                 choices.push_back (_("Yes, destroy it."));
4048                 
4049                 Gtkmm2ext::Choice prompter (prompt, choices);
4050                 
4051                 if (prompter.run () == 1) {
4052                         session->remove_last_capture ();
4053                 }
4054
4055         } else {
4056                 session->remove_last_capture();
4057         }
4058 }
4059
4060 void
4061 Editor::normalize_region ()
4062 {
4063         if (!session) {
4064                 return;
4065         }
4066
4067         if (selection->regions.empty()) {
4068                 return;
4069         }
4070
4071         begin_reversible_command (_("normalize"));
4072
4073         track_canvas.get_window()->set_cursor (*wait_cursor);
4074         gdk_flush ();
4075
4076         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4077                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4078                 if (!arv)
4079                         continue;
4080                 XMLNode &before = arv->region()->get_state();
4081                 arv->audio_region()->normalize_to (0.0f);
4082                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4083         }
4084
4085         commit_reversible_command ();
4086         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4087 }
4088
4089
4090 void
4091 Editor::denormalize_region ()
4092 {
4093         if (!session) {
4094                 return;
4095         }
4096
4097         ExclusiveRegionSelection (*this, entered_regionview);
4098
4099         if (selection->regions.empty()) {
4100                 return;
4101         }
4102
4103         begin_reversible_command ("denormalize");
4104
4105         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4106                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4107                 if (!arv)
4108                         continue;
4109                 XMLNode &before = arv->region()->get_state();
4110                 arv->audio_region()->set_scale_amplitude (1.0f);
4111                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4112         }
4113
4114         commit_reversible_command ();
4115 }
4116
4117 void
4118 Editor::adjust_region_scale_amplitude (bool up)
4119 {
4120         if (!session) {
4121                 return;
4122         }
4123
4124         ExclusiveRegionSelection esr (*this, entered_regionview);
4125
4126         if (selection->regions.empty()) {
4127                 return;
4128         }
4129
4130         begin_reversible_command ("denormalize");
4131
4132         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
4133                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4134                 if (!arv)
4135                         continue;
4136                 XMLNode &before = arv->region()->get_state();
4137                 
4138                 double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
4139                 
4140                 if (up) {
4141                         fraction += 0.05;
4142                         fraction = min (fraction, 1.0);
4143                 } else {
4144                         fraction -= 0.05;
4145                         fraction = max (fraction, 0.0);
4146                 }
4147
4148                 if (!up && fraction <= 0) {
4149                         continue;
4150                 }
4151
4152                 fraction = slider_position_to_gain (fraction);
4153                 fraction = coefficient_to_dB (fraction);
4154                 fraction = dB_to_coefficient (fraction);
4155
4156                 if (up && fraction >= 2.0) {
4157                         continue;
4158                 }
4159                 
4160                 arv->audio_region()->set_scale_amplitude (fraction);
4161                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4162         }
4163
4164         commit_reversible_command ();
4165 }
4166
4167
4168 void
4169 Editor::reverse_region ()
4170 {
4171         if (!session) {
4172                 return;
4173         }
4174
4175         Reverse rev (*session);
4176         apply_filter (rev, _("reverse regions"));
4177 }
4178
4179 void
4180 Editor::apply_filter (AudioFilter& filter, string command)
4181 {
4182         if (selection->regions.empty()) {
4183                 return;
4184         }
4185
4186         begin_reversible_command (command);
4187
4188         track_canvas.get_window()->set_cursor (*wait_cursor);
4189         gdk_flush ();
4190
4191         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
4192                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4193                 if (!arv)
4194                         continue;
4195
4196                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4197
4198                 RegionSelection::iterator tmp;
4199                 
4200                 tmp = r;
4201                 ++tmp;
4202
4203                 if (arv->audio_region()->apply (filter) == 0) {
4204
4205                         XMLNode &before = playlist->get_state();
4206                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4207                         XMLNode &after = playlist->get_state();
4208                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4209                 } else {
4210                         goto out;
4211                 }
4212
4213                 r = tmp;
4214         }
4215
4216         commit_reversible_command ();
4217         selection->regions.clear ();
4218
4219   out:
4220         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4221 }
4222
4223 void
4224 Editor::region_selection_op (void (Region::*pmf)(void))
4225 {
4226         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4227                 Region* region = (*i)->region().get();
4228                 (region->*pmf)();
4229         }
4230 }
4231
4232
4233 void
4234 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4235 {
4236         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4237                 Region* region = (*i)->region().get();
4238                 (region->*pmf)(arg);
4239         }
4240 }
4241
4242 void
4243 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4244 {
4245         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4246                 Region* region = (*i)->region().get();
4247                 (region->*pmf)(yn);
4248         }
4249 }
4250
4251 void
4252 Editor::external_edit_region ()
4253 {
4254         if (!clicked_regionview) {
4255                 return;
4256         }
4257
4258         /* more to come */
4259 }
4260
4261 void
4262 Editor::brush (nframes_t pos)
4263 {
4264         RegionSelection sel;
4265         snap_to (pos);
4266
4267         if (selection->regions.empty()) {
4268                 /* XXX get selection from region list */
4269         } else { 
4270                 sel = selection->regions;
4271         }
4272
4273         if (sel.empty()) {
4274                 return;
4275         }
4276
4277         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4278                 mouse_brush_insert_region ((*i), pos);
4279         }
4280 }
4281
4282 void
4283 Editor::reset_region_gain_envelopes ()
4284 {
4285         if (!session || selection->regions.empty()) {
4286                 return;
4287         }
4288
4289         session->begin_reversible_command (_("reset region gain"));
4290
4291         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4292                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4293                 if (arv) {
4294                         AutomationList& alist (arv->audio_region()->envelope());
4295                         XMLNode& before (alist.get_state());
4296
4297                         arv->audio_region()->set_default_envelope ();
4298                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
4299                 }
4300         }
4301
4302         session->commit_reversible_command ();
4303 }
4304
4305 void
4306 Editor::toggle_gain_envelope_visibility ()
4307 {
4308         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4309                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4310                 if (arv) {
4311                         arv->set_envelope_visible (!arv->envelope_visible());
4312                 }
4313         }
4314 }
4315
4316 void
4317 Editor::toggle_gain_envelope_active ()
4318 {
4319         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4320                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4321                 if (arv) {
4322                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4323                 }
4324         }
4325 }
4326
4327 void
4328 Editor::toggle_region_lock ()
4329 {
4330         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4331                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4332                 if (arv) {
4333                         arv->audio_region()->set_locked (!arv->audio_region()->locked());
4334                 }
4335         }
4336 }
4337
4338 void
4339 Editor::toggle_region_mute ()
4340 {
4341         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4342                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4343                 if (arv) {
4344                         arv->audio_region()->set_muted (!arv->audio_region()->muted());
4345                 }
4346         }
4347 }
4348
4349 void
4350 Editor::toggle_region_opaque ()
4351 {
4352         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4353                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4354                 if (arv) {
4355                         arv->audio_region()->set_opaque (!arv->audio_region()->opaque());
4356                 }
4357         }
4358 }
4359
4360 void
4361 Editor::set_fade_length (bool in)
4362 {
4363         ExclusiveRegionSelection esr (*this, entered_regionview);
4364
4365         /* we need a region to measure the offset from the start */
4366
4367         RegionView* rv;
4368
4369         if (!selection->regions.empty()) {
4370                 rv = selection->regions.front();
4371         } else if (entered_regionview) {
4372                 rv = entered_regionview;
4373         } else {
4374                 return;
4375         }
4376
4377         nframes64_t pos = get_preferred_edit_position();
4378         nframes_t len;
4379         char* cmd;
4380         
4381         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4382                 /* edit point is outside the relevant region */
4383                 return;
4384         }
4385
4386         if (in) {
4387                 if (pos <= rv->region()->position()) {
4388                         /* can't do it */
4389                         return;
4390                 }
4391                 len = pos - rv->region()->position();
4392                 cmd = _("set fade in length");
4393         } else {
4394                 if (pos >= rv->region()->last_frame()) {
4395                         /* can't do it */
4396                         return;
4397                 }
4398                 len = rv->region()->last_frame() - pos;
4399                 cmd = _("set fade out length");
4400         }
4401
4402         begin_reversible_command (cmd);
4403
4404         RegionSelection& rs (get_regions_for_action());
4405
4406         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4407                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4408
4409                 if (!tmp) {
4410                         return;
4411                 }
4412
4413                 AutomationList* alist;
4414                 if (in) {
4415                         alist = &tmp->audio_region()->fade_in();
4416                 } else {
4417                         alist = &tmp->audio_region()->fade_out();
4418                 }
4419
4420                 XMLNode &before = alist->get_state();
4421
4422                 if (in) {
4423                         tmp->audio_region()->set_fade_in_length (len);
4424                 } else {
4425                         tmp->audio_region()->set_fade_out_length (len);
4426                 }
4427                 
4428                 XMLNode &after = alist->get_state();
4429                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4430         }
4431
4432         commit_reversible_command ();
4433 }
4434
4435
4436 void
4437 Editor::toggle_fade_active (bool in)
4438 {
4439         ensure_entered_region_selected (true);
4440
4441         if (selection->regions.empty()) {
4442                 return;
4443         }
4444
4445         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4446         bool have_switch = false;
4447         bool yn;
4448
4449         begin_reversible_command (cmd);
4450
4451         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4452                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4453                 
4454                 if (!tmp) {
4455                         return;
4456                 }
4457
4458                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4459
4460                 /* make the behaviour consistent across all regions */
4461                 
4462                 if (!have_switch) {
4463                         if (in) {
4464                                 yn = region->fade_in_active();
4465                         } else {
4466                                 yn = region->fade_out_active();
4467                         }
4468                         have_switch = true;
4469                 }
4470
4471                 XMLNode &before = region->get_state();
4472                 if (in) {
4473                         region->set_fade_in_active (!yn);
4474                 } else {
4475                         region->set_fade_out_active (!yn);
4476                 }
4477                 XMLNode &after = region->get_state();
4478                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4479         }
4480
4481         commit_reversible_command ();
4482 }
4483
4484 void
4485 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4486 {
4487
4488         begin_reversible_command (_("set fade in shape"));
4489
4490         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4491                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4492
4493                 if (!tmp) {
4494                         return;
4495                 }
4496
4497                 AutomationList& alist = tmp->audio_region()->fade_in();
4498                 XMLNode &before = alist.get_state();
4499
4500                 tmp->audio_region()->set_fade_in_shape (shape);
4501                 
4502                 XMLNode &after = alist.get_state();
4503                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4504         }
4505
4506         commit_reversible_command ();
4507                 
4508 }
4509
4510 void
4511 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4512 {
4513         begin_reversible_command (_("set fade out shape"));
4514
4515         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4516                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4517
4518                 if (!tmp) {
4519                         return;
4520                 }
4521
4522                 AutomationList& alist = tmp->audio_region()->fade_out();
4523                 XMLNode &before = alist.get_state();
4524
4525                 tmp->audio_region()->set_fade_out_shape (shape);
4526                 
4527                 XMLNode &after = alist.get_state();
4528                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4529         }
4530
4531         commit_reversible_command ();
4532 }
4533
4534 void
4535 Editor::set_fade_in_active (bool yn)
4536 {
4537         begin_reversible_command (_("set fade in active"));
4538
4539         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4540                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4541
4542                 if (!tmp) {
4543                         return;
4544                 }
4545
4546
4547                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4548
4549                 XMLNode &before = ar->get_state();
4550
4551                 ar->set_fade_in_active (yn);
4552                 
4553                 XMLNode &after = ar->get_state();
4554                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4555         }
4556
4557         commit_reversible_command ();
4558 }
4559
4560 void
4561 Editor::set_fade_out_active (bool yn)
4562 {
4563         begin_reversible_command (_("set fade out active"));
4564
4565         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4566                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4567
4568                 if (!tmp) {
4569                         return;
4570                 }
4571
4572                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4573
4574                 XMLNode &before = ar->get_state();
4575
4576                 ar->set_fade_out_active (yn);
4577                 
4578                 XMLNode &after = ar->get_state();
4579                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4580         }
4581
4582         commit_reversible_command ();
4583 }
4584
4585
4586 /** Update crossfade visibility after its configuration has been changed */
4587 void
4588 Editor::update_xfade_visibility ()
4589 {
4590         _xfade_visibility = Config->get_xfades_visible ();
4591         
4592         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4593                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4594                 if (v) {
4595                         if (_xfade_visibility) {
4596                                 v->show_all_xfades ();
4597                         } else {
4598                                 v->hide_all_xfades ();
4599                         }
4600                 }
4601         }
4602 }
4603
4604 void
4605 Editor::set_edit_point ()
4606 {
4607         nframes64_t where;
4608         bool ignored;
4609
4610         if (!mouse_frame (where, ignored)) {
4611                 return;
4612         }
4613         
4614         snap_to (where);
4615
4616         if (selection->markers.empty()) {
4617                 
4618                 mouse_add_new_marker (where);
4619
4620         } else {
4621                 bool ignored;
4622
4623                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4624
4625                 if (loc) {
4626                         loc->move_to (where);
4627                 }
4628         }
4629 }
4630
4631 void
4632 Editor::set_playhead_cursor ()
4633 {
4634         if (entered_marker) {
4635                 session->request_locate (entered_marker->position(), session->transport_rolling());
4636         } else {
4637                 nframes64_t where;
4638                 bool ignored;
4639
4640                 if (!mouse_frame (where, ignored)) {
4641                         return;
4642                 }
4643                         
4644                 snap_to (where);
4645                 
4646                 if (session) {
4647                         session->request_locate (where, session->transport_rolling());
4648                 }
4649         }
4650 }
4651
4652 void
4653 Editor::split ()
4654 {
4655         ensure_entered_region_selected ();
4656
4657         nframes64_t where = get_preferred_edit_position();
4658
4659         if (!selection->regions.empty()) {
4660                 
4661                 split_regions_at (where, selection->regions);
4662
4663         } else {
4664                 
4665                 RegionSelection rs;
4666                 rs = get_regions_at (where, selection->tracks);
4667                 split_regions_at (where, rs);
4668         }
4669 }
4670
4671 void
4672 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4673 {
4674         if (entered_track && mouse_mode == MouseObject) {
4675                 if (!selection->tracks.empty()) {
4676                         if (!selection->selected (entered_track)) {
4677                                 selection->add (entered_track);
4678                         }
4679                 } else {
4680                         /* there is no selection, but this operation requires/prefers selected objects */
4681
4682                         if (op_really_wants_one_track_if_none_are_selected) {
4683                                 selection->set (entered_track);
4684                         }
4685                 }
4686         }
4687 }
4688
4689 void
4690 Editor::ensure_entered_region_selected (bool op_really_wants_one_region_if_none_are_selected)
4691 {
4692         if (!entered_regionview || mouse_mode != MouseObject) {
4693                 return;
4694         }
4695
4696         
4697         /* heuristic:
4698            
4699         - if there is no existing selection, don't change it. the operation will thus apply to "all"
4700         
4701         - if there is an existing selection, but the entered regionview isn't in it, add it. this
4702         avoids key-mouse ops on unselected regions from interfering with an existing selection,
4703         but also means that the operation will apply to the pointed-at region.
4704         */
4705         
4706         if (!selection->regions.empty()) {
4707                 if (!selection->selected (entered_regionview)) {
4708                         selection->add (entered_regionview);
4709                 }
4710         } else {
4711                 /* there is no selection, but this operation requires/prefers selected objects */
4712                 
4713                 if (op_really_wants_one_region_if_none_are_selected) {
4714                         selection->set (entered_regionview, false);
4715                 }
4716         }
4717 }
4718
4719 void
4720 Editor::trim_region_front ()
4721 {
4722         trim_region (true);
4723 }
4724
4725 void
4726 Editor::trim_region_back ()
4727 {
4728         trim_region (false);
4729 }
4730
4731 void
4732 Editor::trim_region (bool front)
4733 {
4734         ExclusiveRegionSelection ers (*this, entered_regionview);
4735
4736         nframes64_t where = get_preferred_edit_position();
4737         RegionSelection& rs = get_regions_for_action ();
4738
4739         if (rs.empty()) {
4740                 return;
4741         }
4742
4743         begin_reversible_command (front ? _("trim front") : _("trim back"));
4744
4745         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4746                 if (!(*i)->region()->locked()) {
4747                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4748                         XMLNode &before = pl->get_state();
4749                         if (front) {
4750                                 (*i)->region()->trim_front (where, this);       
4751                         } else {
4752                                 (*i)->region()->trim_end (where, this); 
4753                         }
4754                         XMLNode &after = pl->get_state();
4755                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4756                 }
4757         }
4758
4759         commit_reversible_command ();
4760 }
4761
4762 struct EditorOrderRouteSorter {
4763     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4764             /* use of ">" forces the correct sort order */
4765             return a->order_key ("editor") < b->order_key ("editor");
4766     }
4767 };
4768
4769 void
4770 Editor::select_next_route()
4771 {
4772         if (selection->tracks.empty()) {
4773                 selection->set (track_views.front());
4774                 return;
4775         }
4776
4777         TimeAxisView* current = selection->tracks.front();
4778
4779         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4780                 if (*i == current) {
4781                         ++i;
4782                         if (i != track_views.end()) {
4783                                 selection->set (*i);
4784                         } else {
4785                                 selection->set (*(track_views.begin()));
4786                         }
4787                         break;
4788                 }
4789         }
4790 }
4791
4792 void
4793 Editor::select_prev_route()
4794 {
4795         if (selection->tracks.empty()) {
4796                 selection->set (track_views.front());
4797                 return;
4798         }
4799
4800         TimeAxisView* current = selection->tracks.front();
4801
4802         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4803                 if (*i == current) {
4804                         ++i;
4805                         if (i != track_views.rend()) {
4806                                 selection->set (*i);
4807                         } else {
4808                                 selection->set (*(track_views.rbegin()));
4809                         }
4810                         break;
4811                 }
4812         }
4813 }
4814
4815 void
4816 Editor::set_loop_from_selection (bool play)
4817 {
4818         if (session == 0 || selection->time.empty()) {
4819                 return;
4820         }
4821
4822         nframes_t start = selection->time[clicked_selection].start;
4823         nframes_t end = selection->time[clicked_selection].end;
4824         
4825         set_loop_range (start, end,  _("set loop range from selection"));
4826
4827         if (play) {
4828                 session->request_play_loop (true);
4829                 session->request_locate (start, true);
4830         }
4831 }
4832
4833 void
4834 Editor::set_loop_from_edit_range (bool play)
4835 {
4836         if (session == 0) {
4837                 return;
4838         }
4839
4840         nframes64_t start;
4841         nframes64_t end;
4842         
4843         if (!get_edit_op_range (start, end)) {
4844                 return;
4845         }
4846
4847         set_loop_range (start, end,  _("set loop range from edit range"));
4848
4849         if (play) {
4850                 session->request_play_loop (true);
4851                 session->request_locate (start, true);
4852         }
4853 }
4854
4855 void
4856 Editor::set_loop_from_region (bool play)
4857 {
4858         nframes64_t start = max_frames;
4859         nframes64_t end = 0;
4860
4861         ExclusiveRegionSelection esr (*this, entered_regionview);
4862
4863         if (selection->regions.empty()) {
4864                 info << _("cannot set loop: no region selected") << endmsg;
4865                 return;
4866         }
4867
4868         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4869                 if ((*i)->region()->position() < start) {
4870                         start = (*i)->region()->position();
4871                 }
4872                 if ((*i)->region()->last_frame() + 1 > end) {
4873                         end = (*i)->region()->last_frame() + 1;
4874                 }
4875         }
4876
4877         set_loop_range (start, end, _("set loop range from region"));
4878
4879         if (play) {
4880                 session->request_play_loop (true);
4881                 session->request_locate (start, true);
4882         }
4883 }
4884
4885 void
4886 Editor::set_punch_from_selection ()
4887 {
4888         if (session == 0 || selection->time.empty()) {
4889                 return;
4890         }
4891
4892         nframes_t start = selection->time[clicked_selection].start;
4893         nframes_t end = selection->time[clicked_selection].end;
4894         
4895         set_punch_range (start, end,  _("set punch range from selection"));
4896 }
4897
4898 void
4899 Editor::set_punch_from_edit_range ()
4900 {
4901         if (session == 0) {
4902                 return;
4903         }
4904
4905         nframes64_t start;
4906         nframes64_t end;
4907         
4908         if (!get_edit_op_range (start, end)) {
4909                 return;
4910         }
4911
4912         set_punch_range (start, end,  _("set punch range from edit range"));
4913 }
4914
4915 void
4916 Editor::set_punch_from_region ()
4917 {
4918         nframes64_t start = max_frames;
4919         nframes64_t end = 0;
4920
4921         ExclusiveRegionSelection esr (*this, entered_regionview);
4922
4923         if (selection->regions.empty()) {
4924                 info << _("cannot set punch: no region selected") << endmsg;
4925                 return;
4926         }
4927
4928         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4929                 if ((*i)->region()->position() < start) {
4930                         start = (*i)->region()->position();
4931                 }
4932                 if ((*i)->region()->last_frame() + 1 > end) {
4933                         end = (*i)->region()->last_frame() + 1;
4934                 }
4935         }
4936
4937         set_punch_range (start, end, _("set punch range from region"));
4938 }
4939
4940 void
4941 Editor::pitch_shift_regions ()
4942 {
4943         ensure_entered_region_selected (true);
4944         
4945         if (selection->regions.empty()) {
4946                 return;
4947         }
4948
4949         pitch_shift (selection->regions, 1.2);
4950 }
4951         
4952 void
4953 Editor::use_region_as_bar ()
4954 {
4955         if (!session) {
4956                 return;
4957         }
4958
4959         ExclusiveRegionSelection esr (*this, entered_regionview);
4960
4961         if (selection->regions.empty()) {
4962                 return;
4963         }
4964
4965         RegionView* rv = selection->regions.front();
4966
4967         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
4968 }
4969
4970 void
4971 Editor::use_range_as_bar ()
4972 {
4973         nframes64_t start, end;
4974         if (get_edit_op_range (start, end)) {
4975                 define_one_bar (start, end);
4976         }
4977 }
4978
4979 void
4980 Editor::define_one_bar (nframes64_t start, nframes64_t end)
4981 {
4982         nframes64_t length = end - start;
4983
4984         const Meter& m (session->tempo_map().meter_at (start));
4985
4986         /* region length = 1 bar */
4987
4988         /* 1 bar = how many beats per bar */
4989         
4990         double beats_per_bar = m.beats_per_bar();
4991         
4992         /* now we want frames per beat.
4993            we have frames per bar, and beats per bar, so ...
4994         */
4995
4996         double frames_per_beat = length / beats_per_bar;
4997         
4998         /* beats per minute = */
4999
5000         double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
5001
5002         const TempoSection& t (session->tempo_map().tempo_section_at (start));
5003
5004         begin_reversible_command (_("set tempo from region"));
5005         XMLNode& before (session->tempo_map().get_state());
5006
5007         if (t.frame() == start) {
5008                 session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5009         } else {
5010                 session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5011         }
5012
5013         XMLNode& after (session->tempo_map().get_state());
5014
5015         session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
5016         commit_reversible_command ();
5017 }
5018
5019 void
5020 Editor::split_region_at_transients ()
5021 {
5022         list<nframes64_t> transients;
5023
5024         if (!session) {
5025                 return;
5026         }
5027
5028         ExclusiveRegionSelection esr (*this, entered_regionview);
5029
5030         if (selection->regions.empty()) {
5031                 return;
5032         }
5033
5034         show_rhythm_ferret ();
5035         return;
5036 #if 0
5037
5038         cerr << "selection size is " << selection->regions.size() << endl;
5039
5040         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ) {
5041
5042                 RegionSelection::iterator tmp;
5043
5044                 tmp = i;
5045                 ++tmp;
5046
5047                 cerr << "working on " << (*i)->get_item_name() << endl;
5048
5049                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5050                 
5051                 if (!ar) {
5052                         continue;
5053                 }
5054
5055                 boost::shared_ptr<Playlist> pl = ar->playlist();
5056                 
5057                 if (!pl) {
5058                         continue;
5059                 }
5060
5061                 cerr << "getting transients\n";
5062                 
5063                 ar->get_transients (transients);
5064                 nframes64_t start = ar->start();
5065                 nframes64_t pos = ar->position();
5066
5067                 pl->freeze ();
5068                 pl->remove_region (ar);
5069
5070                 cerr << "creating new regions from " << transients.size() << " transients\n";
5071
5072                 for (list<nframes64_t>::iterator x = transients.begin(); x != transients.end(); ++x) {
5073
5074                         nframes_t len = (*x) - start;
5075
5076                         string new_name;
5077
5078                         if (session->region_name (new_name, ar->name())) {
5079                                 continue;
5080                         }
5081
5082                         pl->add_region (RegionFactory::create (ar->get_sources(), start, len, new_name), pos);
5083                         
5084                         start = (*x);
5085                         pos += len;
5086                 }
5087
5088                 pl->thaw ();
5089
5090                 transients.clear ();
5091
5092                 cerr << "done with that one\n";
5093
5094                 i = tmp;
5095         }
5096 #endif
5097 }