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