rationalize all region selection for editor operations
[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         /* don't reset the return frame if its already set */
2277
2278         if ((return_frame = session->requested_return_frame()) < 0) {
2279                 return_frame = session->audible_frame();
2280         }
2281
2282         start_frame = get_preferred_edit_position (true);
2283
2284         if (start_frame >= 0) {
2285                 session->request_roll_at_and_return (start_frame, return_frame);
2286         }
2287 }
2288
2289 void
2290 Editor::play_selection ()
2291 {
2292         if (selection->time.empty()) {
2293                 return;
2294         }
2295
2296         session->request_play_range (true);
2297 }
2298
2299 void
2300 Editor::loop_selected_region ()
2301 {
2302         RegionSelection rs; 
2303
2304         get_regions_for_action (rs);
2305
2306         if (!rs.empty()) {
2307                 RegionView *rv = *(rs.begin());
2308                 Location* tll;
2309
2310                 if ((tll = transport_loop_location()) != 0)  {
2311
2312                         tll->set (rv->region()->position(), rv->region()->last_frame());
2313                         
2314                         // enable looping, reposition and start rolling
2315
2316                         session->request_play_loop (true);
2317                         session->request_locate (tll->start(), false);
2318                         session->request_transport_speed (1.0f);
2319                 }
2320         }
2321 }
2322
2323 void
2324 Editor::play_location (Location& location)
2325 {
2326         if (location.start() <= location.end()) {
2327                 return;
2328         }
2329
2330         session->request_bounded_roll (location.start(), location.end());
2331 }
2332
2333 void
2334 Editor::loop_location (Location& location)
2335 {
2336         if (location.start() <= location.end()) {
2337                 return;
2338         }
2339
2340         Location* tll;
2341
2342         if ((tll = transport_loop_location()) != 0) {
2343                 tll->set (location.start(), location.end());
2344
2345                 // enable looping, reposition and start rolling
2346                 session->request_play_loop (true);
2347                 session->request_locate (tll->start(), true);
2348         }
2349 }
2350
2351 void
2352 Editor::raise_region ()
2353 {
2354         selection->foreach_region (&Region::raise);
2355 }
2356
2357 void
2358 Editor::raise_region_to_top ()
2359 {
2360         selection->foreach_region (&Region::raise_to_top);
2361 }
2362
2363 void
2364 Editor::lower_region ()
2365 {
2366         selection->foreach_region (&Region::lower);
2367 }
2368
2369 void
2370 Editor::lower_region_to_bottom ()
2371 {
2372         selection->foreach_region (&Region::lower_to_bottom);
2373 }
2374
2375 void
2376 Editor::edit_region ()
2377 {
2378         if (clicked_regionview == 0) {
2379                 return;
2380         }
2381         
2382         clicked_regionview->show_region_editor ();
2383 }
2384
2385 void
2386 Editor::rename_region()
2387 {
2388         RegionSelection rs; 
2389
2390         get_regions_for_action (rs);
2391
2392         if (rs.empty()) {
2393                 return;
2394         }
2395
2396         WindowTitle title (Glib::get_application_name());
2397         title += _("Rename Region");
2398
2399         ArdourDialog d (*this, title.get_string(), true, false);
2400         Entry entry;
2401         Label label (_("New name:"));
2402         HBox hbox;
2403
2404         hbox.set_spacing (6);
2405         hbox.pack_start (label, false, false);
2406         hbox.pack_start (entry, true, true);
2407
2408         d.get_vbox()->set_border_width (12);
2409         d.get_vbox()->pack_start (hbox, false, false);
2410
2411         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2412         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2413
2414         d.set_size_request (300, -1);
2415         d.set_position (Gtk::WIN_POS_MOUSE);
2416
2417         entry.set_text (rs.front()->region()->name());
2418         entry.select_region (0, -1);
2419
2420         entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK));
2421         
2422         d.show_all ();
2423         
2424         entry.grab_focus();
2425
2426         int ret = d.run();
2427
2428         d.hide ();
2429
2430         if (ret == RESPONSE_OK) {
2431                 std::string str = entry.get_text();
2432                 strip_whitespace_edges (str);
2433                 if (!str.empty()) {
2434                         rs.front()->region()->set_name (str);
2435                         redisplay_regions ();
2436                 }
2437         }
2438 }
2439
2440 void
2441 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2442 {
2443         if (session->is_auditioning()) {
2444                 session->cancel_audition ();
2445         } 
2446
2447         // note: some potential for creativity here, because region doesn't
2448         // have to belong to the playlist that Route is handling
2449
2450         // bool was_soloed = route.soloed();
2451
2452         route.set_solo (true, this);
2453         
2454         session->request_bounded_roll (region->position(), region->position() + region->length());
2455         
2456         /* XXX how to unset the solo state ? */
2457 }
2458
2459 void
2460 Editor::play_edit_range ()
2461 {
2462         nframes64_t start, end;
2463
2464         if (get_edit_op_range (start, end)) {
2465                 session->request_bounded_roll (start, end);
2466         }
2467 }
2468
2469 void
2470 Editor::play_selected_region ()
2471 {
2472         nframes64_t start = max_frames;
2473         nframes64_t end = 0;
2474         RegionSelection rs; 
2475
2476         get_regions_for_action (rs);
2477          
2478         if (rs.empty()) {
2479                 return;
2480         }
2481
2482         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2483                 if ((*i)->region()->position() < start) {
2484                         start = (*i)->region()->position();
2485                 }
2486                 if ((*i)->region()->last_frame() + 1 > end) {
2487                         end = (*i)->region()->last_frame() + 1;
2488                 }
2489         }
2490
2491         session->request_stop ();
2492         session->request_bounded_roll (start, end);
2493 }
2494
2495 void
2496 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2497 {
2498         session->audition_region (region);
2499 }
2500
2501 void
2502 Editor::build_interthread_progress_window ()
2503 {
2504         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2505
2506         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2507         
2508         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2509         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2510
2511         // GTK2FIX: this button needs a modifiable label
2512
2513         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2514         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2515
2516         interthread_cancel_button.add (interthread_cancel_label);
2517
2518         interthread_progress_window->set_default_size (200, 100);
2519 }
2520
2521 void
2522 Editor::interthread_cancel_clicked ()
2523 {
2524         if (current_interthread_info) {
2525                 current_interthread_info->cancel = true;
2526         }
2527 }
2528
2529 void
2530 Editor::region_from_selection ()
2531 {
2532         if (clicked_trackview == 0) {
2533                 return;
2534         }
2535
2536         if (selection->time.empty()) {
2537                 return;
2538         }
2539
2540         nframes_t start = selection->time[clicked_selection].start;
2541         nframes_t end = selection->time[clicked_selection].end;
2542
2543         nframes_t selection_cnt = end - start + 1;
2544         
2545         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2546                 boost::shared_ptr<AudioRegion> current;
2547                 boost::shared_ptr<Region> current_r;
2548                 boost::shared_ptr<Playlist> pl;
2549
2550                 nframes_t internal_start;
2551                 string new_name;
2552
2553                 if ((pl = (*i)->playlist()) == 0) {
2554                         continue;
2555                 }
2556
2557                 if ((current_r = pl->top_region_at (start)) == 0) {
2558                         continue;
2559                 }
2560
2561                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2562                 // FIXME: audio only
2563                 if (current != 0) {
2564                         internal_start = start - current->position();
2565                         session->region_name (new_name, current->name(), true);
2566                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2567                 }
2568         }
2569 }       
2570
2571 void
2572 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2573 {
2574         if (selection->time.empty() || selection->tracks.empty()) {
2575                 return;
2576         }
2577
2578         nframes_t start = selection->time[clicked_selection].start;
2579         nframes_t end = selection->time[clicked_selection].end;
2580         
2581         sort_track_selection ();
2582
2583         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2584
2585                 boost::shared_ptr<AudioRegion> current;
2586                 boost::shared_ptr<Region> current_r;
2587                 boost::shared_ptr<Playlist> playlist;
2588                 nframes_t internal_start;
2589                 string new_name;
2590
2591                 if ((playlist = (*i)->playlist()) == 0) {
2592                         continue;
2593                 }
2594
2595                 if ((current_r = playlist->top_region_at(start)) == 0) {
2596                         continue;
2597                 }
2598
2599                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2600                         continue;
2601                 }
2602         
2603                 internal_start = start - current->position();
2604                 session->region_name (new_name, current->name(), true);
2605                 
2606                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2607         }
2608 }
2609
2610 void
2611 Editor::split_multichannel_region ()
2612 {
2613         RegionSelection rs; 
2614
2615         get_regions_for_action (rs);
2616
2617         if (rs.empty()) {
2618                 return;
2619         }
2620
2621         vector<boost::shared_ptr<AudioRegion> > v;
2622
2623         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2624
2625                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2626                 
2627                 if (!arv || arv->audio_region()->n_channels() < 2) {
2628                         continue;
2629                 }
2630
2631                 (arv)->audio_region()->separate_by_channel (*session, v);
2632         }
2633 }
2634
2635 void
2636 Editor::new_region_from_selection ()
2637 {
2638         region_from_selection ();
2639         cancel_selection ();
2640 }
2641
2642 static void
2643 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2644 {
2645         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2646         case OverlapNone:
2647                 break;
2648         default:
2649                 rs->push_back (rv);
2650         }
2651 }
2652
2653 void
2654 Editor::separate_regions_between (const TimeSelection& ts)
2655 {
2656         bool in_command = false;
2657         boost::shared_ptr<Playlist> playlist;
2658         RegionSelection new_selection;
2659         TrackSelection tmptracks;
2660
2661         if (selection->tracks.empty()) {
2662                 
2663                 /* use tracks with selected regions */
2664
2665                 RegionSelection rs; 
2666
2667                 get_regions_for_action (rs);
2668
2669                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2670                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2671
2672                         if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) {
2673                                 tmptracks.push_back (tv);
2674                         }
2675                 }
2676
2677                 if (tmptracks.empty()) {
2678                         /* no regions selected: use all tracks */
2679                         tmptracks = track_views;
2680                 }
2681
2682         } else {
2683
2684                 tmptracks = selection->tracks;
2685
2686         }
2687
2688         sort_track_selection (&tmptracks);
2689
2690         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2691
2692                 AudioTimeAxisView* atv;
2693
2694                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2695
2696                         if (atv->is_audio_track()) {
2697
2698                                 /* no edits to destructive tracks */
2699
2700                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2701                                         continue;
2702                                 }
2703                                         
2704                                 if ((playlist = atv->playlist()) != 0) {
2705
2706
2707                                         XMLNode *before;
2708                                         bool got_some;
2709
2710                                         before = &(playlist->get_state());
2711                                         got_some = false;
2712
2713                                         /* XXX need to consider musical time selections here at some point */
2714
2715                                         double speed = atv->get_diskstream()->speed();
2716
2717
2718                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2719
2720                                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2721                                                 latest_regionviews.clear ();
2722
2723                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2724
2725                                                 c.disconnect ();
2726
2727                                                 if (!latest_regionviews.empty()) {
2728                                                         
2729                                                         got_some = true;
2730
2731                                                         atv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2732                                                         
2733                                                         if (!in_command) {
2734                                                                 begin_reversible_command (_("separate"));
2735                                                                 in_command = true;
2736                                                         }
2737                                                         
2738                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2739                                                         
2740                                                 } 
2741                                         }
2742
2743                                         if (!got_some) {
2744                                                 delete before;
2745                                         }
2746                                 }
2747                         }
2748                 }
2749         }
2750
2751         if (in_command) {
2752                 selection->set (new_selection);
2753                 set_mouse_mode (MouseObject);
2754
2755                 commit_reversible_command ();
2756         }
2757 }
2758
2759 void
2760 Editor::separate_region_from_selection ()
2761 {
2762         /* preferentially use *all* ranges in the time selection if we're in range mode
2763            to allow discontiguous operation, since get_edit_op_range() currently
2764            returns a single range.
2765         */
2766         if (mouse_mode == MouseRange && !selection->time.empty()) {
2767
2768                 separate_regions_between (selection->time);
2769
2770         } else {
2771
2772                 nframes64_t start;
2773                 nframes64_t end;
2774                 
2775                 if (get_edit_op_range (start, end)) {
2776                         
2777                         AudioRange ar (start, end, 1);
2778                         TimeSelection ts;
2779                         ts.push_back (ar);
2780
2781                         separate_regions_between (ts);
2782                 }
2783         }
2784 }
2785
2786 void
2787 Editor::separate_regions_using_location (Location& loc)
2788 {
2789         if (loc.is_mark()) {
2790                 return;
2791         }
2792
2793         AudioRange ar (loc.start(), loc.end(), 1);
2794         TimeSelection ts;
2795
2796         ts.push_back (ar);
2797
2798         separate_regions_between (ts);
2799 }
2800
2801 void
2802 Editor::crop_region_to_selection ()
2803 {
2804         if (!selection->time.empty()) {
2805
2806                 crop_region_to (selection->time.start(), selection->time.end_frame());
2807
2808         } else {
2809
2810                 nframes64_t start;
2811                 nframes64_t end;
2812
2813                 if (get_edit_op_range (start, end)) {
2814                         crop_region_to (start, end);
2815                 }
2816         }
2817                 
2818 }               
2819
2820 void
2821 Editor::crop_region_to (nframes_t start, nframes_t end)
2822 {
2823         vector<boost::shared_ptr<Playlist> > playlists;
2824         boost::shared_ptr<Playlist> playlist;
2825         TrackSelection* ts;
2826
2827         if (selection->tracks.empty()) {
2828                 ts = &track_views;
2829         } else {
2830                 sort_track_selection ();
2831                 ts = &selection->tracks;
2832         }
2833         
2834         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2835                 
2836                 AudioTimeAxisView* atv;
2837                 
2838                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2839                         
2840                         if (atv->is_audio_track()) {
2841                                 
2842                                 /* no edits to destructive tracks */
2843
2844                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2845                                         continue;
2846                                 }
2847
2848                                 if ((playlist = atv->playlist()) != 0) {
2849                                         playlists.push_back (playlist);
2850                                 }
2851                         }
2852                 }
2853         }
2854
2855         if (playlists.empty()) {
2856                 return;
2857         }
2858                 
2859         nframes_t the_start;
2860         nframes_t the_end;
2861         nframes_t cnt;
2862         
2863         begin_reversible_command (_("trim to selection"));
2864         
2865         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2866                 
2867                 boost::shared_ptr<Region> region;
2868         
2869                 the_start = start;
2870         
2871                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2872                         continue;
2873                 }
2874                 
2875                 /* now adjust lengths to that we do the right thing
2876                    if the selection extends beyond the region
2877                 */
2878                 
2879                 the_start = max (the_start, region->position());
2880                 if (max_frames - the_start < region->length()) {
2881                         the_end = the_start + region->length() - 1;
2882                 } else {
2883                         the_end = max_frames;
2884                 }
2885                 the_end = min (end, the_end);
2886                 cnt = the_end - the_start + 1;
2887                 
2888                 XMLNode &before = (*i)->get_state();
2889                 region->trim_to (the_start, cnt, this);
2890                 XMLNode &after = (*i)->get_state();
2891                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2892         }
2893         
2894         commit_reversible_command ();
2895 }               
2896
2897 void
2898 Editor::region_fill_track ()
2899 {
2900         nframes_t end;
2901         RegionSelection rs; 
2902
2903         get_regions_for_action (rs);
2904
2905         if (!session || rs.empty()) {
2906                 return;
2907         }
2908
2909         end = session->current_end_frame ();
2910
2911         begin_reversible_command (_("region fill"));
2912
2913         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2914
2915                 boost::shared_ptr<Region> region ((*i)->region());
2916                 
2917                 // FIXME
2918                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2919                 if (!ar)
2920                         continue;
2921
2922                 boost::shared_ptr<Playlist> pl = region->playlist();
2923
2924                 if (end <= region->last_frame()) {
2925                         return;
2926                 }
2927
2928                 double times = (double) (end - region->last_frame()) / (double) region->length();
2929
2930                 if (times == 0) {
2931                         return;
2932                 }
2933
2934                 XMLNode &before = pl->get_state();
2935                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2936                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2937         }
2938
2939         commit_reversible_command ();
2940 }
2941
2942 void
2943 Editor::region_fill_selection ()
2944 {
2945         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2946                 return;
2947         }
2948
2949         if (selection->time.empty()) {
2950                 return;
2951         }
2952
2953
2954         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2955
2956         if (selected->count_selected_rows() != 1) {
2957                 return;
2958         }
2959
2960         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2961         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2962
2963         nframes_t start = selection->time[clicked_selection].start;
2964         nframes_t end = selection->time[clicked_selection].end;
2965
2966         boost::shared_ptr<Playlist> playlist; 
2967
2968         if (selection->tracks.empty()) {
2969                 return;
2970         }
2971
2972         nframes_t selection_length = end - start;
2973         float times = (float)selection_length / region->length();
2974         
2975         begin_reversible_command (_("fill selection"));
2976         
2977         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2978
2979                 if ((playlist = (*i)->playlist()) == 0) {
2980                         continue;
2981                 }               
2982                 
2983                 XMLNode &before = playlist->get_state();
2984                 playlist->add_region (RegionFactory::create (region), start, times);
2985                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2986         }
2987         
2988         commit_reversible_command ();                   
2989 }
2990
2991 void
2992 Editor::set_region_sync_from_edit_point ()
2993 {
2994         nframes64_t where = get_preferred_edit_position ();
2995         RegionSelection rs;
2996         get_regions_for_action (rs);
2997         set_sync_point (where, rs);
2998 }
2999
3000 void
3001 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
3002 {
3003         bool in_command = false;
3004
3005         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3006                 
3007                 if (!(*r)->region()->covers (where)) {
3008                         continue;
3009                 }
3010
3011                 boost::shared_ptr<Region> region ((*r)->region());
3012
3013                 if (!in_command) {
3014                         begin_reversible_command (_("set sync point"));
3015                         in_command = true;
3016                 }
3017
3018                 XMLNode &before = region->playlist()->get_state();
3019                 region->set_sync_position (get_preferred_edit_position());
3020                 XMLNode &after = region->playlist()->get_state();
3021                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3022         }
3023
3024         if (in_command) {
3025                 commit_reversible_command ();
3026         }
3027 }
3028
3029 void
3030 Editor::remove_region_sync ()
3031 {
3032         if (clicked_regionview) {
3033                 boost::shared_ptr<Region> region (clicked_regionview->region());
3034                 begin_reversible_command (_("remove sync"));
3035                 XMLNode &before = region->playlist()->get_state();
3036                 region->clear_sync_position ();
3037                 XMLNode &after = region->playlist()->get_state();
3038                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3039                 commit_reversible_command ();
3040         }
3041 }
3042
3043 void
3044 Editor::naturalize ()
3045 {
3046         RegionSelection rs; 
3047
3048         get_regions_for_action (rs);
3049
3050         if (rs.empty()) {
3051                 return;
3052         }
3053
3054         begin_reversible_command (_("naturalize"));
3055         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3056                 XMLNode &before = (*i)->region()->get_state();
3057                 (*i)->region()->move_to_natural_position (this);
3058                 XMLNode &after = (*i)->region()->get_state();
3059                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
3060         }
3061         commit_reversible_command ();
3062 }
3063
3064 void
3065 Editor::align (RegionPoint what)
3066 {
3067         RegionSelection rs; 
3068
3069         get_regions_for_action (rs);
3070         nframes64_t where = get_preferred_edit_position();
3071
3072         if (!rs.empty()) {
3073                 align_selection (what, where, rs);
3074         } else {
3075
3076                 RegionSelection rs;
3077                 get_regions_at (rs, where, selection->tracks);
3078                 align_selection (what, where, rs);
3079         }
3080 }
3081
3082 void
3083 Editor::align_relative (RegionPoint what)
3084 {
3085         nframes64_t where = get_preferred_edit_position();
3086         RegionSelection rs; 
3087
3088         get_regions_for_action (rs);
3089
3090         if (!rs.empty()) {
3091                 align_selection_relative (what, where, rs);
3092         } 
3093 }
3094
3095 struct RegionSortByTime {
3096     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
3097             return a->region()->position() < b->region()->position();
3098     }
3099 };
3100
3101 void
3102 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
3103 {
3104         if (rs.empty()) {
3105                 return;
3106         }
3107
3108         nframes_t distance;
3109         nframes_t pos = 0;
3110         int dir;
3111
3112         list<RegionView*> sorted;
3113         rs.by_position (sorted);
3114         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3115
3116         switch (point) {
3117         case Start:
3118                 pos = position;
3119                 if (position > r->position()) {
3120                         distance = position - r->position();
3121                         dir = 1;
3122                 } else {
3123                         distance = r->position() - position;
3124                         dir = -1;
3125                 }
3126                 break;
3127                 
3128         case End:
3129                 if (position > r->last_frame()) {
3130                         distance = position - r->last_frame();
3131                         pos = r->position() + distance;
3132                         dir = 1;
3133                 } else {
3134                         distance = r->last_frame() - position;
3135                         pos = r->position() - distance;
3136                         dir = -1;
3137                 }
3138                 break;
3139
3140         case SyncPoint:
3141                 pos = r->adjust_to_sync (position);
3142                 if (pos > r->position()) {
3143                         distance = pos - r->position();
3144                         dir = 1;
3145                 } else {
3146                         distance = r->position() - pos;
3147                         dir = -1;
3148                 }
3149                 break;  
3150         }
3151
3152         if (pos == r->position()) {
3153                 return;
3154         }
3155
3156         begin_reversible_command (_("align selection (relative)"));
3157
3158         /* move first one specially */
3159
3160         XMLNode &before = r->playlist()->get_state();
3161         r->set_position (pos, this);
3162         XMLNode &after = r->playlist()->get_state();
3163         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
3164
3165         /* move rest by the same amount */
3166         
3167         RegionSelection::const_iterator i = rs.begin();
3168         ++i;
3169
3170         for (; i != rs.end(); ++i) {
3171
3172                 boost::shared_ptr<Region> region ((*i)->region());
3173
3174                 XMLNode &before = region->playlist()->get_state();
3175                 
3176                 if (dir > 0) {
3177                         region->set_position (region->position() + distance, this);
3178                 } else {
3179                         region->set_position (region->position() - distance, this);
3180                 }
3181
3182                 XMLNode &after = region->playlist()->get_state();
3183                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3184
3185         }
3186
3187         commit_reversible_command ();
3188 }
3189
3190 void
3191 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
3192 {
3193         if (rs.empty()) {
3194                 return;
3195         }
3196
3197         begin_reversible_command (_("align selection"));
3198
3199         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3200                 align_region_internal ((*i)->region(), point, position);
3201         }
3202
3203         commit_reversible_command ();
3204 }
3205
3206 void
3207 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3208 {
3209         begin_reversible_command (_("align region"));
3210         align_region_internal (region, point, position);
3211         commit_reversible_command ();
3212 }
3213
3214 void
3215 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
3216 {
3217         XMLNode &before = region->playlist()->get_state();
3218
3219         switch (point) {
3220         case SyncPoint:
3221                 region->set_position (region->adjust_to_sync (position), this);
3222                 break;
3223
3224         case End:
3225                 if (position > region->length()) {
3226                         region->set_position (position - region->length(), this);
3227                 }
3228                 break;
3229
3230         case Start:
3231                 region->set_position (position, this);
3232                 break;
3233         }
3234
3235         XMLNode &after = region->playlist()->get_state();
3236         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3237 }       
3238
3239 void
3240 Editor::trim_region_to_loop ()
3241 {
3242         Location* loc = session->locations()->auto_loop_location();
3243         if (!loc) {
3244                 return;
3245         }
3246         trim_region_to_location (*loc, _("trim to loop"));
3247 }
3248
3249 void
3250 Editor::trim_region_to_punch ()
3251 {
3252         Location* loc = session->locations()->auto_punch_location();
3253         if (!loc) {
3254                 return;
3255         }
3256         trim_region_to_location (*loc, _("trim to punch"));
3257 }
3258
3259 void
3260 Editor::trim_region_to_location (const Location& loc, const char* str)
3261 {
3262         RegionSelection rs;
3263
3264         get_regions_for_action (rs);
3265
3266         begin_reversible_command (str);
3267
3268         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3269                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3270
3271                 if (!arv) {
3272                         continue;
3273                 }
3274
3275                 /* require region to span proposed trim */
3276
3277                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3278                 case OverlapInternal:
3279                         break;
3280                 default:
3281                         continue;
3282                 }
3283                                 
3284                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3285
3286                 if (!atav) {
3287                         return;
3288                 }
3289
3290                 float speed = 1.0;
3291                 nframes_t start;
3292                 nframes_t end;
3293
3294                 if (atav->get_diskstream() != 0) {
3295                         speed = atav->get_diskstream()->speed();
3296                 }
3297
3298                 start = session_frame_to_track_frame (loc.start(), speed);
3299                 end = session_frame_to_track_frame (loc.end(), speed);
3300
3301                 XMLNode &before = arv->region()->playlist()->get_state();
3302                 arv->region()->trim_to (start, (end - start), this);
3303                 XMLNode &after = arv->region()->playlist()->get_state();
3304                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3305         }
3306                 
3307         commit_reversible_command ();
3308 }
3309
3310 void
3311 Editor::trim_region_to_edit_point ()
3312 {
3313         RegionSelection rs;
3314         
3315         get_regions_for_action (rs);
3316
3317         nframes64_t where = get_preferred_edit_position();
3318
3319         begin_reversible_command (_("trim region start to edit point"));
3320
3321         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3322                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3323
3324                 if (!arv) {
3325                         continue;
3326                 }
3327
3328                 /* require region to cover trim */
3329
3330                 if (!arv->region()->covers (where)) {
3331                         continue;
3332                 }
3333
3334                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3335
3336                 if (!atav) {
3337                         return;
3338                 }
3339
3340                 float speed = 1.0;
3341
3342                 if (atav->get_diskstream() != 0) {
3343                         speed = atav->get_diskstream()->speed();
3344                 }
3345
3346                 XMLNode &before = arv->region()->playlist()->get_state();
3347                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3348                 XMLNode &after = arv->region()->playlist()->get_state();
3349                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3350         }
3351                 
3352         commit_reversible_command ();
3353 }
3354
3355 void
3356 Editor::trim_region_from_edit_point ()
3357 {
3358         RegionSelection rs;
3359
3360         get_regions_for_action (rs);
3361
3362         nframes64_t where = get_preferred_edit_position();
3363
3364         begin_reversible_command (_("trim region end to edit point"));
3365
3366         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3367                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3368
3369                 if (!arv) {
3370                         continue;
3371                 }
3372
3373                 /* require region to cover trim */
3374
3375                 if (!arv->region()->covers (where)) {
3376                         continue;
3377                 }
3378
3379                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3380
3381                 if (!atav) {
3382                         return;
3383                 }
3384
3385                 float speed = 1.0;
3386
3387                 if (atav->get_diskstream() != 0) {
3388                         speed = atav->get_diskstream()->speed();
3389                 }
3390
3391                 XMLNode &before = arv->region()->playlist()->get_state();
3392                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3393                 XMLNode &after = arv->region()->playlist()->get_state();
3394                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3395         }
3396                 
3397         commit_reversible_command ();
3398 }
3399
3400 void
3401 Editor::unfreeze_route ()
3402 {
3403         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3404                 return;
3405         }
3406         
3407         clicked_audio_trackview->audio_track()->unfreeze ();
3408 }
3409
3410 void*
3411 Editor::_freeze_thread (void* arg)
3412 {
3413         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
3414         return static_cast<Editor*>(arg)->freeze_thread ();
3415 }
3416
3417 void*
3418 Editor::freeze_thread ()
3419 {
3420         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
3421         return 0;
3422 }
3423
3424 gint
3425 Editor::freeze_progress_timeout (void *arg)
3426 {
3427         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
3428         return !(current_interthread_info->done || current_interthread_info->cancel);
3429 }
3430
3431 void
3432 Editor::freeze_route ()
3433 {
3434         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3435                 return;
3436         }
3437         
3438         InterThreadInfo itt;
3439
3440         if (interthread_progress_window == 0) {
3441                 build_interthread_progress_window ();
3442         }
3443
3444         WindowTitle title(Glib::get_application_name());
3445         title += _("Freeze");
3446         interthread_progress_window->set_title (title.get_string());
3447         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3448         interthread_progress_window->show_all ();
3449         interthread_progress_bar.set_fraction (0.0f);
3450         interthread_progress_label.set_text ("");
3451         interthread_cancel_label.set_text (_("Cancel Freeze"));
3452         current_interthread_info = &itt;
3453
3454         interthread_progress_connection = 
3455           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3456
3457         itt.done = false;
3458         itt.cancel = false;
3459         itt.progress = 0.0f;
3460         
3461         pthread_attr_t attr;
3462         pthread_attr_init(&attr);
3463         pthread_attr_setstacksize(&attr, 500000);
3464
3465         pthread_create (&itt.thread, &attr, _freeze_thread, this);
3466
3467         pthread_attr_destroy(&attr);
3468
3469         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3470
3471         while (!itt.done && !itt.cancel) {
3472                 gtk_main_iteration ();
3473         }
3474
3475         interthread_progress_connection.disconnect ();
3476         interthread_progress_window->hide_all ();
3477         current_interthread_info = 0;
3478         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3479 }
3480
3481 void
3482 Editor::bounce_range_selection ()
3483 {
3484         if (selection->time.empty()) {
3485                 return;
3486         }
3487
3488         TrackSelection views = selection->tracks;
3489
3490         nframes_t start = selection->time[clicked_selection].start;
3491         nframes_t end = selection->time[clicked_selection].end;
3492         nframes_t cnt = end - start + 1;
3493
3494         begin_reversible_command (_("bounce range"));
3495
3496         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3497
3498                 AudioTimeAxisView* atv;
3499
3500                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
3501                         continue;
3502                 }
3503                 
3504                 boost::shared_ptr<Playlist> playlist;
3505                 
3506                 if ((playlist = atv->playlist()) == 0) {
3507                         return;
3508                 }
3509
3510                 InterThreadInfo itt;
3511                 
3512                 itt.done = false;
3513                 itt.cancel = false;
3514                 itt.progress = false;
3515
3516                 XMLNode &before = playlist->get_state();
3517                 atv->audio_track()->bounce_range (start, cnt, itt);
3518                 XMLNode &after = playlist->get_state();
3519                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3520         }
3521         
3522         commit_reversible_command ();
3523 }
3524
3525 void
3526 Editor::cut ()
3527 {
3528         cut_copy (Cut);
3529 }
3530
3531 void
3532 Editor::copy ()
3533 {
3534         cut_copy (Copy);
3535 }
3536
3537 void 
3538 Editor::cut_copy (CutCopyOp op)
3539 {
3540         /* only cancel selection if cut/copy is successful.*/
3541
3542         string opname;
3543
3544         switch (op) {
3545         case Cut:
3546                 opname = _("cut");
3547                 break;
3548         case Copy:
3549                 opname = _("copy");
3550                 break;
3551         case Clear:
3552                 opname = _("clear");
3553                 break;
3554         }
3555         
3556         cut_buffer->clear ();
3557
3558         if (entered_marker) {
3559
3560                 /* cut/delete op while pointing at a marker */
3561
3562                 bool ignored;
3563                 Location* loc = find_location_from_marker (entered_marker, ignored);
3564
3565                 if (session && loc) {
3566                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3567                 }
3568
3569                 return;
3570         }
3571
3572         RegionSelection rs; 
3573
3574         get_regions_for_action (rs);
3575
3576         switch (current_mouse_mode()) {
3577         case MouseObject: 
3578                 if (!rs.empty() || !selection->points.empty()) {
3579
3580                         begin_reversible_command (opname + _(" objects"));
3581
3582                         if (!rs.empty()) {
3583                                 cut_copy_regions (op);
3584                                 
3585                                 if (op == Cut) {
3586                                         selection->clear_regions ();
3587                                 }
3588                         }
3589
3590                         if (!selection->points.empty()) {
3591                                 cut_copy_points (op);
3592
3593                                 if (op == Cut) {
3594                                         selection->clear_points ();
3595                                 }
3596                         }
3597
3598                         commit_reversible_command ();   
3599                         break; // terminate case statement here
3600                 } 
3601                 if (!selection->time.empty()) {
3602                         /* don't cause suprises */
3603                         break;
3604                 }
3605                 // fall thru if there was nothing selected
3606                 
3607         case MouseRange:
3608                 if (selection->time.empty()) {
3609                         nframes64_t start, end;
3610                         if (!get_edit_op_range (start, end)) {
3611                                 return;
3612                         }
3613                         selection->set ((TimeAxisView*) 0, start, end);
3614                 }
3615                         
3616                 begin_reversible_command (opname + _(" range"));
3617                 cut_copy_ranges (op);
3618                 commit_reversible_command ();
3619                 
3620                 if (op == Cut) {
3621                         selection->clear_time ();
3622                 }
3623
3624                 break;
3625                 
3626         default:
3627                 break;
3628         }
3629 }
3630
3631 void
3632 Editor::cut_copy_points (CutCopyOp op)
3633 {
3634         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3635
3636                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3637
3638                 if (atv) {
3639                         atv->cut_copy_clear_objects (selection->points, op);
3640                 } 
3641         }
3642 }
3643
3644 struct PlaylistState {
3645     boost::shared_ptr<Playlist> playlist;
3646     XMLNode*  before;
3647 };
3648
3649 struct lt_playlist {
3650     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3651             return a.playlist < b.playlist;
3652     }
3653 };
3654         
3655 struct PlaylistMapping { 
3656     TimeAxisView* tv;
3657     boost::shared_ptr<AudioPlaylist> pl;
3658
3659     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3660 };
3661
3662 void
3663 Editor::cut_copy_regions (CutCopyOp op)
3664 {
3665         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3666            a map when we want ordered access to both elements. i think.
3667         */
3668
3669         vector<PlaylistMapping> pmap;
3670
3671         nframes_t first_position = max_frames;
3672         
3673         set<PlaylistState, lt_playlist> freezelist;
3674         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3675         
3676         /* get ordering correct before we cut/copy */
3677         
3678         RegionSelection rs; 
3679
3680         get_regions_for_action (rs);
3681         rs.sort_by_position_and_track ();
3682
3683         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3684
3685                 first_position = min ((*x)->region()->position(), first_position);
3686
3687                 if (op == Cut || op == Clear) {
3688                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3689
3690                         if (pl) {
3691
3692                                 PlaylistState before;
3693                                 before.playlist = pl;
3694                                 before.before = &pl->get_state();
3695                                 
3696                                 insert_result = freezelist.insert (before);
3697                                 
3698                                 if (insert_result.second) {
3699                                         pl->freeze ();
3700                                 }
3701                         }
3702                 }
3703
3704                 TimeAxisView* tv = &(*x)->get_trackview();
3705                 vector<PlaylistMapping>::iterator z;
3706
3707                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3708                         if ((*z).tv == tv) {
3709                                 break;
3710                         }
3711                 }
3712                 
3713                 if (z == pmap.end()) {
3714                         pmap.push_back (PlaylistMapping (tv));
3715                 }
3716         }
3717
3718         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3719
3720                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3721                 
3722                 if (!pl) {
3723                         /* impossible, but this handles it for the future */
3724                         continue;
3725                 }
3726
3727                 TimeAxisView& tv = (*x)->get_trackview();
3728                 boost::shared_ptr<AudioPlaylist> npl;
3729                 RegionSelection::iterator tmp;
3730                 
3731                 tmp = x;
3732                 ++tmp;
3733
3734                 vector<PlaylistMapping>::iterator z;
3735                 
3736                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3737                         if ((*z).tv == &tv) {
3738                                 break;
3739                         }
3740                 }
3741                 
3742                 assert (z != pmap.end());
3743                 
3744                 if (!(*z).pl) {
3745                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3746                         npl->freeze();
3747                         (*z).pl = npl;
3748                 } else {
3749                         npl = (*z).pl;
3750                 }
3751                 
3752                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3753                 boost::shared_ptr<Region> _xx;
3754                 
3755                 switch (op) {
3756                 case Cut:
3757                         if (!ar) break;
3758                         
3759                         _xx = RegionFactory::create ((*x)->region());
3760                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3761                         pl->remove_region (((*x)->region()));
3762                         break;
3763                         
3764                 case Copy:
3765                         if (!ar) break;
3766
3767                         /* copy region before adding, so we're not putting same object into two different playlists */
3768                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3769                         break;
3770                         
3771                 case Clear:
3772                         pl->remove_region (((*x)->region()));
3773                         break;
3774                 }
3775
3776                 x = tmp;
3777         }
3778         
3779         list<boost::shared_ptr<Playlist> > foo;
3780         
3781         /* the pmap is in the same order as the tracks in which selected regions occured */
3782         
3783         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3784                 (*i).pl->thaw();
3785                 foo.push_back ((*i).pl);
3786         }
3787         
3788
3789         if (!foo.empty()) {
3790                 cut_buffer->set (foo);
3791         }
3792
3793         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3794                 (*pl).playlist->thaw ();
3795                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3796         }
3797 }
3798
3799 void
3800 Editor::cut_copy_ranges (CutCopyOp op)
3801 {
3802         TrackSelection* ts;
3803
3804         if (selection->tracks.empty()) {
3805                 ts = &track_views;
3806         } else {
3807                 ts = &selection->tracks;
3808         }
3809
3810         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3811                 (*i)->cut_copy_clear (*selection, op);
3812         }
3813 }
3814
3815 void
3816 Editor::paste (float times)
3817 {
3818         paste_internal (get_preferred_edit_position(), times);
3819 }
3820
3821 void
3822 Editor::mouse_paste ()
3823 {
3824         nframes64_t where;
3825         bool ignored;
3826
3827         if (!mouse_frame (where, ignored)) {
3828                 return;
3829         }
3830
3831         snap_to (where);
3832         paste_internal (where, 1);
3833 }
3834
3835 void
3836 Editor::paste_internal (nframes_t position, float times)
3837 {
3838         bool commit = false;
3839
3840         if (cut_buffer->empty()) {
3841                 return;
3842         }
3843
3844         if (position == max_frames) {
3845                 position = get_preferred_edit_position();
3846         }
3847
3848         begin_reversible_command (_("paste"));
3849
3850         TrackSelection ts;
3851         TrackSelection::iterator i;
3852         size_t nth;
3853
3854         /* get everything in the correct order */
3855
3856
3857         if (!selection->tracks.empty()) {
3858                 sort_track_selection ();
3859                 ts = selection->tracks;
3860         } else if (entered_track) {
3861                 ts.push_back (entered_track);
3862         }
3863
3864         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
3865
3866                 /* undo/redo is handled by individual tracks */
3867
3868                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3869                         commit = true;
3870                 }
3871         }
3872         
3873         if (commit) {
3874                 commit_reversible_command ();
3875         }
3876 }
3877
3878 void
3879 Editor::paste_named_selection (float times)
3880 {
3881         TrackSelection::iterator t;
3882
3883         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3884
3885         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3886                 return;
3887         }
3888
3889         TreeModel::iterator i = selected->get_selected();
3890         NamedSelection* ns = (*i)[named_selection_columns.selection];
3891
3892         list<boost::shared_ptr<Playlist> >::iterator chunk;
3893         list<boost::shared_ptr<Playlist> >::iterator tmp;
3894
3895         chunk = ns->playlists.begin();
3896                 
3897         begin_reversible_command (_("paste chunk"));
3898         
3899         sort_track_selection ();
3900
3901         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3902                 
3903                 AudioTimeAxisView* atv;
3904                 boost::shared_ptr<Playlist> pl;
3905                 boost::shared_ptr<AudioPlaylist> apl;
3906
3907                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3908                         continue;
3909                 }
3910
3911                 if ((pl = atv->playlist()) == 0) {
3912                         continue;
3913                 }
3914                 
3915                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3916                         continue;
3917                 }
3918
3919                 tmp = chunk;
3920                 ++tmp;
3921
3922                 XMLNode &before = apl->get_state();
3923                 apl->paste (*chunk, get_preferred_edit_position(), times);
3924                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3925
3926                 if (tmp != ns->playlists.end()) {
3927                         chunk = tmp;
3928                 }
3929         }
3930
3931         commit_reversible_command();
3932 }
3933
3934 void
3935 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3936 {
3937         boost::shared_ptr<Playlist> playlist; 
3938         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
3939         RegionSelection foo;
3940
3941         begin_reversible_command (_("duplicate region"));
3942
3943         selection->clear_regions ();
3944
3945         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3946
3947                 boost::shared_ptr<Region> r ((*i)->region());
3948
3949                 TimeAxisView& tv = (*i)->get_time_axis_view();
3950                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3951
3952                 latest_regionviews.clear ();
3953                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3954                 
3955                 playlist = (*i)->region()->playlist();
3956                 XMLNode &before = playlist->get_state();
3957                 playlist->duplicate (r, r->last_frame() + 1, times);
3958                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3959
3960                 c.disconnect ();
3961                 
3962                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3963         }
3964
3965         commit_reversible_command ();
3966
3967         if (!foo.empty()) {
3968                 selection->set (foo);
3969         }
3970 }
3971
3972 void
3973 Editor::duplicate_selection (float times)
3974 {
3975         if (selection->time.empty() || selection->tracks.empty()) {
3976                 return;
3977         }
3978
3979         boost::shared_ptr<Playlist> playlist; 
3980         vector<boost::shared_ptr<AudioRegion> > new_regions;
3981         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3982                 
3983         create_region_from_selection (new_regions);
3984
3985         if (new_regions.empty()) {
3986                 return;
3987         }
3988         
3989         begin_reversible_command (_("duplicate selection"));
3990
3991         ri = new_regions.begin();
3992
3993         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3994                 if ((playlist = (*i)->playlist()) == 0) {
3995                         continue;
3996                 }
3997                 XMLNode &before = playlist->get_state();
3998                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3999                 XMLNode &after = playlist->get_state();
4000                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4001
4002                 ++ri;
4003                 if (ri == new_regions.end()) {
4004                         --ri;
4005                 }
4006         }
4007
4008         commit_reversible_command ();
4009 }
4010
4011 void
4012 Editor::reset_point_selection ()
4013 {
4014         /* reset all selected points to the relevant default value */
4015
4016         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4017                 
4018                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
4019                 
4020                 if (atv) {
4021                         atv->reset_objects (selection->points);
4022                 } 
4023         }
4024 }
4025
4026 void
4027 Editor::center_playhead ()
4028 {
4029         float page = canvas_width * frames_per_unit;
4030         center_screen_internal (playhead_cursor->current_frame, page);
4031 }
4032
4033 void
4034 Editor::center_edit_point ()
4035 {
4036         float page = canvas_width * frames_per_unit;
4037         center_screen_internal (get_preferred_edit_position(), page);
4038 }
4039
4040 void
4041 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4042 {
4043         begin_reversible_command (_("clear playlist"));
4044         XMLNode &before = playlist->get_state();
4045         playlist->clear ();
4046         XMLNode &after = playlist->get_state();
4047         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4048         commit_reversible_command ();
4049 }
4050
4051 void
4052 Editor::nudge_track (bool use_edit, bool forwards)
4053 {
4054         boost::shared_ptr<Playlist> playlist; 
4055         nframes_t distance;
4056         nframes_t next_distance;
4057         nframes_t start;
4058
4059         if (use_edit) {
4060                 start = get_preferred_edit_position();
4061         } else {
4062                 start = 0;
4063         }
4064
4065         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4066                 return;
4067         }
4068         
4069         if (selection->tracks.empty()) {
4070                 return;
4071         }
4072         
4073         begin_reversible_command (_("nudge track"));
4074         
4075         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4076
4077                 if ((playlist = (*i)->playlist()) == 0) {
4078                         continue;
4079                 }               
4080                 
4081                 XMLNode &before = playlist->get_state();
4082                 playlist->nudge_after (start, distance, forwards);
4083                 XMLNode &after = playlist->get_state();
4084                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4085         }
4086         
4087         commit_reversible_command ();                   
4088 }
4089
4090 void
4091 Editor::remove_last_capture ()
4092 {
4093         vector<string> choices;
4094         string prompt;
4095         
4096         if (!session) {
4097                 return;
4098         }
4099
4100         if (Config->get_verify_remove_last_capture()) {
4101                 prompt  = _("Do you really want to destroy the last capture?"
4102                             "\n(This is destructive and cannot be undone)");
4103
4104                 choices.push_back (_("No, do nothing."));
4105                 choices.push_back (_("Yes, destroy it."));
4106                 
4107                 Gtkmm2ext::Choice prompter (prompt, choices);
4108                 
4109                 if (prompter.run () == 1) {
4110                         session->remove_last_capture ();
4111                 }
4112
4113         } else {
4114                 session->remove_last_capture();
4115         }
4116 }
4117
4118 void
4119 Editor::normalize_region ()
4120 {
4121         RegionSelection rs; 
4122
4123         get_regions_for_action (rs);
4124         
4125         if (!session) {
4126                 return;
4127         }
4128
4129         if (rs.empty()) {
4130                 return;
4131         }
4132
4133         begin_reversible_command (_("normalize"));
4134
4135         track_canvas.get_window()->set_cursor (*wait_cursor);
4136         gdk_flush ();
4137
4138         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4139                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4140                 if (!arv)
4141                         continue;
4142                 XMLNode &before = arv->region()->get_state();
4143                 arv->audio_region()->normalize_to (0.0f);
4144                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4145         }
4146
4147         commit_reversible_command ();
4148         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4149 }
4150
4151
4152 void
4153 Editor::denormalize_region ()
4154 {
4155         if (!session) {
4156                 return;
4157         }
4158
4159         RegionSelection rs; 
4160
4161         get_regions_for_action (rs);
4162
4163         if (rs.empty()) {
4164                 return;
4165         }
4166
4167         begin_reversible_command ("denormalize");
4168
4169         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4170                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4171                 if (!arv)
4172                         continue;
4173                 XMLNode &before = arv->region()->get_state();
4174                 arv->audio_region()->set_scale_amplitude (1.0f);
4175                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4176         }
4177
4178         commit_reversible_command ();
4179 }
4180
4181 void
4182 Editor::adjust_region_scale_amplitude (bool up)
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                 
4204                 double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
4205                 
4206                 if (up) {
4207                         fraction += 0.05;
4208                         fraction = min (fraction, 1.0);
4209                 } else {
4210                         fraction -= 0.05;
4211                         fraction = max (fraction, 0.0);
4212                 }
4213
4214                 if (!up && fraction <= 0) {
4215                         continue;
4216                 }
4217
4218                 fraction = slider_position_to_gain (fraction);
4219                 fraction = coefficient_to_dB (fraction);
4220                 fraction = dB_to_coefficient (fraction);
4221
4222                 if (up && fraction >= 2.0) {
4223                         continue;
4224                 }
4225                 
4226                 arv->audio_region()->set_scale_amplitude (fraction);
4227                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4228         }
4229
4230         commit_reversible_command ();
4231 }
4232
4233
4234 void
4235 Editor::reverse_region ()
4236 {
4237         if (!session) {
4238                 return;
4239         }
4240
4241         Reverse rev (*session);
4242         apply_filter (rev, _("reverse regions"));
4243 }
4244
4245 void
4246 Editor::apply_filter (AudioFilter& filter, string command)
4247 {
4248         RegionSelection rs; 
4249
4250         get_regions_for_action (rs);
4251
4252         if (rs.empty()) {
4253                 return;
4254         }
4255
4256         begin_reversible_command (command);
4257
4258         track_canvas.get_window()->set_cursor (*wait_cursor);
4259         gdk_flush ();
4260
4261         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4262                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4263                 if (!arv)
4264                         continue;
4265
4266                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4267
4268                 RegionSelection::iterator tmp;
4269                 
4270                 tmp = r;
4271                 ++tmp;
4272
4273                 if (arv->audio_region()->apply (filter) == 0) {
4274
4275                         XMLNode &before = playlist->get_state();
4276                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4277                         XMLNode &after = playlist->get_state();
4278                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4279                 } else {
4280                         goto out;
4281                 }
4282
4283                 r = tmp;
4284         }
4285
4286         commit_reversible_command ();
4287         rs.clear ();
4288
4289   out:
4290         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
4291 }
4292
4293 void
4294 Editor::region_selection_op (void (Region::*pmf)(void))
4295 {
4296         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4297                 Region* region = (*i)->region().get();
4298                 (region->*pmf)();
4299         }
4300 }
4301
4302
4303 void
4304 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4305 {
4306         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4307                 Region* region = (*i)->region().get();
4308                 (region->*pmf)(arg);
4309         }
4310 }
4311
4312 void
4313 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4314 {
4315         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4316                 Region* region = (*i)->region().get();
4317                 (region->*pmf)(yn);
4318         }
4319 }
4320
4321 void
4322 Editor::external_edit_region ()
4323 {
4324         if (!clicked_regionview) {
4325                 return;
4326         }
4327
4328         /* more to come */
4329 }
4330
4331 void
4332 Editor::brush (nframes_t pos)
4333 {
4334         RegionSelection sel;
4335         RegionSelection rs; 
4336
4337         get_regions_for_action (rs);
4338
4339         snap_to (pos);
4340
4341         if (rs.empty()) {
4342                 return;
4343         }
4344
4345         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4346                 mouse_brush_insert_region ((*i), pos);
4347         }
4348 }
4349
4350 void
4351 Editor::reset_region_gain_envelopes ()
4352 {
4353         RegionSelection rs; 
4354
4355         get_regions_for_action (rs);
4356
4357         if (!session || rs.empty()) {
4358                 return;
4359         }
4360
4361         session->begin_reversible_command (_("reset region gain"));
4362
4363         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4364                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4365                 if (arv) {
4366                         AutomationList& alist (arv->audio_region()->envelope());
4367                         XMLNode& before (alist.get_state());
4368
4369                         arv->audio_region()->set_default_envelope ();
4370                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
4371                 }
4372         }
4373
4374         session->commit_reversible_command ();
4375 }
4376
4377 void
4378 Editor::toggle_gain_envelope_visibility ()
4379 {
4380         RegionSelection rs; 
4381
4382         get_regions_for_action (rs);
4383
4384         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4385                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4386                 if (arv) {
4387                         arv->set_envelope_visible (!arv->envelope_visible());
4388                 }
4389         }
4390 }
4391
4392 void
4393 Editor::toggle_gain_envelope_active ()
4394 {
4395         RegionSelection rs; 
4396
4397         get_regions_for_action (rs);
4398
4399         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4400                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4401                 if (arv) {
4402                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4403                 }
4404         }
4405 }
4406
4407 void
4408 Editor::toggle_region_lock ()
4409 {
4410         RegionSelection rs; 
4411
4412         get_regions_for_action (rs);
4413
4414         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4415                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4416                 if (arv) {
4417                         arv->audio_region()->set_locked (!arv->audio_region()->locked());
4418                 }
4419         }
4420 }
4421
4422 void
4423 Editor::set_region_lock_style (Region::PositionLockStyle ps)
4424 {
4425         RegionSelection rs; 
4426
4427         get_regions_for_action (rs);
4428
4429         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4430                 (*i)->region()->set_position_lock_style (ps);
4431         }
4432 }
4433
4434
4435 void
4436 Editor::toggle_region_mute ()
4437 {
4438         RegionSelection rs; 
4439
4440         get_regions_for_action (rs);
4441
4442         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4443                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4444                 if (arv) {
4445                         arv->audio_region()->set_muted (!arv->audio_region()->muted());
4446                 }
4447         }
4448 }
4449
4450 void
4451 Editor::toggle_region_opaque ()
4452 {
4453         RegionSelection rs; 
4454
4455         get_regions_for_action (rs);
4456
4457         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4458                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4459                 if (arv) {
4460                         arv->audio_region()->set_opaque (!arv->audio_region()->opaque());
4461                 }
4462         }
4463 }
4464
4465 void
4466 Editor::set_fade_length (bool in)
4467 {
4468         RegionSelection rs; 
4469
4470         get_regions_for_action (rs);
4471
4472         /* we need a region to measure the offset from the start */
4473
4474         RegionView* rv;
4475
4476         if (!rs.empty()) {
4477                 rv = rs.front();
4478         } else if (entered_regionview) {
4479                 rv = entered_regionview;
4480         } else {
4481                 return;
4482         }
4483
4484         nframes64_t pos = get_preferred_edit_position();
4485         nframes_t len;
4486         char* cmd;
4487         
4488         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4489                 /* edit point is outside the relevant region */
4490                 return;
4491         }
4492
4493         if (in) {
4494                 if (pos <= rv->region()->position()) {
4495                         /* can't do it */
4496                         return;
4497                 }
4498                 len = pos - rv->region()->position();
4499                 cmd = _("set fade in length");
4500         } else {
4501                 if (pos >= rv->region()->last_frame()) {
4502                         /* can't do it */
4503                         return;
4504                 }
4505                 len = rv->region()->last_frame() - pos;
4506                 cmd = _("set fade out length");
4507         }
4508
4509         begin_reversible_command (cmd);
4510
4511         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4512                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4513
4514                 if (!tmp) {
4515                         return;
4516                 }
4517
4518                 AutomationList* alist;
4519                 if (in) {
4520                         alist = &tmp->audio_region()->fade_in();
4521                 } else {
4522                         alist = &tmp->audio_region()->fade_out();
4523                 }
4524
4525                 XMLNode &before = alist->get_state();
4526
4527                 if (in) {
4528                         tmp->audio_region()->set_fade_in_length (len);
4529                         tmp->audio_region()->set_fade_in_active (true);
4530                 } else {
4531                         tmp->audio_region()->set_fade_out_length (len);
4532                         tmp->audio_region()->set_fade_out_active (true);
4533                 }
4534                 
4535                 XMLNode &after = alist->get_state();
4536                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4537         }
4538
4539         commit_reversible_command ();
4540 }
4541
4542
4543 void
4544 Editor::toggle_fade_active (bool in)
4545 {
4546         RegionSelection rs; 
4547
4548         get_regions_for_action (rs);
4549
4550         if (rs.empty()) {
4551                 return;
4552         }
4553
4554         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4555         bool have_switch = false;
4556         bool yn;
4557
4558         begin_reversible_command (cmd);
4559
4560         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4561                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4562                 
4563                 if (!tmp) {
4564                         return;
4565                 }
4566
4567                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4568
4569                 /* make the behaviour consistent across all regions */
4570                 
4571                 if (!have_switch) {
4572                         if (in) {
4573                                 yn = region->fade_in_active();
4574                         } else {
4575                                 yn = region->fade_out_active();
4576                         }
4577                         have_switch = true;
4578                 }
4579
4580                 XMLNode &before = region->get_state();
4581                 if (in) {
4582                         region->set_fade_in_active (!yn);
4583                 } else {
4584                         region->set_fade_out_active (!yn);
4585                 }
4586                 XMLNode &after = region->get_state();
4587                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4588         }
4589
4590         commit_reversible_command ();
4591 }
4592
4593 void
4594 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4595 {
4596         RegionSelection rs; 
4597
4598         get_regions_for_action (rs);
4599
4600         if (rs.empty()) {
4601                 return;
4602         }
4603
4604         begin_reversible_command (_("set fade in shape"));
4605
4606         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4607                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4608
4609                 if (!tmp) {
4610                         return;
4611                 }
4612
4613                 AutomationList& alist = tmp->audio_region()->fade_in();
4614                 XMLNode &before = alist.get_state();
4615
4616                 tmp->audio_region()->set_fade_in_shape (shape);
4617                 
4618                 XMLNode &after = alist.get_state();
4619                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4620         }
4621
4622         commit_reversible_command ();
4623                 
4624 }
4625
4626 void
4627 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4628 {
4629         RegionSelection rs; 
4630
4631         get_regions_for_action (rs);
4632
4633         if (rs.empty()) {
4634                 return;
4635         }
4636
4637         begin_reversible_command (_("set fade out shape"));
4638
4639         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4640                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4641
4642                 if (!tmp) {
4643                         return;
4644                 }
4645
4646                 AutomationList& alist = tmp->audio_region()->fade_out();
4647                 XMLNode &before = alist.get_state();
4648
4649                 tmp->audio_region()->set_fade_out_shape (shape);
4650                 
4651                 XMLNode &after = alist.get_state();
4652                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4653         }
4654
4655         commit_reversible_command ();
4656 }
4657
4658 void
4659 Editor::set_fade_in_active (bool yn)
4660 {
4661         RegionSelection rs; 
4662
4663         get_regions_for_action (rs);
4664
4665         if (rs.empty()) {
4666                 return;
4667         }
4668
4669         begin_reversible_command (_("set fade in active"));
4670
4671         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4672                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4673
4674                 if (!tmp) {
4675                         return;
4676                 }
4677
4678
4679                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4680
4681                 XMLNode &before = ar->get_state();
4682
4683                 ar->set_fade_in_active (yn);
4684                 
4685                 XMLNode &after = ar->get_state();
4686                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4687         }
4688
4689         commit_reversible_command ();
4690 }
4691
4692 void
4693 Editor::set_fade_out_active (bool yn)
4694 {
4695         RegionSelection rs; 
4696
4697         get_regions_for_action (rs);
4698
4699         if (rs.empty()) {
4700                 return;
4701         }
4702
4703         begin_reversible_command (_("set fade out active"));
4704
4705         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4706                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4707
4708                 if (!tmp) {
4709                         return;
4710                 }
4711
4712                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4713
4714                 XMLNode &before = ar->get_state();
4715
4716                 ar->set_fade_out_active (yn);
4717                 
4718                 XMLNode &after = ar->get_state();
4719                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4720         }
4721
4722         commit_reversible_command ();
4723 }
4724
4725
4726 /** Update crossfade visibility after its configuration has been changed */
4727 void
4728 Editor::update_xfade_visibility ()
4729 {
4730         _xfade_visibility = Config->get_xfades_visible ();
4731         
4732         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4733                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4734                 if (v) {
4735                         if (_xfade_visibility) {
4736                                 v->show_all_xfades ();
4737                         } else {
4738                                 v->hide_all_xfades ();
4739                         }
4740                 }
4741         }
4742 }
4743
4744 void
4745 Editor::set_edit_point ()
4746 {
4747         nframes64_t where;
4748         bool ignored;
4749
4750         if (!mouse_frame (where, ignored)) {
4751                 return;
4752         }
4753         
4754         snap_to (where);
4755
4756         if (selection->markers.empty()) {
4757                 
4758                 mouse_add_new_marker (where);
4759
4760         } else {
4761                 bool ignored;
4762
4763                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4764
4765                 if (loc) {
4766                         loc->move_to (where);
4767                 }
4768         }
4769 }
4770
4771 void
4772 Editor::set_playhead_cursor ()
4773 {
4774         if (entered_marker) {
4775                 session->request_locate (entered_marker->position(), session->transport_rolling());
4776         } else {
4777                 nframes64_t where;
4778                 bool ignored;
4779
4780                 if (!mouse_frame (where, ignored)) {
4781                         return;
4782                 }
4783                         
4784                 snap_to (where);
4785                 
4786                 if (session) {
4787                         session->request_locate (where, session->transport_rolling());
4788                 }
4789         }
4790 }
4791
4792 void
4793 Editor::split ()
4794 {
4795         RegionSelection rs; 
4796
4797         get_regions_for_action (rs);
4798
4799         nframes64_t where = get_preferred_edit_position();
4800
4801         if (rs.empty()) {
4802                 return;
4803         }
4804
4805         split_regions_at (where, rs);
4806 }
4807
4808 void
4809 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
4810 {
4811         if (entered_track && mouse_mode == MouseObject) {
4812                 if (!selection->tracks.empty()) {
4813                         if (!selection->selected (entered_track)) {
4814                                 selection->add (entered_track);
4815                         }
4816                 } else {
4817                         /* there is no selection, but this operation requires/prefers selected objects */
4818
4819                         if (op_really_wants_one_track_if_none_are_selected) {
4820                                 selection->set (entered_track);
4821                         }
4822                 }
4823         }
4824 }
4825
4826 void
4827 Editor::trim_region_front ()
4828 {
4829         trim_region (true);
4830 }
4831
4832 void
4833 Editor::trim_region_back ()
4834 {
4835         trim_region (false);
4836 }
4837
4838 void
4839 Editor::trim_region (bool front)
4840 {
4841         nframes64_t where = get_preferred_edit_position();
4842         RegionSelection rs;
4843
4844         get_regions_for_action (rs);
4845
4846         if (rs.empty()) {
4847                 return;
4848         }
4849
4850         begin_reversible_command (front ? _("trim front") : _("trim back"));
4851
4852         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
4853                 if (!(*i)->region()->locked()) {
4854                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
4855                         XMLNode &before = pl->get_state();
4856                         if (front) {
4857                                 (*i)->region()->trim_front (where, this);       
4858                         } else {
4859                                 (*i)->region()->trim_end (where, this); 
4860                         }
4861                         XMLNode &after = pl->get_state();
4862                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
4863                 }
4864         }
4865
4866         commit_reversible_command ();
4867 }
4868
4869 struct EditorOrderRouteSorter {
4870     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
4871             /* use of ">" forces the correct sort order */
4872             return a->order_key ("editor") < b->order_key ("editor");
4873     }
4874 };
4875
4876 void
4877 Editor::select_next_route()
4878 {
4879         if (selection->tracks.empty()) {
4880                 selection->set (track_views.front());
4881                 return;
4882         }
4883
4884         TimeAxisView* current = selection->tracks.front();
4885
4886         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4887                 if (*i == current) {
4888                         ++i;
4889                         if (i != track_views.end()) {
4890                                 selection->set (*i);
4891                         } else {
4892                                 selection->set (*(track_views.begin()));
4893                         }
4894                         break;
4895                 }
4896         }
4897 }
4898
4899 void
4900 Editor::select_prev_route()
4901 {
4902         if (selection->tracks.empty()) {
4903                 selection->set (track_views.front());
4904                 return;
4905         }
4906
4907         TimeAxisView* current = selection->tracks.front();
4908
4909         for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
4910                 if (*i == current) {
4911                         ++i;
4912                         if (i != track_views.rend()) {
4913                                 selection->set (*i);
4914                         } else {
4915                                 selection->set (*(track_views.rbegin()));
4916                         }
4917                         break;
4918                 }
4919         }
4920 }
4921
4922 void
4923 Editor::set_loop_from_selection (bool play)
4924 {
4925         if (session == 0 || selection->time.empty()) {
4926                 return;
4927         }
4928
4929         nframes_t start = selection->time[clicked_selection].start;
4930         nframes_t end = selection->time[clicked_selection].end;
4931         
4932         set_loop_range (start, end,  _("set loop range from selection"));
4933
4934         if (play) {
4935                 session->request_play_loop (true);
4936                 session->request_locate (start, true);
4937         }
4938 }
4939
4940 void
4941 Editor::set_loop_from_edit_range (bool play)
4942 {
4943         if (session == 0) {
4944                 return;
4945         }
4946
4947         nframes64_t start;
4948         nframes64_t end;
4949         
4950         if (!get_edit_op_range (start, end)) {
4951                 return;
4952         }
4953
4954         set_loop_range (start, end,  _("set loop range from edit range"));
4955
4956         if (play) {
4957                 session->request_play_loop (true);
4958                 session->request_locate (start, true);
4959         }
4960 }
4961
4962 void
4963 Editor::set_loop_from_region (bool play)
4964 {
4965         nframes64_t start = max_frames;
4966         nframes64_t end = 0;
4967
4968         RegionSelection rs; 
4969
4970         get_regions_for_action (rs);
4971
4972         if (rs.empty()) {
4973                 return;
4974         }
4975
4976         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4977                 if ((*i)->region()->position() < start) {
4978                         start = (*i)->region()->position();
4979                 }
4980                 if ((*i)->region()->last_frame() + 1 > end) {
4981                         end = (*i)->region()->last_frame() + 1;
4982                 }
4983         }
4984
4985         set_loop_range (start, end, _("set loop range from region"));
4986
4987         if (play) {
4988                 session->request_play_loop (true);
4989                 session->request_locate (start, true);
4990         }
4991 }
4992
4993 void
4994 Editor::set_punch_from_selection ()
4995 {
4996         if (session == 0 || selection->time.empty()) {
4997                 return;
4998         }
4999
5000         nframes_t start = selection->time[clicked_selection].start;
5001         nframes_t end = selection->time[clicked_selection].end;
5002         
5003         set_punch_range (start, end,  _("set punch range from selection"));
5004 }
5005
5006 void
5007 Editor::set_punch_from_edit_range ()
5008 {
5009         if (session == 0) {
5010                 return;
5011         }
5012
5013         nframes64_t start;
5014         nframes64_t end;
5015         
5016         if (!get_edit_op_range (start, end)) {
5017                 return;
5018         }
5019
5020         set_punch_range (start, end,  _("set punch range from edit range"));
5021 }
5022
5023 void
5024 Editor::set_punch_from_region ()
5025 {
5026         nframes64_t start = max_frames;
5027         nframes64_t end = 0;
5028
5029         RegionSelection rs; 
5030
5031         get_regions_for_action (rs);
5032
5033         if (rs.empty()) {
5034                 return;
5035         }
5036
5037         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5038                 if ((*i)->region()->position() < start) {
5039                         start = (*i)->region()->position();
5040                 }
5041                 if ((*i)->region()->last_frame() + 1 > end) {
5042                         end = (*i)->region()->last_frame() + 1;
5043                 }
5044         }
5045
5046         set_punch_range (start, end, _("set punch range from region"));
5047 }
5048
5049 void
5050 Editor::pitch_shift_regions ()
5051 {
5052         RegionSelection rs; 
5053
5054         get_regions_for_action (rs);
5055         
5056         if (rs.empty()) {
5057                 return;
5058         }
5059
5060         pitch_shift (rs, 1.2);
5061 }
5062         
5063 void
5064 Editor::use_region_as_bar ()
5065 {
5066         if (!session) {
5067                 return;
5068         }
5069
5070         RegionSelection rs; 
5071
5072         get_regions_for_action (rs);
5073
5074         if (rs.empty()) {
5075                 return;
5076         }
5077
5078         RegionView* rv = rs.front();
5079
5080         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5081 }
5082
5083 void
5084 Editor::use_range_as_bar ()
5085 {
5086         nframes64_t start, end;
5087         if (get_edit_op_range (start, end)) {
5088                 define_one_bar (start, end);
5089         }
5090 }
5091
5092 void
5093 Editor::define_one_bar (nframes64_t start, nframes64_t end)
5094 {
5095         nframes64_t length = end - start;
5096         
5097         const Meter& m (session->tempo_map().meter_at (start));
5098
5099         /* length = 1 bar */
5100
5101         /* now we want frames per beat.
5102            we have frames per bar, and beats per bar, so ...
5103         */
5104
5105         double frames_per_beat = length / m.beats_per_bar();
5106         
5107         /* beats per minute = */
5108
5109         double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
5110
5111         /* now decide whether to:
5112
5113             (a) set global tempo 
5114             (b) add a new tempo marker
5115
5116         */
5117
5118         const TempoSection& t (session->tempo_map().tempo_section_at (start));
5119
5120         bool do_global = false;
5121
5122         if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) {
5123                 
5124                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5125                    at the start, or create a new marker
5126                 */
5127
5128                 vector<string> options;
5129                 options.push_back (_("Cancel"));
5130                 options.push_back (_("Add new marker"));
5131                 options.push_back (_("Set global tempo"));
5132                 Choice c (_("Do you want to set the global tempo or add new tempo marker?"),
5133                           options);
5134                 c.set_default_response (2);
5135
5136                 switch (c.run()) {
5137                 case 0:
5138                         return;
5139
5140                 case 2:
5141                         do_global = true;
5142                         break;
5143
5144                 default:
5145                         do_global = false;
5146                 }
5147
5148         } else {
5149
5150                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5151                    if the marker is at the region starter, change it, otherwise add
5152                    a new tempo marker 
5153                 */
5154         }
5155
5156         begin_reversible_command (_("set tempo from region"));
5157         XMLNode& before (session->tempo_map().get_state());
5158
5159         if (do_global) {
5160                 session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5161         } else if (t.frame() == start) {
5162                 session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5163         } else {
5164                 session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5165         }
5166
5167         XMLNode& after (session->tempo_map().get_state());
5168
5169         session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
5170         commit_reversible_command ();
5171 }
5172
5173 void
5174 Editor::split_region_at_transients ()
5175 {
5176         AnalysisFeatureList positions;
5177
5178         if (!session) {
5179                 return;
5180         }
5181
5182         RegionSelection rs; 
5183
5184         get_regions_for_action (rs);
5185
5186         if (rs.empty()) {
5187                 return;
5188         }
5189
5190         session->begin_reversible_command (_("split regions"));
5191
5192         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5193
5194                 RegionSelection::iterator tmp;
5195
5196                 tmp = i;
5197                 ++tmp;
5198
5199                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5200                 
5201                 if (ar && (ar->get_transients (positions) == 0)) {
5202                         split_region_at_points ((*i)->region(), positions);
5203                         positions.clear ();
5204                 }
5205                 
5206                 i = tmp;
5207         }
5208
5209         session->commit_reversible_command ();
5210
5211 }
5212
5213 void
5214 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions)
5215 {
5216         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
5217         
5218         if (!ar) {
5219                 return;
5220         }
5221         
5222         boost::shared_ptr<Playlist> pl = ar->playlist();
5223         
5224         if (!pl) {
5225                 return;
5226         }
5227         
5228         if (positions.empty()) {
5229                 return;
5230         }
5231         
5232         AnalysisFeatureList::const_iterator x;  
5233         
5234         nframes64_t pos = ar->position();
5235
5236         XMLNode& before (pl->get_state());
5237         
5238         x = positions.begin();
5239         
5240         while (x != positions.end()) {
5241                 if ((*x) > pos) {
5242                         break;
5243                 }
5244                 ++x;
5245         }
5246         
5247         if (x == positions.end()) {
5248                 return;
5249         }
5250         
5251         pl->freeze ();
5252         pl->remove_region (ar);
5253         
5254         while (x != positions.end()) {
5255                 
5256                 /* file start = original start + how far we from the initial position ? 
5257                  */
5258                 
5259                 nframes64_t file_start = ar->start() + (pos - ar->position());
5260                 
5261                 /* length = next position - current position
5262                  */
5263                 
5264                 nframes64_t len = (*x) - pos;
5265
5266                 /* XXX we do we really want to allow even single-sample regions?
5267                    shouldn't we have some kind of lower limit on region size?
5268                 */
5269
5270                 if (len <= 0) {
5271                         break;
5272                 }
5273                 
5274                 string new_name;
5275                 
5276                 if (session->region_name (new_name, ar->name())) {
5277                         continue;
5278                 }
5279                 
5280                 pl->add_region (RegionFactory::create (ar->get_sources(), file_start, len, new_name), pos);
5281                 
5282                 pos += len;
5283                 ++x;
5284
5285                 if (*x > ar->last_frame()) {
5286                         break;
5287                 }
5288         } 
5289
5290         pl->thaw ();
5291         
5292         XMLNode& after (pl->get_state());
5293         
5294         session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
5295 }
5296
5297 void
5298 Editor::tab_to_transient (bool forward)
5299 {
5300         AnalysisFeatureList positions;
5301
5302         if (!session) {
5303                 return;
5304         }
5305
5306         nframes64_t pos = session->audible_frame ();
5307
5308         if (!selection->tracks.empty()) {
5309
5310                 for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
5311
5312                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
5313
5314                         if (rtv) {
5315                                 boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
5316                                 if (ds) {
5317                                         boost::shared_ptr<Playlist> pl = rtv->get_diskstream()->playlist ();
5318                                         if (pl) {
5319                                                 nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1);
5320                                                 
5321                                                 if (result >= 0) {
5322                                                         positions.push_back (result);
5323                                                 }
5324                                         }
5325                                 }
5326                         }
5327                 }
5328
5329         } else {
5330                 
5331                 RegionSelection rs; 
5332
5333                 get_regions_for_action (rs);
5334         
5335                 if (rs.empty()) {
5336                         return;
5337                 }
5338                 
5339                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5340                         (*r)->region()->get_transients (positions);
5341                 }
5342         }
5343
5344         TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0);
5345
5346         if (forward) {
5347                 AnalysisFeatureList::iterator x;
5348
5349                 for (x = positions.begin(); x != positions.end(); ++x) {
5350                         if ((*x) > pos) {
5351                                 break;
5352                         }
5353                 }
5354
5355                 if (x != positions.end ()) {
5356                         session->request_locate (*x);
5357                 }
5358
5359         } else {
5360                 AnalysisFeatureList::reverse_iterator x;
5361
5362                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
5363                         if ((*x) < pos) {
5364                                 break;
5365                         }
5366                 }
5367
5368                 if (x != positions.rend ()) {
5369                         session->request_locate (*x);
5370                 }
5371         }
5372 }