add trim to loop/punch; fixup trim start/end to EP to work with new paradigm
[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
32 #include <gtkmm2ext/utils.h>
33 #include <gtkmm2ext/choice.h>
34 #include <gtkmm2ext/window_title.h>
35
36 #include <ardour/audioengine.h>
37 #include <ardour/session.h>
38 #include <ardour/audioplaylist.h>
39 #include <ardour/audioregion.h>
40 #include <ardour/audio_diskstream.h>
41 #include <ardour/utils.h>
42 #include <ardour/location.h>
43 #include <ardour/named_selection.h>
44 #include <ardour/audio_track.h>
45 #include <ardour/audioplaylist.h>
46 #include <ardour/region_factory.h>
47 #include <ardour/playlist_factory.h>
48 #include <ardour/reverse.h>
49
50 #include "ardour_ui.h"
51 #include "editor.h"
52 #include "time_axis_view.h"
53 #include "audio_time_axis.h"
54 #include "automation_time_axis.h"
55 #include "streamview.h"
56 #include "audio_region_view.h"
57 #include "rgb_macros.h"
58 #include "selection_templates.h"
59 #include "selection.h"
60 #include "editing.h"
61 #include "gtk-custom-hruler.h"
62 #include "gui_thread.h"
63
64 #include "i18n.h"
65
66 using namespace std;
67 using namespace ARDOUR;
68 using namespace PBD;
69 using namespace sigc;
70 using namespace Gtk;
71 using namespace Gtkmm2ext;
72 using namespace Editing;
73
74 /***********************************************************************
75   Editor operations
76  ***********************************************************************/
77
78 void
79 Editor::undo (uint32_t n)
80 {
81         if (session) {
82                 session->undo (n);
83         }
84 }
85
86 void
87 Editor::redo (uint32_t n)
88 {
89         if (session) {
90                 session->redo (n);
91         }
92 }
93
94 void
95 Editor::split_region ()
96 {
97         split_region_at (get_preferred_edit_position());
98 }
99
100 void
101 Editor::split_region_at (nframes_t where)
102 {
103         split_regions_at (where, selection->regions);
104 }
105
106 void
107 Editor::split_regions_at (nframes_t where, RegionSelection& regions)
108 {
109         begin_reversible_command (_("split"));
110
111         // if splitting a single region, and snap-to is using
112         // region boundaries, don't pay attention to them
113
114         if (regions.size() == 1) {
115                 switch (snap_type) {
116                 case SnapToRegionStart:
117                 case SnapToRegionSync:
118                 case SnapToRegionEnd:
119                         break;
120                 default:
121                         snap_to (where);
122                 }
123         } else {
124                 snap_to (where);
125         }
126                 
127
128         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
129
130                 RegionSelection::iterator tmp;
131
132                 /* XXX this test needs to be more complicated, to make sure we really
133                    have something to split.
134                 */
135                 
136                 if (!(*a)->region()->covers (where)) {
137                         ++a;
138                         continue;
139                 }
140
141                 tmp = a;
142                 ++tmp;
143
144                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
145
146                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
147
148                 if (arv) {
149                         _new_regionviews_show_envelope = arv->envelope_visible();
150                 }
151                 
152                 if (pl) {
153                         XMLNode &before = pl->get_state();
154                         pl->split_region ((*a)->region(), where);
155                         XMLNode &after = pl->get_state();
156                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
157                 }
158
159                 a = tmp;
160         }
161         
162         commit_reversible_command ();
163         _new_regionviews_show_envelope = false;
164 }
165
166 void
167 Editor::remove_clicked_region ()
168 {
169         if (clicked_audio_trackview == 0 || clicked_regionview == 0) {
170                 return;
171         }
172
173         boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
174         
175         begin_reversible_command (_("remove region"));
176         XMLNode &before = playlist->get_state();
177         playlist->remove_region (clicked_regionview->region());
178         XMLNode &after = playlist->get_state();
179         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
180         commit_reversible_command ();
181 }
182
183 void
184 Editor::destroy_clicked_region ()
185 {
186         uint32_t selected = selection->regions.size();
187
188         if (!session || !selected) {
189                 return;
190         }
191
192         vector<string> choices;
193         string prompt;
194         
195         prompt  = string_compose (_(" This is destructive, will possibly delete audio files\n\
196 It cannot be undone\n\
197 Do you really want to destroy %1 ?"),
198                            (selected > 1 ? 
199                             _("these regions") : _("this region")));
200
201         choices.push_back (_("No, do nothing."));
202
203         if (selected > 1) {
204                 choices.push_back (_("Yes, destroy them."));
205         } else {
206                 choices.push_back (_("Yes, destroy it."));
207         }
208
209         Gtkmm2ext::Choice prompter (prompt, choices);
210         
211         if (prompter.run() == 0) { /* first choice */
212                 return;
213         }
214
215         if (selected) {
216                 list<boost::shared_ptr<Region> > r;
217
218                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
219                         r.push_back ((*i)->region());
220                 }
221
222                 session->destroy_regions (r);
223         } 
224 }
225
226 boost::shared_ptr<Region>
227 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
228 {
229         RegionView* rv;
230         boost::shared_ptr<Region> region;
231         nframes_t start = 0;
232
233         if (selection->time.start () == selection->time.end_frame ()) {
234                 
235                 /* no current selection-> is there a selected regionview? */
236
237                 if (selection->regions.empty()) {
238                         return region;
239                 }
240
241         } 
242
243         if (!selection->regions.empty()) {
244
245                 rv = *(selection->regions.begin());
246                 (*tv) = &rv->get_time_axis_view();
247                 region = rv->region();
248
249         } else if (!selection->tracks.empty()) {
250
251                 (*tv) = selection->tracks.front();
252
253                 RouteTimeAxisView* rtv;
254
255                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
256                         boost::shared_ptr<Playlist> pl;
257                         
258                         if ((pl = rtv->playlist()) == 0) {
259                                 return region;
260                         }
261                         
262                         region = pl->top_region_at (start);
263                 }
264         } 
265         
266         return region;
267 }
268         
269 void
270 Editor::extend_selection_to_end_of_region (bool next)
271 {
272         TimeAxisView *tv;
273         boost::shared_ptr<Region> region;
274         nframes_t start;
275
276         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
277                 return;
278         }
279
280         if (region && selection->time.start () == selection->time.end_frame ()) {
281                 start = region->position();
282         } else {
283                 start = selection->time.start ();
284         }
285
286         /* Try to leave the selection with the same route if possible */
287
288         if ((tv = selection->time.track) == 0) {
289                 return;
290         }
291
292         begin_reversible_command (_("extend selection"));
293         selection->set (tv, start, region->position() + region->length());
294         commit_reversible_command ();
295 }
296
297 void
298 Editor::extend_selection_to_start_of_region (bool previous)
299 {
300         TimeAxisView *tv;
301         boost::shared_ptr<Region> region;
302         nframes_t end;
303
304         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
305                 return;
306         }
307
308         if (region && selection->time.start () == selection->time.end_frame ()) {
309                 end = region->position() + region->length();
310         } else {
311                 end = selection->time.end_frame ();
312         }
313
314         /* Try to leave the selection with the same route if possible */
315         
316         if ((tv = selection->time.track) == 0) {
317                 return;
318         }
319
320         begin_reversible_command (_("extend selection"));
321         selection->set (tv, region->position(), end);
322         commit_reversible_command ();
323 }
324
325
326 void
327 Editor::nudge_forward (bool next)
328 {
329         nframes_t distance;
330         nframes_t next_distance;
331
332         if (!session) return;
333         
334         if (!selection->regions.empty()) {
335
336                 begin_reversible_command (_("nudge forward"));
337
338                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
339                         boost::shared_ptr<Region> r ((*i)->region());
340                         
341                         distance = get_nudge_distance (r->position(), next_distance);
342
343                         if (next) {
344                                 distance = next_distance;
345                         }
346
347                         XMLNode &before = r->playlist()->get_state();
348                         r->set_position (r->position() + distance, this);
349                         XMLNode &after = r->playlist()->get_state();
350                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
351                 }
352
353                 commit_reversible_command ();
354
355         } else {
356                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
357                 session->request_locate (playhead_cursor->current_frame + distance);
358         }
359 }
360                 
361 void
362 Editor::nudge_backward (bool next)
363 {
364         nframes_t distance;
365         nframes_t next_distance;
366
367         if (!session) return;
368         
369         if (!selection->regions.empty()) {
370
371                 begin_reversible_command (_("nudge forward"));
372
373                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
374                         boost::shared_ptr<Region> r ((*i)->region());
375
376                         distance = get_nudge_distance (r->position(), next_distance);
377                         
378                         if (next) {
379                                 distance = next_distance;
380                         }
381
382                         XMLNode &before = r->playlist()->get_state();
383                         
384                         if (r->position() > distance) {
385                                 r->set_position (r->position() - distance, this);
386                         } else {
387                                 r->set_position (0, this);
388                         }
389                         XMLNode &after = r->playlist()->get_state();
390                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
391                 }
392
393                 commit_reversible_command ();
394
395         } else {
396
397                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
398
399                 if (playhead_cursor->current_frame > distance) {
400                         session->request_locate (playhead_cursor->current_frame - distance);
401                 } else {
402                         session->goto_start();
403                 }
404         }
405 }
406
407 void
408 Editor::nudge_forward_capture_offset ()
409 {
410         nframes_t distance;
411
412         if (!session) return;
413         
414         if (!selection->regions.empty()) {
415
416                 begin_reversible_command (_("nudge forward"));
417
418                 distance = session->worst_output_latency();
419
420                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
421                         boost::shared_ptr<Region> r ((*i)->region());
422                         
423                         XMLNode &before = r->playlist()->get_state();
424                         r->set_position (r->position() + distance, this);
425                         XMLNode &after = r->playlist()->get_state();
426                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
427                 }
428
429                 commit_reversible_command ();
430
431         } 
432 }
433                 
434 void
435 Editor::nudge_backward_capture_offset ()
436 {
437         nframes_t distance;
438
439         if (!session) return;
440         
441         if (!selection->regions.empty()) {
442
443                 begin_reversible_command (_("nudge forward"));
444
445                 distance = session->worst_output_latency();
446
447                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
448                         boost::shared_ptr<Region> r ((*i)->region());
449
450                         XMLNode &before = r->playlist()->get_state();
451                         
452                         if (r->position() > distance) {
453                                 r->set_position (r->position() - distance, this);
454                         } else {
455                                 r->set_position (0, this);
456                         }
457                         XMLNode &after = r->playlist()->get_state();
458                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
459                 }
460
461                 commit_reversible_command ();
462         }
463 }
464
465 /* DISPLAY MOTION */
466
467 void
468 Editor::move_to_start ()
469 {
470         session->goto_start ();
471 }
472
473 void
474 Editor::move_to_end ()
475 {
476
477         session->request_locate (session->current_end_frame());
478 }
479
480 void
481 Editor::build_region_boundary_cache ()
482 {
483         nframes_t pos = 0;
484         vector<RegionPoint> interesting_points;
485         boost::shared_ptr<Region> r;
486         TrackViewList tracks;
487         bool at_end = false;
488
489         region_boundary_cache.clear ();
490
491         if (session == 0) {
492                 return;
493         }
494         
495         switch (snap_type) {
496         case SnapToRegionStart:
497                 interesting_points.push_back (Start);
498                 break;
499         case SnapToRegionEnd:
500                 interesting_points.push_back (End);
501                 break;  
502         case SnapToRegionSync:
503                 interesting_points.push_back (SyncPoint);
504                 break;  
505         case SnapToRegionBoundary:
506                 interesting_points.push_back (Start);
507                 interesting_points.push_back (End);
508                 break;  
509         default:
510                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
511                 /*NOTREACHED*/
512                 return;
513         }
514         
515         TimeAxisView *ontrack = 0;
516         TrackViewList tlist;
517
518         if (!selection->tracks.empty()) {
519                 tlist = selection->tracks;
520         } else {
521                 tlist = track_views;
522         }
523
524         while (pos < session->current_end_frame() && !at_end) {
525
526                 nframes_t rpos;
527                 nframes_t lpos = max_frames;
528
529                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
530
531                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
532                                 if (*p == interesting_points.back()) {
533                                         at_end = true;
534                                 }
535                                 /* move to next point type */
536                                 continue;
537                         }
538
539                         switch (*p) {
540                         case Start:
541                                 rpos = r->first_frame();
542                                 break;
543                         case End:
544                                 rpos = r->last_frame();
545                                 break;  
546                         case SyncPoint:
547                                 rpos = r->adjust_to_sync (r->first_frame());
548                                 break;
549                         default:
550                                 break;
551                         }
552                         
553                         float speed = 1.0f;
554                         AudioTimeAxisView *atav;
555                         
556                         if (ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
557                                 if (atav->get_diskstream() != 0) {
558                                         speed = atav->get_diskstream()->speed();
559                                 }
560                         }
561                         
562                         rpos = track_frame_to_session_frame (rpos, speed);
563
564                         if (rpos < lpos) {
565                                 lpos = rpos;
566                         }
567
568                         /* prevent duplicates, but we don't use set<> because we want to be able
569                            to sort later.
570                         */
571
572                         vector<nframes_t>::iterator ri; 
573                         
574                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
575                                 if (*ri == rpos) {
576                                         break;
577                                 }
578                         }
579
580                         if (ri == region_boundary_cache.end()) {
581                                 region_boundary_cache.push_back (rpos);
582                         }
583                 }
584
585                 pos = lpos + 1;
586         }
587
588         /* finally sort to be sure that the order is correct */
589
590         sort (region_boundary_cache.begin(), region_boundary_cache.end());
591 }
592
593 boost::shared_ptr<Region>
594 Editor::find_next_region (nframes_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
595 {
596         TrackViewList::iterator i;
597         nframes_t closest = max_frames;
598         boost::shared_ptr<Region> ret;
599         nframes_t rpos = 0;
600
601         float track_speed;
602         nframes_t track_frame;
603         AudioTimeAxisView *atav;
604
605         for (i = tracks.begin(); i != tracks.end(); ++i) {
606
607                 nframes_t distance;
608                 boost::shared_ptr<Region> r;
609                 
610                 track_speed = 1.0f;
611                 if ( (atav = dynamic_cast<AudioTimeAxisView*>(*i)) != 0 ) {
612                         if (atav->get_diskstream()!=0)
613                                 track_speed = atav->get_diskstream()->speed();
614                 }
615
616                 track_frame = session_frame_to_track_frame(frame, track_speed);
617
618                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
619                         continue;
620                 }
621
622                 switch (point) {
623                 case Start:
624                         rpos = r->first_frame ();
625                         break;
626
627                 case End:
628                         rpos = r->last_frame ();
629                         break;
630
631                 case SyncPoint:
632                         rpos = r->adjust_to_sync (r->first_frame());
633                         break;
634                 }
635                 // rpos is a "track frame", converting it to "session frame"
636                 rpos = track_frame_to_session_frame(rpos, track_speed);
637
638                 if (rpos > frame) {
639                         distance = rpos - frame;
640                 } else {
641                         distance = frame - rpos;
642                 }
643
644                 if (distance < closest) {
645                         closest = distance;
646                         if (ontrack != 0)
647                                 *ontrack = (*i);
648                         ret = r;
649                 }
650         }
651
652         return ret;
653 }
654
655 void
656 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
657 {
658         boost::shared_ptr<Region> r;
659         nframes_t pos = cursor->current_frame;
660
661         if (!session) {
662                 return;
663         }
664
665         TimeAxisView *ontrack = 0;
666
667         // so we don't find the current region again..
668         if (dir>0 || pos>0)
669                 pos+=dir;
670
671         if (!selection->tracks.empty()) {
672                 
673                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
674                 
675         } else if (clicked_trackview) {
676                 
677                 TrackViewList t;
678                 t.push_back (clicked_trackview);
679                 
680                 r = find_next_region (pos, point, dir, t, &ontrack);
681                 
682         } else {
683                 
684                 r = find_next_region (pos, point, dir, track_views, &ontrack);
685         }
686
687         if (r == 0) {
688                 return;
689         }
690         
691         switch (point){
692         case Start:
693                 pos = r->first_frame ();
694                 break;
695
696         case End:
697                 pos = r->last_frame ();
698                 break;
699
700         case SyncPoint:
701                 pos = r->adjust_to_sync (r->first_frame());
702                 break;  
703         }
704         
705         float speed = 1.0f;
706         AudioTimeAxisView *atav;
707
708         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
709                 if (atav->get_diskstream() != 0) {
710                         speed = atav->get_diskstream()->speed();
711                 }
712         }
713
714         pos = track_frame_to_session_frame(pos, speed);
715         
716         if (cursor == playhead_cursor) {
717                 session->request_locate (pos);
718         } else {
719                 cursor->set_position (pos);
720         }
721 }
722
723 void
724 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
725 {
726         cursor_to_region_point (cursor, point, 1);
727 }
728
729 void
730 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
731 {
732         cursor_to_region_point (cursor, point, -1);
733 }
734
735 void
736 Editor::cursor_to_selection_start (Cursor *cursor)
737 {
738         nframes_t pos = 0;
739         switch (mouse_mode) {
740         case MouseObject:
741                 if (!selection->regions.empty()) {
742                         pos = selection->regions.start();
743                 }
744                 break;
745
746         case MouseRange:
747                 if (!selection->time.empty()) {
748                         pos = selection->time.start ();
749                 }
750                 break;
751
752         default:
753                 return;
754         }
755
756         if (cursor == playhead_cursor) {
757                 session->request_locate (pos);
758         } else {
759                 cursor->set_position (pos);
760         }
761 }
762
763 void
764 Editor::cursor_to_selection_end (Cursor *cursor)
765 {
766         nframes_t pos = 0;
767
768         switch (mouse_mode) {
769         case MouseObject:
770                 if (!selection->regions.empty()) {
771                         pos = selection->regions.end_frame();
772                 }
773                 break;
774
775         case MouseRange:
776                 if (!selection->time.empty()) {
777                         pos = selection->time.end_frame ();
778                 }
779                 break;
780
781         default:
782                 return;
783         }
784
785         if (cursor == playhead_cursor) {
786                 session->request_locate (pos);
787         } else {
788                 cursor->set_position (pos);
789         }
790 }
791
792 void
793 Editor::edit_point_to_region_point (RegionPoint point, int32_t dir)
794 {
795         boost::shared_ptr<Region> r;
796         nframes_t pos;
797         Location* loc;
798         bool ignored;
799
800         if (!session || _edit_point != EditAtSelectedMarker || selection->markers.empty()) {
801                 return;
802         }
803
804         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
805                 return;
806         }
807
808         TimeAxisView *ontrack = 0;
809
810         pos = loc->start();
811
812         // so we don't find the current region again..
813         if (dir>0 || pos>0)
814                 pos+=dir;
815
816         if (!selection->tracks.empty()) {
817                 
818                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
819                 
820         } else if (clicked_trackview) {
821                 
822                 TrackViewList t;
823                 t.push_back (clicked_trackview);
824                 
825                 r = find_next_region (pos, point, dir, t, &ontrack);
826                 
827         } else {
828                 
829                 r = find_next_region (pos, point, dir, track_views, &ontrack);
830         }
831
832         if (r == 0) {
833                 return;
834         }
835         
836         switch (point){
837         case Start:
838                 pos = r->first_frame ();
839                 break;
840
841         case End:
842                 pos = r->last_frame ();
843                 break;
844
845         case SyncPoint:
846                 pos = r->adjust_to_sync (r->first_frame());
847                 break;  
848         }
849         
850         float speed = 1.0f;
851         AudioTimeAxisView *atav;
852
853         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
854                 if (atav->get_diskstream() != 0) {
855                         speed = atav->get_diskstream()->speed();
856                 }
857         }
858
859         pos = track_frame_to_session_frame(pos, speed);
860
861         loc->move_to (pos);
862 }
863
864 void
865 Editor::edit_point_to_next_region_point (RegionPoint point)
866 {
867         edit_point_to_region_point (point, 1);
868 }
869
870 void
871 Editor::edit_point_to_previous_region_point (RegionPoint point)
872 {
873         edit_point_to_region_point (point, -1);
874 }
875
876 void
877 Editor::edit_point_to_selection_start ()
878 {
879         nframes_t pos = 0;
880         Location* loc;
881         bool ignored;
882
883         if (!session || _edit_point != EditAtSelectedMarker || selection->markers.empty()) {
884                 return;
885         }
886
887         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
888                 return;
889         }
890
891         switch (mouse_mode) {
892         case MouseObject:
893                 if (!selection->regions.empty()) {
894                         pos = selection->regions.start();
895                 }
896                 break;
897
898         case MouseRange:
899                 if (!selection->time.empty()) {
900                         pos = selection->time.start ();
901                 }
902                 break;
903
904         default:
905                 return;
906         }
907
908         loc->move_to (pos);
909 }
910
911 void
912 Editor::edit_point_to_selection_end ()
913 {
914         nframes_t pos = 0;
915         Location* loc;
916         bool ignored;
917
918         if (!session || _edit_point != EditAtSelectedMarker || selection->markers.empty()) {
919                 return;
920         }
921
922         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
923                 return;
924         }
925
926         switch (mouse_mode) {
927         case MouseObject:
928                 if (!selection->regions.empty()) {
929                         pos = selection->regions.end_frame();
930                 }
931                 break;
932
933         case MouseRange:
934                 if (!selection->time.empty()) {
935                         pos = selection->time.end_frame ();
936                 }
937                 break;
938
939         default:
940                 return;
941         }
942
943         loc->move_to (pos);
944 }
945
946 void
947 Editor::scroll_playhead (bool forward)
948 {
949         nframes_t pos = playhead_cursor->current_frame;
950         nframes_t delta = (nframes_t) floor (current_page_frames() / 0.8);
951
952         if (forward) {
953                 if (pos == max_frames) {
954                         return;
955                 }
956
957                 if (pos < max_frames - delta) {
958                         pos += delta ;
959                 } else {
960                         pos = max_frames;
961                 } 
962
963         } else {
964
965                 if (pos == 0) {
966                         return;
967                 } 
968
969                 if (pos > delta) {
970                         pos -= delta;
971                 } else {
972                         pos = 0;
973                 }
974         }
975
976         session->request_locate (pos);
977 }
978
979 void
980 Editor::playhead_backward ()
981 {
982         nframes_t pos;
983         nframes_t cnt;
984         float prefix;
985         bool was_floating;
986
987         if (get_prefix (prefix, was_floating)) {
988                 cnt = 1;
989         } else {
990                 if (was_floating) {
991                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
992                 } else {
993                         cnt = (nframes_t) prefix;
994                 }
995         }
996
997         pos = playhead_cursor->current_frame;
998
999         if ((nframes_t) pos < cnt) {
1000                 pos = 0;
1001         } else {
1002                 pos -= cnt;
1003         }
1004         
1005         /* XXX this is completely insane. with the current buffering
1006            design, we'll force a complete track buffer flush and
1007            reload, just to move 1 sample !!!
1008         */
1009
1010         session->request_locate (pos);
1011 }
1012
1013 void
1014 Editor::playhead_forward ()
1015 {
1016         nframes_t pos;
1017         nframes_t cnt;
1018         bool was_floating;
1019         float prefix;
1020
1021         if (get_prefix (prefix, was_floating)) {
1022                 cnt = 1;
1023         } else {
1024                 if (was_floating) {
1025                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1026                 } else {
1027                         cnt = (nframes_t) floor (prefix);
1028                 }
1029         }
1030
1031         pos = playhead_cursor->current_frame;
1032         
1033         /* XXX this is completely insane. with the current buffering
1034            design, we'll force a complete track buffer flush and
1035            reload, just to move 1 sample !!!
1036         */
1037
1038         session->request_locate (pos+cnt);
1039 }
1040
1041 void
1042 Editor::cursor_align (bool playhead_to_edit)
1043 {
1044         if (!session) {
1045                 return;
1046         }
1047
1048         if (playhead_to_edit) {
1049
1050                 if (selection->markers.empty()) {
1051                         return;
1052                 }
1053                 
1054                 session->request_locate (selection->markers.front()->position(), session->transport_rolling());
1055         
1056         } else {
1057
1058                 /* move selected markers to playhead */
1059                 
1060                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1061                         bool ignored;
1062                         
1063                         Location* loc = find_location_from_marker (*i, ignored);
1064                         
1065                         if (loc->is_mark()) {
1066                                 loc->set_start (playhead_cursor->current_frame);
1067                         } else {
1068                                 loc->set (playhead_cursor->current_frame,
1069                                           playhead_cursor->current_frame + loc->length());
1070                         }
1071                 }
1072         }
1073 }
1074
1075 void
1076 Editor::edit_cursor_backward ()
1077 {
1078         nframes64_t pos;
1079         nframes64_t cnt;
1080         float prefix;
1081         bool was_floating;
1082
1083         if (get_prefix (prefix, was_floating)) {
1084                 cnt = 1;
1085         } else {
1086                 if (was_floating) {
1087                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1088                 } else {
1089                         cnt = (nframes_t) prefix;
1090                 }
1091         }
1092
1093         if ((pos = get_preferred_edit_position()) < 0) {
1094                 return;
1095         }
1096
1097         if (pos < cnt) {
1098                 pos = 0;
1099         } else {
1100                 pos -= cnt;
1101         }
1102         
1103         // EDIT CURSOR edit_cursor->set_position (pos);
1104 }
1105
1106 void
1107 Editor::edit_cursor_forward ()
1108 {
1109         nframes_t pos;
1110         nframes_t cnt;
1111         bool was_floating;
1112         float prefix;
1113
1114         if (get_prefix (prefix, was_floating)) {
1115                 cnt = 1;
1116         } else {
1117                 if (was_floating) {
1118                         cnt = (nframes_t) floor (prefix * session->frame_rate ());
1119                 } else {
1120                         cnt = (nframes_t) floor (prefix);
1121                 }
1122         }
1123
1124         // pos = edit_cursor->current_frame;
1125         // EDIT CURSOR edit_cursor->set_position (pos+cnt);
1126 }
1127
1128 void
1129 Editor::goto_frame ()
1130 {
1131         float prefix;
1132         bool was_floating;
1133         nframes_t frame;
1134
1135         if (get_prefix (prefix, was_floating)) {
1136                 return;
1137         }
1138
1139         if (was_floating) {
1140                 frame = (nframes_t) floor (prefix * session->frame_rate());
1141         } else {
1142                 frame = (nframes_t) floor (prefix);
1143         }
1144
1145         session->request_locate (frame);
1146 }
1147
1148 void
1149 Editor::scroll_backward (float pages)
1150 {
1151         nframes_t frame;
1152         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1153         bool was_floating;
1154         float prefix;
1155         nframes_t cnt;
1156         
1157         if (get_prefix (prefix, was_floating)) {
1158                 cnt = (nframes_t) floor (pages * one_page);
1159         } else {
1160                 if (was_floating) {
1161                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1162                 } else {
1163                         cnt = (nframes_t) floor (prefix * one_page);
1164                 }
1165         }
1166
1167         if (leftmost_frame < cnt) {
1168                 frame = 0;
1169         } else {
1170                 frame = leftmost_frame - cnt;
1171         }
1172
1173         reset_x_origin (frame);
1174 }
1175
1176 void
1177 Editor::scroll_forward (float pages)
1178 {
1179         nframes_t frame;
1180         nframes_t one_page = (nframes_t) rint (canvas_width * frames_per_unit);
1181         bool was_floating;
1182         float prefix;
1183         nframes_t cnt;
1184         
1185         if (get_prefix (prefix, was_floating)) {
1186                 cnt = (nframes_t) floor (pages * one_page);
1187         } else {
1188                 if (was_floating) {
1189                         cnt = (nframes_t) floor (prefix * session->frame_rate());
1190                 } else {
1191                         cnt = (nframes_t) floor (prefix * one_page);
1192                 }
1193         }
1194
1195         if (max_frames - cnt < leftmost_frame) {
1196                 frame = max_frames - cnt;
1197         } else {
1198                 frame = leftmost_frame + cnt;
1199         }
1200
1201         reset_x_origin (frame);
1202 }
1203
1204 void
1205 Editor::scroll_tracks_down ()
1206 {
1207         float prefix;
1208         bool was_floating;
1209         int cnt;
1210
1211         if (get_prefix (prefix, was_floating)) {
1212                 cnt = 1;
1213         } else {
1214                 cnt = (int) floor (prefix);
1215         }
1216
1217         double vert_value = vertical_adjustment.get_value() + (cnt *
1218                 vertical_adjustment.get_page_size());
1219         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1220                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1221         }
1222         vertical_adjustment.set_value (vert_value);
1223 }
1224
1225 void
1226 Editor::scroll_tracks_up ()
1227 {
1228         float prefix;
1229         bool was_floating;
1230         int cnt;
1231
1232         if (get_prefix (prefix, was_floating)) {
1233                 cnt = 1;
1234         } else {
1235                 cnt = (int) floor (prefix);
1236         }
1237
1238         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1239 }
1240
1241 void
1242 Editor::scroll_tracks_down_line ()
1243 {
1244
1245         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1246         double vert_value = adj->get_value() + 20;
1247
1248         if (vert_value>adj->get_upper() - canvas_height) {
1249                 vert_value = adj->get_upper() - canvas_height;
1250         }
1251         adj->set_value (vert_value);
1252 }
1253
1254 void
1255 Editor::scroll_tracks_up_line ()
1256 {
1257         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1258         adj->set_value (adj->get_value() - 20);
1259 }
1260
1261 /* ZOOM */
1262
1263 void
1264 Editor::temporal_zoom_step (bool coarser)
1265 {
1266         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1267
1268         double nfpu;
1269
1270         nfpu = frames_per_unit;
1271         
1272         if (coarser) { 
1273                 nfpu *= 1.61803399;
1274         } else { 
1275                 nfpu = max(1.0,(nfpu/1.61803399));
1276         }
1277
1278         temporal_zoom (nfpu);
1279 }       
1280
1281 void
1282 Editor::temporal_zoom (gdouble fpu)
1283 {
1284         if (!session) return;
1285         
1286         nframes64_t current_page = current_page_frames();
1287         nframes64_t current_leftmost = leftmost_frame;
1288         nframes64_t current_rightmost;
1289         nframes64_t current_center;
1290         nframes64_t new_page;
1291         nframes64_t leftmost_after_zoom = 0;
1292         nframes64_t where;
1293         bool in_track_canvas;
1294         double nfpu;
1295
1296         nfpu = fpu;
1297         
1298         new_page = (nframes_t) floor (canvas_width * nfpu);
1299
1300         switch (zoom_focus) {
1301         case ZoomFocusLeft:
1302                 leftmost_after_zoom = current_leftmost;
1303                 break;
1304                 
1305         case ZoomFocusRight:
1306                 current_rightmost = leftmost_frame + current_page;
1307                 if (current_rightmost > new_page) {
1308                         leftmost_after_zoom = current_rightmost - new_page;
1309                 } else {
1310                         leftmost_after_zoom = 0;
1311                 }
1312                 break;
1313                 
1314         case ZoomFocusCenter:
1315                 current_center = current_leftmost + (current_page/2); 
1316                 if (current_center > (new_page/2)) {
1317                         leftmost_after_zoom = current_center - (new_page / 2);
1318                 } else {
1319                         leftmost_after_zoom = 0;
1320                 }
1321                 break;
1322                 
1323         case ZoomFocusPlayhead:
1324                 /* try to keep the playhead in the center */
1325                 if (playhead_cursor->current_frame > new_page/2) {
1326                         leftmost_after_zoom = playhead_cursor->current_frame - (new_page/2);
1327                 } else {
1328                         leftmost_after_zoom = 0;
1329                 }
1330                 break;
1331
1332         case ZoomFocusMouse:
1333                 /* try to keep the mouse over the same point in the display */
1334
1335                 if (!mouse_frame (where, in_track_canvas)) {
1336                         /* use playhead instead */
1337                         where = playhead_cursor->current_frame;
1338
1339                         if (where > new_page/2) {
1340                                 leftmost_after_zoom = where - (new_page/2);
1341                         } else {
1342                                 leftmost_after_zoom = 0;
1343                         }
1344
1345                 } else {
1346
1347                         double l = - ((new_page * ((where - current_leftmost)/(double)current_page)) - where);
1348
1349                         if (l < 0) {
1350                                 leftmost_after_zoom = 0;
1351                         } else if (l > max_frames) { 
1352                                 leftmost_after_zoom = max_frames - new_page;
1353                         } else {
1354                                 leftmost_after_zoom = (nframes64_t) l;
1355                         }
1356                 }
1357
1358                 break;
1359
1360         case ZoomFocusEdit:
1361                 /* try to keep the edit point in the center */
1362                 if (get_preferred_edit_position() > new_page/2) {
1363                         leftmost_after_zoom = get_preferred_edit_position() - (new_page/2);
1364                 } else {
1365                         leftmost_after_zoom = 0;
1366                 }
1367                 break;
1368                 
1369         }
1370  
1371         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1372
1373 //      begin_reversible_command (_("zoom"));
1374 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), current_leftmost, frames_per_unit));
1375 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_after_zoom, nfpu));
1376 //      commit_reversible_command ();
1377         
1378         // cerr << "repos & zoom to " << leftmost_after_zoom << " @ " << nfpu << endl;
1379
1380         reposition_and_zoom (leftmost_after_zoom, nfpu);
1381 }       
1382
1383 void
1384 Editor::temporal_zoom_selection ()
1385 {
1386         if (!selection) return;
1387         
1388         if (selection->time.empty()) {
1389                 return;
1390         }
1391
1392         nframes_t start = selection->time[clicked_selection].start;
1393         nframes_t end = selection->time[clicked_selection].end;
1394
1395         temporal_zoom_by_frame (start, end, "zoom to selection");
1396 }
1397
1398 void
1399 Editor::temporal_zoom_session ()
1400 {
1401         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session));
1402
1403         if (session) {
1404                 temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session");
1405         }
1406 }
1407
1408 void
1409 Editor::temporal_zoom_by_frame (nframes_t start, nframes_t end, const string & op)
1410 {
1411         if (!session) return;
1412
1413         if ((start == 0 && end == 0) || end < start) {
1414                 return;
1415         }
1416
1417         nframes_t range = end - start;
1418
1419         double new_fpu = (double)range / (double)canvas_width;
1420 //      double p2 = 1.0;
1421
1422 //      while (p2 < new_fpu) {
1423 //              p2 *= 2.0;
1424 //      }
1425 //      new_fpu = p2;
1426         
1427         nframes_t new_page = (nframes_t) floor (canvas_width * new_fpu);
1428         nframes_t middle = (nframes_t) floor( (double)start + ((double)range / 2.0f ));
1429         nframes_t new_leftmost = (nframes_t) floor( (double)middle - ((double)new_page/2.0f));
1430
1431         if (new_leftmost > middle) new_leftmost = 0;
1432
1433 //      begin_reversible_command (op);
1434 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1435 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1436 //      commit_reversible_command ();
1437
1438         reposition_and_zoom (new_leftmost, new_fpu);
1439 }
1440
1441 void 
1442 Editor::temporal_zoom_to_frame (bool coarser, nframes_t frame)
1443 {
1444         if (!session) return;
1445         
1446         double range_before = frame - leftmost_frame;
1447         double new_fpu;
1448         
1449         new_fpu = frames_per_unit;
1450         
1451         if (coarser) { 
1452                 new_fpu *= 1.61803399;
1453                 range_before *= 1.61803399;
1454         } else { 
1455                 new_fpu = max(1.0,(new_fpu/1.61803399));
1456                 range_before /= 1.61803399;
1457         }
1458
1459         if (new_fpu == frames_per_unit) return;
1460
1461         nframes_t new_leftmost = frame - (nframes_t)range_before;
1462
1463         if (new_leftmost > frame) new_leftmost = 0;
1464
1465 //      begin_reversible_command (_("zoom to frame"));
1466 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1467 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1468 //      commit_reversible_command ();
1469
1470         reposition_and_zoom (new_leftmost, new_fpu);
1471 }
1472
1473 void
1474 Editor::add_location_from_selection ()
1475 {
1476         string rangename;
1477
1478         if (selection->time.empty()) {
1479                 return;
1480         }
1481
1482         if (session == 0 || clicked_trackview == 0) {
1483                 return;
1484         }
1485
1486         nframes_t start = selection->time[clicked_selection].start;
1487         nframes_t end = selection->time[clicked_selection].end;
1488
1489         session->locations()->next_available_name(rangename,"selection");
1490         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1491
1492         session->begin_reversible_command (_("add marker"));
1493         XMLNode &before = session->locations()->get_state();
1494         session->locations()->add (location, true);
1495         XMLNode &after = session->locations()->get_state();
1496         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1497         session->commit_reversible_command ();
1498 }
1499
1500 void
1501 Editor::add_location_from_playhead_cursor ()
1502 {
1503         string markername;
1504
1505         nframes_t where = session->audible_frame();
1506         
1507         session->locations()->next_available_name(markername,"mark");
1508         Location *location = new Location (where, where, markername, Location::IsMark);
1509         session->begin_reversible_command (_("add marker"));
1510         XMLNode &before = session->locations()->get_state();
1511         session->locations()->add (location, true);
1512         XMLNode &after = session->locations()->get_state();
1513         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1514         session->commit_reversible_command ();
1515 }
1516
1517 void
1518 Editor::add_location_from_audio_region ()
1519 {
1520         if (selection->regions.empty()) {
1521                 return;
1522         }
1523
1524         RegionView* rv = *(selection->regions.begin());
1525         boost::shared_ptr<Region> region = rv->region();
1526         
1527         Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1528         session->begin_reversible_command (_("add marker"));
1529         XMLNode &before = session->locations()->get_state();
1530         session->locations()->add (location, true);
1531         XMLNode &after = session->locations()->get_state();
1532         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1533         session->commit_reversible_command ();
1534 }
1535
1536 void
1537 Editor::amplitude_zoom_step (bool in)
1538 {
1539         gdouble zoom = 1.0;
1540
1541         if (in) {
1542                 zoom *= 2.0;
1543         } else {
1544                 if (zoom > 2.0) {
1545                         zoom /= 2.0;
1546                 } else {
1547                         zoom = 1.0;
1548                 }
1549         }
1550
1551 #ifdef FIX_FOR_CANVAS
1552         /* XXX DO SOMETHING */
1553 #endif
1554 }       
1555
1556
1557 /* DELETION */
1558
1559
1560 void
1561 Editor::delete_sample_forward ()
1562 {
1563 }
1564
1565 void
1566 Editor::delete_sample_backward ()
1567 {
1568 }
1569
1570 void
1571 Editor::delete_screen ()
1572 {
1573 }
1574
1575 /* SEARCH */
1576
1577 void
1578 Editor::search_backwards ()
1579 {
1580         /* what ? */
1581 }
1582
1583 void
1584 Editor::search_forwards ()
1585 {
1586         /* what ? */
1587 }
1588
1589 /* MARKS */
1590
1591 void
1592 Editor::jump_forward_to_mark ()
1593 {
1594         if (!session) {
1595                 return;
1596         }
1597         
1598         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1599
1600         if (location) {
1601                 session->request_locate (location->start(), session->transport_rolling());
1602         } else {
1603                 session->request_locate (session->current_end_frame());
1604         }
1605 }
1606
1607 void
1608 Editor::jump_backward_to_mark ()
1609 {
1610         if (!session) {
1611                 return;
1612         }
1613
1614         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1615         
1616         if (location) {
1617                 session->request_locate (location->start(), session->transport_rolling());
1618         } else {
1619                 session->goto_start ();
1620         }
1621 }
1622
1623 void
1624 Editor::set_mark ()
1625 {
1626         nframes_t pos;
1627         float prefix;
1628         bool was_floating;
1629         string markername;
1630
1631         if (get_prefix (prefix, was_floating)) {
1632                 pos = session->audible_frame ();
1633         } else {
1634                 if (was_floating) {
1635                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1636                 } else {
1637                         pos = (nframes_t) floor (prefix);
1638                 }
1639         }
1640
1641         session->locations()->next_available_name(markername,"mark");
1642         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1643 }
1644
1645 void
1646 Editor::clear_markers ()
1647 {
1648         if (session) {
1649                 session->begin_reversible_command (_("clear markers"));
1650                 XMLNode &before = session->locations()->get_state();
1651                 session->locations()->clear_markers ();
1652                 XMLNode &after = session->locations()->get_state();
1653                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1654                 session->commit_reversible_command ();
1655         }
1656 }
1657
1658 void
1659 Editor::clear_ranges ()
1660 {
1661         if (session) {
1662                 session->begin_reversible_command (_("clear ranges"));
1663                 XMLNode &before = session->locations()->get_state();
1664                 
1665                 Location * looploc = session->locations()->auto_loop_location();
1666                 Location * punchloc = session->locations()->auto_punch_location();
1667                 
1668                 session->locations()->clear_ranges ();
1669                 // re-add these
1670                 if (looploc) session->locations()->add (looploc);
1671                 if (punchloc) session->locations()->add (punchloc);
1672                 
1673                 XMLNode &after = session->locations()->get_state();
1674                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1675                 session->commit_reversible_command ();
1676         }
1677 }
1678
1679 void
1680 Editor::clear_locations ()
1681 {
1682         session->begin_reversible_command (_("clear locations"));
1683         XMLNode &before = session->locations()->get_state();
1684         session->locations()->clear ();
1685         XMLNode &after = session->locations()->get_state();
1686         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1687         session->commit_reversible_command ();
1688         session->locations()->clear ();
1689 }
1690
1691 void
1692 Editor::unhide_markers ()
1693 {
1694         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1695                 Location *l = (*i).first;
1696                 if (l->is_hidden() && l->is_mark()) {
1697                         l->set_hidden(false, this);
1698                 }
1699         }
1700 }
1701
1702 void
1703 Editor::unhide_ranges ()
1704 {
1705         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1706                 Location *l = (*i).first;
1707                 if (l->is_hidden() && l->is_range_marker()) { 
1708                         l->set_hidden(false, this);
1709                 }
1710         }
1711 }
1712
1713 /* INSERT/REPLACE */
1714
1715 void
1716 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
1717 {
1718         double wx, wy;
1719         double cx, cy;
1720         TimeAxisView *tv;
1721         nframes_t where;
1722         AudioTimeAxisView *atv = 0;
1723         boost::shared_ptr<Playlist> playlist;
1724         
1725         track_canvas.window_to_world (x, y, wx, wy);
1726         wx += horizontal_adjustment.get_value();
1727         wy += vertical_adjustment.get_value();
1728
1729         GdkEvent event;
1730         event.type = GDK_BUTTON_RELEASE;
1731         event.button.x = wx;
1732         event.button.y = wy;
1733         
1734         where = event_frame (&event, &cx, &cy);
1735
1736         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1737                 /* clearly outside canvas area */
1738                 return;
1739         }
1740         
1741         if ((tv = trackview_by_y_position (cy)) == 0) {
1742                 return;
1743         }
1744         
1745         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
1746                 return;
1747         }
1748
1749         if ((playlist = atv->playlist()) == 0) {
1750                 return;
1751         }
1752         
1753         snap_to (where);
1754         
1755         begin_reversible_command (_("insert dragged region"));
1756         XMLNode &before = playlist->get_state();
1757         playlist->add_region (RegionFactory::create (region), where, 1.0);
1758         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1759         commit_reversible_command ();
1760 }
1761
1762 void
1763 Editor::insert_region_list_selection (float times)
1764 {
1765         RouteTimeAxisView *tv = 0;
1766         boost::shared_ptr<Playlist> playlist;
1767
1768         if (clicked_audio_trackview != 0) {
1769                 tv = clicked_audio_trackview;
1770         } else if (!selection->tracks.empty()) {
1771                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
1772                         return;
1773                 }
1774         } else {
1775                 return;
1776         }
1777
1778         if ((playlist = tv->playlist()) == 0) {
1779                 return;
1780         }
1781         
1782         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
1783         
1784         if (selected->count_selected_rows() != 1) {
1785                 return;
1786         }
1787         
1788         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
1789
1790         /* only one row selected, so rows.begin() is it */
1791
1792         TreeIter iter;
1793
1794         if ((iter = region_list_model->get_iter (*rows.begin()))) {
1795
1796                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
1797                 
1798                 begin_reversible_command (_("insert region"));
1799                 XMLNode &before = playlist->get_state();
1800                 playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times);
1801                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1802                 commit_reversible_command ();
1803         } 
1804 }
1805
1806 /* BUILT-IN EFFECTS */
1807
1808 void
1809 Editor::reverse_selection ()
1810 {
1811
1812 }
1813
1814 /* GAIN ENVELOPE EDITING */
1815
1816 void
1817 Editor::edit_envelope ()
1818 {
1819 }
1820
1821 /* PLAYBACK */
1822
1823 void
1824 Editor::transition_to_rolling (bool fwd)
1825 {
1826         if (!session) {
1827                 return;
1828         }
1829
1830         switch (Config->get_slave_source()) {
1831         case None:
1832         case JACK:
1833                 break;
1834         default:
1835                 /* transport controlled by the master */
1836                 return;
1837         }
1838
1839         if (session->is_auditioning()) {
1840                 session->cancel_audition ();
1841                 return;
1842         }
1843         
1844         session->request_transport_speed (fwd ? 1.0f : -1.0f);
1845 }
1846
1847 void
1848 Editor::toggle_playback (bool with_abort)
1849 {
1850         if (!session) {
1851                 return;
1852         }
1853
1854         switch (Config->get_slave_source()) {
1855         case None:
1856         case JACK:
1857                 break;
1858         default:
1859                 /* transport controlled by the master */
1860                 return;
1861         }
1862
1863         if (session->is_auditioning()) {
1864                 session->cancel_audition ();
1865                 return;
1866         }
1867         
1868         if (session->transport_rolling()) {
1869                 session->request_stop (with_abort);
1870                 if (session->get_play_loop()) {
1871                         session->request_play_loop (false);
1872                 }
1873         } else {
1874                 session->request_transport_speed (1.0f);
1875         }
1876 }
1877
1878 void
1879 Editor::play_from_start ()
1880 {
1881         session->request_locate (session->current_start_frame(), true);
1882 }
1883
1884 void
1885 Editor::play_from_edit_point ()
1886 {
1887         session->request_locate (get_preferred_edit_position(), true);
1888 }
1889
1890 void
1891 Editor::play_selection ()
1892 {
1893         if (selection->time.empty()) {
1894                 return;
1895         }
1896
1897         session->request_play_range (true);
1898 }
1899
1900 void
1901 Editor::play_selected_region ()
1902 {
1903         if (!selection->regions.empty()) {
1904                 RegionView *rv = *(selection->regions.begin());
1905
1906                 session->request_bounded_roll (rv->region()->position(), rv->region()->last_frame());   
1907         }
1908 }
1909
1910 void
1911 Editor::loop_selected_region ()
1912 {
1913         if (!selection->regions.empty()) {
1914                 RegionView *rv = *(selection->regions.begin());
1915                 Location* tll;
1916
1917                 if ((tll = transport_loop_location()) != 0)  {
1918
1919                         tll->set (rv->region()->position(), rv->region()->last_frame());
1920                         
1921                         // enable looping, reposition and start rolling
1922
1923                         session->request_play_loop (true);
1924                         session->request_locate (tll->start(), false);
1925                         session->request_transport_speed (1.0f);
1926                 }
1927         }
1928 }
1929
1930 void
1931 Editor::play_location (Location& location)
1932 {
1933         if (location.start() <= location.end()) {
1934                 return;
1935         }
1936
1937         session->request_bounded_roll (location.start(), location.end());
1938 }
1939
1940 void
1941 Editor::loop_location (Location& location)
1942 {
1943         if (location.start() <= location.end()) {
1944                 return;
1945         }
1946
1947         Location* tll;
1948
1949         if ((tll = transport_loop_location()) != 0) {
1950                 tll->set (location.start(), location.end());
1951
1952                 // enable looping, reposition and start rolling
1953                 session->request_play_loop (true);
1954                 session->request_locate (tll->start(), true);
1955         }
1956 }
1957
1958 void
1959 Editor::raise_region ()
1960 {
1961         selection->foreach_region (&Region::raise);
1962 }
1963
1964 void
1965 Editor::raise_region_to_top ()
1966 {
1967         selection->foreach_region (&Region::raise_to_top);
1968 }
1969
1970 void
1971 Editor::lower_region ()
1972 {
1973         selection->foreach_region (&Region::lower);
1974 }
1975
1976 void
1977 Editor::lower_region_to_bottom ()
1978 {
1979         selection->foreach_region (&Region::lower_to_bottom);
1980 }
1981
1982 void
1983 Editor::edit_region ()
1984 {
1985         if (clicked_regionview == 0) {
1986                 return;
1987         }
1988         
1989         clicked_regionview->show_region_editor ();
1990 }
1991
1992 void
1993 Editor::rename_region ()
1994 {
1995         Dialog dialog;
1996         Entry  entry;
1997         Button ok_button (_("OK"));
1998         Button cancel_button (_("Cancel"));
1999
2000         if (selection->regions.empty()) {
2001                 return;
2002         }
2003
2004         WindowTitle title(Glib::get_application_name());
2005         title += _("Rename Region");
2006
2007         dialog.set_title (title.get_string());
2008         dialog.set_name ("RegionRenameWindow");
2009         dialog.set_size_request (300, -1);
2010         dialog.set_position (Gtk::WIN_POS_MOUSE);
2011         dialog.set_modal (true);
2012
2013         dialog.get_vbox()->set_border_width (10);
2014         dialog.get_vbox()->pack_start (entry);
2015         dialog.get_action_area()->pack_start (ok_button);
2016         dialog.get_action_area()->pack_start (cancel_button);
2017
2018         entry.set_name ("RegionNameDisplay");
2019         ok_button.set_name ("EditorGTKButton");
2020         cancel_button.set_name ("EditorGTKButton");
2021
2022         region_renamed = false;
2023
2024         entry.signal_activate().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2025         ok_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2026         cancel_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), false));
2027
2028         /* recurse */
2029
2030         dialog.show_all ();
2031         Main::run ();
2032
2033         if (region_renamed) {
2034                 (*selection->regions.begin())->region()->set_name (entry.get_text());
2035                 redisplay_regions ();
2036         }
2037 }
2038
2039 void
2040 Editor::rename_region_finished (bool status)
2041
2042 {
2043         region_renamed = status;
2044         Main::quit ();
2045 }
2046
2047 void
2048 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2049 {
2050         if (session->is_auditioning()) {
2051                 session->cancel_audition ();
2052         } 
2053
2054         // note: some potential for creativity here, because region doesn't
2055         // have to belong to the playlist that Route is handling
2056
2057         // bool was_soloed = route.soloed();
2058
2059         route.set_solo (true, this);
2060         
2061         session->request_bounded_roll (region->position(), region->position() + region->length());
2062         
2063         /* XXX how to unset the solo state ? */
2064 }
2065
2066 void
2067 Editor::audition_selected_region ()
2068 {
2069         if (!selection->regions.empty()) {
2070                 RegionView* rv = *(selection->regions.begin());
2071                 session->audition_region (rv->region());
2072         }
2073 }
2074
2075 void
2076 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2077 {
2078         session->audition_region (region);
2079 }
2080
2081 void
2082 Editor::build_interthread_progress_window ()
2083 {
2084         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2085
2086         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2087         
2088         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2089         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2090
2091         // GTK2FIX: this button needs a modifiable label
2092
2093         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2094         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2095
2096         interthread_cancel_button.add (interthread_cancel_label);
2097
2098         interthread_progress_window->set_default_size (200, 100);
2099 }
2100
2101 void
2102 Editor::interthread_cancel_clicked ()
2103 {
2104         if (current_interthread_info) {
2105                 current_interthread_info->cancel = true;
2106         }
2107 }
2108
2109 void
2110 Editor::region_from_selection ()
2111 {
2112         if (clicked_trackview == 0) {
2113                 return;
2114         }
2115
2116         if (selection->time.empty()) {
2117                 return;
2118         }
2119
2120         nframes_t start = selection->time[clicked_selection].start;
2121         nframes_t end = selection->time[clicked_selection].end;
2122
2123         nframes_t selection_cnt = end - start + 1;
2124         
2125         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2126                 boost::shared_ptr<AudioRegion> current;
2127                 boost::shared_ptr<Region> current_r;
2128                 boost::shared_ptr<Playlist> pl;
2129
2130                 nframes_t internal_start;
2131                 string new_name;
2132
2133                 if ((pl = (*i)->playlist()) == 0) {
2134                         continue;
2135                 }
2136
2137                 if ((current_r = pl->top_region_at (start)) == 0) {
2138                         continue;
2139                 }
2140
2141                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2142                 // FIXME: audio only
2143                 if (current != 0) {
2144                         internal_start = start - current->position();
2145                         session->region_name (new_name, current->name(), true);
2146                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2147                 }
2148         }
2149 }       
2150
2151 void
2152 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2153 {
2154         if (selection->time.empty() || selection->tracks.empty()) {
2155                 return;
2156         }
2157
2158         nframes_t start = selection->time[clicked_selection].start;
2159         nframes_t end = selection->time[clicked_selection].end;
2160         
2161         sort_track_selection ();
2162
2163         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2164
2165                 boost::shared_ptr<AudioRegion> current;
2166                 boost::shared_ptr<Region> current_r;
2167                 boost::shared_ptr<Playlist> playlist;
2168                 nframes_t internal_start;
2169                 string new_name;
2170
2171                 if ((playlist = (*i)->playlist()) == 0) {
2172                         continue;
2173                 }
2174
2175                 if ((current_r = playlist->top_region_at(start)) == 0) {
2176                         continue;
2177                 }
2178
2179                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2180                         continue;
2181                 }
2182         
2183                 internal_start = start - current->position();
2184                 session->region_name (new_name, current->name(), true);
2185                 
2186                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2187         }
2188 }
2189
2190 void
2191 Editor::split_multichannel_region ()
2192 {
2193         if (selection->regions.empty()) {
2194                 return;
2195         }
2196
2197         vector<boost::shared_ptr<AudioRegion> > v;
2198
2199         for (list<RegionView*>::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2200
2201                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2202                 
2203                 if (!arv || arv->audio_region()->n_channels() < 2) {
2204                         continue;
2205                 }
2206
2207                 (arv)->audio_region()->separate_by_channel (*session, v);
2208         }
2209 }
2210
2211 void
2212 Editor::new_region_from_selection ()
2213 {
2214         region_from_selection ();
2215         cancel_selection ();
2216 }
2217
2218 void
2219 Editor::separate_region_from_selection ()
2220 {
2221         bool doing_undo = false;
2222
2223         if (selection->time.empty()) {
2224                 return;
2225         }
2226
2227         boost::shared_ptr<Playlist> playlist;
2228                 
2229         sort_track_selection ();
2230
2231         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2232
2233                 AudioTimeAxisView* atv;
2234
2235                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2236
2237                         if (atv->is_audio_track()) {
2238
2239                                 /* no edits to destructive tracks */
2240
2241                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2242                                         continue;
2243                                 }
2244                                         
2245                                 if ((playlist = atv->playlist()) != 0) {
2246                                         if (!doing_undo) {
2247                                                 begin_reversible_command (_("separate"));
2248                                                 doing_undo = true;
2249                                         }
2250                                         XMLNode *before;
2251                                         if (doing_undo) 
2252                                             before = &(playlist->get_state());
2253                         
2254                                         /* XXX need to consider musical time selections here at some point */
2255
2256                                         double speed = atv->get_diskstream()->speed();
2257
2258                                         for (list<AudioRange>::iterator t = selection->time.begin(); t != selection->time.end(); ++t) {
2259                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2260                                         }
2261
2262                                         if (doing_undo) 
2263                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2264                                 }
2265                         }
2266                 }
2267         }
2268
2269         if (doing_undo) commit_reversible_command ();
2270 }
2271
2272 void
2273 Editor::separate_regions_using_location (Location& loc)
2274 {
2275         bool doing_undo = false;
2276
2277         if (loc.is_mark()) {
2278                 return;
2279         }
2280
2281         boost::shared_ptr<Playlist> playlist;
2282
2283         /* XXX i'm unsure as to whether this should operate on selected tracks only 
2284            or the entire enchillada. uncomment the below line to correct the behaviour 
2285            (currently set for all tracks)
2286         */
2287
2288         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
2289         //for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2290
2291                 AudioTimeAxisView* atv;
2292
2293                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2294
2295                         if (atv->is_audio_track()) {
2296                                         
2297                                 /* no edits to destructive tracks */
2298
2299                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2300                                         continue;
2301                                 }
2302
2303                                 if ((playlist = atv->playlist()) != 0) {
2304                                         XMLNode *before;
2305                                         if (!doing_undo) {
2306                                                 begin_reversible_command (_("separate"));
2307                                                 doing_undo = true;
2308                                         }
2309                                         if (doing_undo) 
2310                                             before = &(playlist->get_state());
2311                                             
2312                         
2313                                         /* XXX need to consider musical time selections here at some point */
2314
2315                                         double speed = atv->get_diskstream()->speed();
2316
2317
2318                                         playlist->partition ((nframes_t)(loc.start() * speed), (nframes_t)(loc.end() * speed), true);
2319                                         if (doing_undo) 
2320                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2321                                 }
2322                         }
2323                 }
2324         }
2325
2326         if (doing_undo) commit_reversible_command ();
2327 }
2328
2329 void
2330 Editor::crop_region_to_selection ()
2331 {
2332         if (!selection->time.empty()) {
2333
2334                 crop_region_to (selection->time.start(), selection->time.end_frame());
2335
2336         } else if (_edit_point != EditAtPlayhead) {
2337
2338                 nframes64_t start;
2339                 nframes64_t end;
2340
2341                 if (get_edit_op_range (start, end)) {
2342                         crop_region_to (start, end);
2343                 }
2344         }
2345                 
2346 }               
2347
2348 void
2349 Editor::crop_region_to (nframes_t start, nframes_t end)
2350 {
2351         vector<boost::shared_ptr<Playlist> > playlists;
2352         boost::shared_ptr<Playlist> playlist;
2353         TrackSelection* ts;
2354
2355         if (selection->tracks.empty()) {
2356                 ts = &track_views;
2357         } else {
2358                 sort_track_selection ();
2359                 ts = &selection->tracks;
2360         }
2361         
2362         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2363                 
2364                 AudioTimeAxisView* atv;
2365                 
2366                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2367                         
2368                         if (atv->is_audio_track()) {
2369                                 
2370                                 /* no edits to destructive tracks */
2371
2372                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2373                                         continue;
2374                                 }
2375
2376                                 if ((playlist = atv->playlist()) != 0) {
2377                                         playlists.push_back (playlist);
2378                                 }
2379                         }
2380                 }
2381         }
2382
2383         if (playlists.empty()) {
2384                 return;
2385         }
2386                 
2387         nframes_t the_start;
2388         nframes_t the_end;
2389         nframes_t cnt;
2390         
2391         begin_reversible_command (_("trim to selection"));
2392         
2393         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2394                 
2395                 boost::shared_ptr<Region> region;
2396         
2397                 the_start = start;
2398         
2399                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2400                         continue;
2401                 }
2402                 
2403                 /* now adjust lengths to that we do the right thing
2404                    if the selection extends beyond the region
2405                 */
2406                 
2407                 the_start = max (the_start, region->position());
2408                 if (max_frames - the_start < region->length()) {
2409                         the_end = the_start + region->length() - 1;
2410                 } else {
2411                         the_end = max_frames;
2412                 }
2413                 the_end = min (end, the_end);
2414                 cnt = the_end - the_start + 1;
2415                 
2416                 XMLNode &before = (*i)->get_state();
2417                 region->trim_to (the_start, cnt, this);
2418                 XMLNode &after = (*i)->get_state();
2419                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2420         }
2421         
2422         commit_reversible_command ();
2423 }               
2424
2425 void
2426 Editor::region_fill_track ()
2427 {
2428         nframes_t end;
2429
2430         if (!session || selection->regions.empty()) {
2431                 return;
2432         }
2433
2434         end = session->current_end_frame ();
2435
2436         begin_reversible_command (_("region fill"));
2437
2438         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2439
2440                 boost::shared_ptr<Region> region ((*i)->region());
2441                 
2442                 // FIXME
2443                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2444                 if (!ar)
2445                         continue;
2446
2447                 boost::shared_ptr<Playlist> pl = region->playlist();
2448
2449                 if (end <= region->last_frame()) {
2450                         return;
2451                 }
2452
2453                 double times = (double) (end - region->last_frame()) / (double) region->length();
2454
2455                 if (times == 0) {
2456                         return;
2457                 }
2458
2459                 XMLNode &before = pl->get_state();
2460                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2461                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2462         }
2463
2464         commit_reversible_command ();
2465 }
2466
2467 void
2468 Editor::region_fill_selection ()
2469 {
2470         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2471                 return;
2472         }
2473
2474         if (selection->time.empty()) {
2475                 return;
2476         }
2477
2478
2479         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2480
2481         if (selected->count_selected_rows() != 1) {
2482                 return;
2483         }
2484
2485         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2486         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2487
2488         nframes_t start = selection->time[clicked_selection].start;
2489         nframes_t end = selection->time[clicked_selection].end;
2490
2491         boost::shared_ptr<Playlist> playlist; 
2492
2493         if (selection->tracks.empty()) {
2494                 return;
2495         }
2496
2497         nframes_t selection_length = end - start;
2498         float times = (float)selection_length / region->length();
2499         
2500         begin_reversible_command (_("fill selection"));
2501         
2502         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2503
2504                 if ((playlist = (*i)->playlist()) == 0) {
2505                         continue;
2506                 }               
2507                 
2508                 XMLNode &before = playlist->get_state();
2509                 playlist->add_region (RegionFactory::create (region), start, times);
2510                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2511         }
2512         
2513         commit_reversible_command ();                   
2514 }
2515
2516 void
2517 Editor::set_a_regions_sync_position (boost::shared_ptr<Region> region, nframes_t position)
2518 {
2519
2520         if (!region->covers (position)) {
2521           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2522                 return;
2523         }
2524         begin_reversible_command (_("set region sync position"));
2525         XMLNode &before = region->playlist()->get_state();
2526         region->set_sync_position (position);
2527         XMLNode &after = region->playlist()->get_state();
2528         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2529         commit_reversible_command ();
2530 }
2531
2532 void
2533 Editor::set_region_sync_from_edit_point ()
2534 {
2535         if (clicked_regionview == 0) {
2536                 return;
2537         }
2538
2539         if (!clicked_regionview->region()->covers (get_preferred_edit_position())) {
2540                 error << _("Place the edit point at the desired sync point") << endmsg;
2541                 return;
2542         }
2543
2544         boost::shared_ptr<Region> region (clicked_regionview->region());
2545         begin_reversible_command (_("set sync from edit point"));
2546         XMLNode &before = region->playlist()->get_state();
2547         region->set_sync_position (get_preferred_edit_position());
2548         XMLNode &after = region->playlist()->get_state();
2549         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2550         commit_reversible_command ();
2551 }
2552
2553 void
2554 Editor::remove_region_sync ()
2555 {
2556         if (clicked_regionview) {
2557                 boost::shared_ptr<Region> region (clicked_regionview->region());
2558                 begin_reversible_command (_("remove sync"));
2559                 XMLNode &before = region->playlist()->get_state();
2560                 region->clear_sync_position ();
2561                 XMLNode &after = region->playlist()->get_state();
2562                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2563                 commit_reversible_command ();
2564         }
2565 }
2566
2567 void
2568 Editor::naturalize ()
2569 {
2570         if (selection->regions.empty()) {
2571                 return;
2572         }
2573         begin_reversible_command (_("naturalize"));
2574         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2575                 XMLNode &before = (*i)->region()->get_state();
2576                 (*i)->region()->move_to_natural_position (this);
2577                 XMLNode &after = (*i)->region()->get_state();
2578                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2579         }
2580         commit_reversible_command ();
2581 }
2582
2583 void
2584 Editor::align (RegionPoint what)
2585 {
2586         nframes64_t where = get_preferred_edit_position();
2587
2588         if (!selection->regions.empty()) {
2589                 align_selection (what, where, selection->regions);
2590         } else {
2591
2592                 RegionSelection rs;
2593                 rs = get_regions_at (where, selection->tracks);
2594                 align_selection (what, where, rs);
2595         }
2596 }
2597
2598 void
2599 Editor::align_relative (RegionPoint what)
2600 {
2601         nframes64_t where = get_preferred_edit_position();
2602
2603         if (!selection->regions.empty()) {
2604                 align_selection_relative (what, where, selection->regions);
2605         } else {
2606
2607                 RegionSelection rs;
2608                 rs = get_regions_at (where, selection->tracks);
2609                 align_selection_relative (what, where, rs);
2610         }
2611 }
2612
2613 struct RegionSortByTime {
2614     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2615             return a->region()->position() < b->region()->position();
2616     }
2617 };
2618
2619 void
2620 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
2621 {
2622         if (rs.empty()) {
2623                 return;
2624         }
2625
2626         nframes_t distance;
2627         nframes_t pos = 0;
2628         int dir;
2629
2630         list<RegionView*> sorted;
2631         rs.by_position (sorted);
2632         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2633
2634         switch (point) {
2635         case Start:
2636                 pos = r->first_frame ();
2637                 break;
2638
2639         case End:
2640                 pos = r->last_frame();
2641                 break;
2642
2643         case SyncPoint:
2644                 pos = r->adjust_to_sync (r->first_frame());
2645                 break;  
2646         }
2647
2648         if (pos > position) {
2649                 distance = pos - position;
2650                 dir = -1;
2651         } else {
2652                 distance = position - pos;
2653                 dir = 1;
2654         }
2655
2656         begin_reversible_command (_("align selection (relative)"));
2657
2658         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
2659
2660                 boost::shared_ptr<Region> region ((*i)->region());
2661
2662                 XMLNode &before = region->playlist()->get_state();
2663                 
2664                 if (dir > 0) {
2665                         region->set_position (region->position() + distance, this);
2666                 } else {
2667                         region->set_position (region->position() - distance, this);
2668                 }
2669
2670                 XMLNode &after = region->playlist()->get_state();
2671                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2672
2673         }
2674
2675         commit_reversible_command ();
2676 }
2677
2678 void
2679 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
2680 {
2681         if (rs.empty()) {
2682                 return;
2683         }
2684
2685         begin_reversible_command (_("align selection"));
2686
2687         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
2688                 align_region_internal ((*i)->region(), point, position);
2689         }
2690
2691         commit_reversible_command ();
2692 }
2693
2694 void
2695 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2696 {
2697         begin_reversible_command (_("align region"));
2698         align_region_internal (region, point, position);
2699         commit_reversible_command ();
2700 }
2701
2702 void
2703 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2704 {
2705         XMLNode &before = region->playlist()->get_state();
2706
2707         switch (point) {
2708         case SyncPoint:
2709                 region->set_position (region->adjust_to_sync (position), this);
2710                 break;
2711
2712         case End:
2713                 if (position > region->length()) {
2714                         region->set_position (position - region->length(), this);
2715                 }
2716                 break;
2717
2718         case Start:
2719                 region->set_position (position, this);
2720                 break;
2721         }
2722
2723         XMLNode &after = region->playlist()->get_state();
2724         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2725 }       
2726
2727 void
2728 Editor::trim_region_to_loop ()
2729 {
2730         Location* loc = session->locations()->auto_loop_location();
2731         if (!loc) {
2732                 return;
2733         }
2734         trim_region_to_location (*loc, _("trim to loop"));
2735 }
2736
2737 void
2738 Editor::trim_region_to_punch ()
2739 {
2740         Location* loc = session->locations()->auto_punch_location();
2741         if (!loc) {
2742                 return;
2743         }
2744         trim_region_to_location (*loc, _("trim to punch"));
2745 }
2746
2747 void
2748 Editor::trim_region_to_location (const Location& loc, const char* str)
2749 {
2750         RegionSelection& rs (get_regions_for_action ());
2751
2752         begin_reversible_command (str);
2753
2754         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2755                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2756
2757                 if (!arv) {
2758                         continue;
2759                 }
2760
2761                 /* require region to span proposed trim */
2762
2763                 switch (arv->region()->coverage (loc.start(), loc.end())) {
2764                 case OverlapInternal:
2765                         break;
2766                 default:
2767                         continue;
2768                 }
2769                                 
2770                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2771
2772                 if (!atav) {
2773                         return;
2774                 }
2775
2776                 float speed = 1.0;
2777                 nframes_t start;
2778                 nframes_t end;
2779
2780                 if (atav->get_diskstream() != 0) {
2781                         speed = atav->get_diskstream()->speed();
2782                 }
2783
2784                 start = session_frame_to_track_frame (loc.start(), speed);
2785                 end = session_frame_to_track_frame (loc.end(), speed);
2786
2787                 XMLNode &before = arv->region()->playlist()->get_state();
2788                 arv->region()->trim_to (start, (end - start), this);
2789                 XMLNode &after = arv->region()->playlist()->get_state();
2790                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2791         }
2792                 
2793         commit_reversible_command ();
2794 }
2795
2796 void
2797 Editor::trim_region_to_edit_point ()
2798 {
2799         RegionSelection& rs (get_regions_for_action ());
2800         nframes64_t where = get_preferred_edit_position();
2801
2802         begin_reversible_command (_("trim region start to edit point"));
2803
2804         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2805                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2806
2807                 if (!arv) {
2808                         continue;
2809                 }
2810
2811                 /* require region to cover trim */
2812
2813                 if (!arv->region()->covers (where)) {
2814                         continue;
2815                 }
2816
2817                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2818
2819                 if (!atav) {
2820                         return;
2821                 }
2822
2823                 float speed = 1.0;
2824
2825                 if (atav->get_diskstream() != 0) {
2826                         speed = atav->get_diskstream()->speed();
2827                 }
2828
2829                 XMLNode &before = arv->region()->playlist()->get_state();
2830                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
2831                 XMLNode &after = arv->region()->playlist()->get_state();
2832                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2833         }
2834                 
2835         commit_reversible_command ();
2836 }
2837
2838 void
2839 Editor::trim_region_from_edit_point ()
2840 {
2841         RegionSelection& rs (get_regions_for_action ());
2842         nframes64_t where = get_preferred_edit_position();
2843
2844         begin_reversible_command (_("trim region end to edit point"));
2845
2846         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2847                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2848
2849                 if (!arv) {
2850                         continue;
2851                 }
2852
2853                 /* require region to cover trim */
2854
2855                 if (!arv->region()->covers (where)) {
2856                         continue;
2857                 }
2858
2859                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2860
2861                 if (!atav) {
2862                         return;
2863                 }
2864
2865                 float speed = 1.0;
2866
2867                 if (atav->get_diskstream() != 0) {
2868                         speed = atav->get_diskstream()->speed();
2869                 }
2870
2871                 XMLNode &before = arv->region()->playlist()->get_state();
2872                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
2873                 XMLNode &after = arv->region()->playlist()->get_state();
2874                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2875         }
2876                 
2877         commit_reversible_command ();
2878 }
2879
2880 void
2881 Editor::unfreeze_route ()
2882 {
2883         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2884                 return;
2885         }
2886         
2887         clicked_audio_trackview->audio_track()->unfreeze ();
2888 }
2889
2890 void*
2891 Editor::_freeze_thread (void* arg)
2892 {
2893         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2894         return static_cast<Editor*>(arg)->freeze_thread ();
2895 }
2896
2897 void*
2898 Editor::freeze_thread ()
2899 {
2900         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2901         return 0;
2902 }
2903
2904 gint
2905 Editor::freeze_progress_timeout (void *arg)
2906 {
2907         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2908         return !(current_interthread_info->done || current_interthread_info->cancel);
2909 }
2910
2911 void
2912 Editor::freeze_route ()
2913 {
2914         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2915                 return;
2916         }
2917         
2918         InterThreadInfo itt;
2919
2920         if (interthread_progress_window == 0) {
2921                 build_interthread_progress_window ();
2922         }
2923
2924         WindowTitle title(Glib::get_application_name());
2925         title += _("Freeze");
2926         interthread_progress_window->set_title (title.get_string());
2927         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2928         interthread_progress_window->show_all ();
2929         interthread_progress_bar.set_fraction (0.0f);
2930         interthread_progress_label.set_text ("");
2931         interthread_cancel_label.set_text (_("Cancel Freeze"));
2932         current_interthread_info = &itt;
2933
2934         interthread_progress_connection = 
2935           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2936
2937         itt.done = false;
2938         itt.cancel = false;
2939         itt.progress = 0.0f;
2940         
2941         pthread_attr_t attr;
2942         pthread_attr_init(&attr);
2943         pthread_attr_setstacksize(&attr, 500000);
2944
2945         pthread_create (&itt.thread, &attr, _freeze_thread, this);
2946
2947         pthread_attr_destroy(&attr);
2948
2949         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2950
2951         while (!itt.done && !itt.cancel) {
2952                 gtk_main_iteration ();
2953         }
2954
2955         interthread_progress_connection.disconnect ();
2956         interthread_progress_window->hide_all ();
2957         current_interthread_info = 0;
2958         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2959 }
2960
2961 void
2962 Editor::bounce_range_selection ()
2963 {
2964         if (selection->time.empty()) {
2965                 return;
2966         }
2967
2968         TrackSelection views = selection->tracks;
2969
2970         nframes_t start = selection->time[clicked_selection].start;
2971         nframes_t end = selection->time[clicked_selection].end;
2972         nframes_t cnt = end - start + 1;
2973
2974         begin_reversible_command (_("bounce range"));
2975
2976         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
2977
2978                 AudioTimeAxisView* atv;
2979
2980                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2981                         continue;
2982                 }
2983                 
2984                 boost::shared_ptr<Playlist> playlist;
2985                 
2986                 if ((playlist = atv->playlist()) == 0) {
2987                         return;
2988                 }
2989
2990                 InterThreadInfo itt;
2991                 
2992                 itt.done = false;
2993                 itt.cancel = false;
2994                 itt.progress = false;
2995
2996                 XMLNode &before = playlist->get_state();
2997                 atv->audio_track()->bounce_range (start, cnt, itt);
2998                 XMLNode &after = playlist->get_state();
2999                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3000         }
3001         
3002         commit_reversible_command ();
3003 }
3004
3005 void
3006 Editor::cut ()
3007 {
3008         cut_copy (Cut);
3009 }
3010
3011 void
3012 Editor::copy ()
3013 {
3014         cut_copy (Copy);
3015 }
3016
3017 void 
3018 Editor::cut_copy (CutCopyOp op)
3019 {
3020         /* only cancel selection if cut/copy is successful.*/
3021
3022         string opname;
3023
3024         switch (op) {
3025         case Cut:
3026                 opname = _("cut");
3027                 break;
3028         case Copy:
3029                 opname = _("copy");
3030                 break;
3031         case Clear:
3032                 opname = _("clear");
3033                 break;
3034         }
3035         
3036         cut_buffer->clear ();
3037
3038         if (entered_marker) {
3039
3040                 /* cut/delete op while pointing at a marker */
3041
3042                 bool ignored;
3043                 Location* loc = find_location_from_marker (entered_marker, ignored);
3044
3045                 if (session && loc) {
3046                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3047                 }
3048
3049                 return;
3050         }
3051
3052         switch (current_mouse_mode()) {
3053         case MouseObject: 
3054                 if (!selection->regions.empty() || !selection->points.empty()) {
3055
3056                         begin_reversible_command (opname + _(" objects"));
3057
3058                         if (!selection->regions.empty()) {
3059                                 
3060                                 cut_copy_regions (op);
3061                                 
3062                                 if (op == Cut) {
3063                                         selection->clear_regions ();
3064                                 }
3065                         }
3066
3067                         if (!selection->points.empty()) {
3068                                 cut_copy_points (op);
3069
3070                                 if (op == Cut) {
3071                                         selection->clear_points ();
3072                                 }
3073                         }
3074
3075                         commit_reversible_command ();   
3076                         break; // terminate case statement here
3077                 } 
3078                 if (!selection->time.empty()) {
3079                         /* don't cause suprises */
3080                         break;
3081                 }
3082                 // fall thru if there was nothing selected
3083                 
3084         case MouseRange:
3085                 if (selection->time.empty()) {
3086                         nframes64_t start, end;
3087                         if (!get_edit_op_range (start, end)) {
3088                                 return;
3089                         }
3090                         selection->set (0, start, end);
3091                 }
3092                         
3093                 begin_reversible_command (opname + _(" range"));
3094                 cut_copy_ranges (op);
3095                 commit_reversible_command ();
3096                 
3097                 if (op == Cut) {
3098                         selection->clear_time ();
3099                 }
3100
3101                 break;
3102                 
3103         default:
3104                 break;
3105         }
3106 }
3107
3108 void
3109 Editor::cut_copy_points (CutCopyOp op)
3110 {
3111         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3112
3113                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3114
3115                 if (atv) {
3116                         atv->cut_copy_clear_objects (selection->points, op);
3117                 } 
3118         }
3119 }
3120
3121 struct PlaylistState {
3122     boost::shared_ptr<Playlist> playlist;
3123     XMLNode*  before;
3124 };
3125
3126 struct lt_playlist {
3127     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3128             return a.playlist < b.playlist;
3129     }
3130 };
3131         
3132 struct PlaylistMapping { 
3133     TimeAxisView* tv;
3134     boost::shared_ptr<AudioPlaylist> pl;
3135
3136     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3137 };
3138
3139 void
3140 Editor::cut_copy_regions (CutCopyOp op)
3141 {
3142         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3143            a map when we want ordered access to both elements. i think.
3144         */
3145
3146         vector<PlaylistMapping> pmap;
3147
3148         nframes_t first_position = max_frames;
3149         
3150         set<PlaylistState, lt_playlist> freezelist;
3151         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3152         
3153         /* get ordering correct before we cut/copy */
3154         
3155         selection->regions.sort_by_position_and_track ();
3156
3157         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3158
3159                 first_position = min ((*x)->region()->position(), first_position);
3160
3161                 if (op == Cut || op == Clear) {
3162                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3163
3164                         if (pl) {
3165
3166                                 PlaylistState before;
3167                                 before.playlist = pl;
3168                                 before.before = &pl->get_state();
3169                                 
3170                                 insert_result = freezelist.insert (before);
3171                                 
3172                                 if (insert_result.second) {
3173                                         pl->freeze ();
3174                                 }
3175                         }
3176                 }
3177
3178                 TimeAxisView* tv = &(*x)->get_trackview();
3179                 vector<PlaylistMapping>::iterator z;
3180
3181                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3182                         if ((*z).tv == tv) {
3183                                 break;
3184                         }
3185                 }
3186                 
3187                 if (z == pmap.end()) {
3188                         pmap.push_back (PlaylistMapping (tv));
3189                 }
3190         }
3191
3192         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3193
3194                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3195                 
3196                 if (!pl) {
3197                         /* impossible, but this handles it for the future */
3198                         continue;
3199                 }
3200
3201                 TimeAxisView& tv = (*x)->get_trackview();
3202                 boost::shared_ptr<AudioPlaylist> npl;
3203                 RegionSelection::iterator tmp;
3204                 
3205                 tmp = x;
3206                 ++tmp;
3207
3208                 vector<PlaylistMapping>::iterator z;
3209                 
3210                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3211                         if ((*z).tv == &tv) {
3212                                 break;
3213                         }
3214                 }
3215                 
3216                 assert (z != pmap.end());
3217                 
3218                 if (!(*z).pl) {
3219                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3220                         npl->freeze();
3221                         (*z).pl = npl;
3222                 } else {
3223                         npl = (*z).pl;
3224                 }
3225                 
3226                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3227                 boost::shared_ptr<Region> _xx;
3228                 
3229                 switch (op) {
3230                 case Cut:
3231                         if (!ar) break;
3232                         
3233                         _xx = RegionFactory::create ((*x)->region());
3234                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3235                         pl->remove_region (((*x)->region()));
3236                         break;
3237                         
3238                 case Copy:
3239                         if (!ar) break;
3240
3241                         /* copy region before adding, so we're not putting same object into two different playlists */
3242                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3243                         break;
3244                         
3245                 case Clear:
3246                         pl->remove_region (((*x)->region()));
3247                         break;
3248                 }
3249
3250                 x = tmp;
3251         }
3252         
3253         list<boost::shared_ptr<Playlist> > foo;
3254         
3255         /* the pmap is in the same order as the tracks in which selected regions occured */
3256         
3257         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3258                 (*i).pl->thaw();
3259                 foo.push_back ((*i).pl);
3260         }
3261         
3262
3263         if (!foo.empty()) {
3264                 cut_buffer->set (foo);
3265         }
3266
3267         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3268                 (*pl).playlist->thaw ();
3269                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3270         }
3271 }
3272
3273 void
3274 Editor::cut_copy_ranges (CutCopyOp op)
3275 {
3276         TrackSelection* ts;
3277
3278         if (selection->tracks.empty()) {
3279                 ts = &track_views;
3280         } else {
3281                 ts = &selection->tracks;
3282         }
3283
3284         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3285                 (*i)->cut_copy_clear (*selection, op);
3286         }
3287 }
3288
3289 void
3290 Editor::paste (float times)
3291 {
3292         paste_internal (get_preferred_edit_position(), times);
3293 }
3294
3295 void
3296 Editor::mouse_paste ()
3297 {
3298         nframes64_t where;
3299         bool ignored;
3300
3301         if (!mouse_frame (where, ignored)) {
3302                 return;
3303         }
3304
3305         snap_to (where);
3306         paste_internal (where, 1);
3307 }
3308
3309 void
3310 Editor::paste_internal (nframes_t position, float times)
3311 {
3312         bool commit = false;
3313
3314         if (cut_buffer->empty() || selection->tracks.empty()) {
3315                 return;
3316         }
3317
3318         if (position == max_frames) {
3319                 position = get_preferred_edit_position();
3320         }
3321
3322         begin_reversible_command (_("paste"));
3323
3324         TrackSelection::iterator i;
3325         size_t nth;
3326
3327         /* get everything in the correct order */
3328
3329         sort_track_selection ();
3330
3331         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3332
3333                 /* undo/redo is handled by individual tracks */
3334
3335                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3336                         commit = true;
3337                 }
3338         }
3339         
3340         if (commit) {
3341                 commit_reversible_command ();
3342         }
3343 }
3344
3345 void
3346 Editor::paste_named_selection (float times)
3347 {
3348         TrackSelection::iterator t;
3349
3350         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3351
3352         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3353                 return;
3354         }
3355
3356         TreeModel::iterator i = selected->get_selected();
3357         NamedSelection* ns = (*i)[named_selection_columns.selection];
3358
3359         list<boost::shared_ptr<Playlist> >::iterator chunk;
3360         list<boost::shared_ptr<Playlist> >::iterator tmp;
3361
3362         chunk = ns->playlists.begin();
3363                 
3364         begin_reversible_command (_("paste chunk"));
3365         
3366         sort_track_selection ();
3367
3368         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3369                 
3370                 AudioTimeAxisView* atv;
3371                 boost::shared_ptr<Playlist> pl;
3372                 boost::shared_ptr<AudioPlaylist> apl;
3373
3374                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3375                         continue;
3376                 }
3377
3378                 if ((pl = atv->playlist()) == 0) {
3379                         continue;
3380                 }
3381                 
3382                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3383                         continue;
3384                 }
3385
3386                 tmp = chunk;
3387                 ++tmp;
3388
3389                 XMLNode &before = apl->get_state();
3390                 apl->paste (*chunk, get_preferred_edit_position(), times);
3391                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3392
3393                 if (tmp != ns->playlists.end()) {
3394                         chunk = tmp;
3395                 }
3396         }
3397
3398         commit_reversible_command();
3399 }
3400
3401 void
3402 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3403 {
3404         boost::shared_ptr<Playlist> playlist; 
3405         RegionSelection sel = regions; // clear (below) will clear the argument list
3406                 
3407         begin_reversible_command (_("duplicate region"));
3408
3409         selection->clear_regions ();
3410
3411         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3412
3413                 boost::shared_ptr<Region> r ((*i)->region());
3414
3415                 TimeAxisView& tv = (*i)->get_time_axis_view();
3416                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3417                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3418                 
3419                 playlist = (*i)->region()->playlist();
3420                 XMLNode &before = playlist->get_state();
3421                 playlist->duplicate (r, r->last_frame() + 1, times);
3422                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3423
3424                 c.disconnect ();
3425
3426                 if (latest_regionview) {
3427                         selection->add (latest_regionview);
3428                 }
3429         }
3430                 
3431
3432         commit_reversible_command ();
3433 }
3434
3435 void
3436 Editor::duplicate_selection (float times)
3437 {
3438         if (selection->time.empty() || selection->tracks.empty()) {
3439                 return;
3440         }
3441
3442         boost::shared_ptr<Playlist> playlist; 
3443         vector<boost::shared_ptr<AudioRegion> > new_regions;
3444         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3445                 
3446         create_region_from_selection (new_regions);
3447
3448         if (new_regions.empty()) {
3449                 return;
3450         }
3451         
3452         begin_reversible_command (_("duplicate selection"));
3453
3454         ri = new_regions.begin();
3455
3456         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3457                 if ((playlist = (*i)->playlist()) == 0) {
3458                         continue;
3459                 }
3460                 XMLNode &before = playlist->get_state();
3461                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3462                 XMLNode &after = playlist->get_state();
3463                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3464
3465                 ++ri;
3466                 if (ri == new_regions.end()) {
3467                         --ri;
3468                 }
3469         }
3470
3471         commit_reversible_command ();
3472 }
3473
3474 void
3475 Editor::reset_point_selection ()
3476 {
3477         /* reset all selected points to the relevant default value */
3478
3479         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3480                 
3481                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3482                 
3483                 if (atv) {
3484                         atv->reset_objects (selection->points);
3485                 } 
3486         }
3487 }
3488
3489 void
3490 Editor::center_playhead ()
3491 {
3492         float page = canvas_width * frames_per_unit;
3493         center_screen_internal (playhead_cursor->current_frame, page);
3494 }
3495
3496 void
3497 Editor::center_edit_point ()
3498 {
3499         float page = canvas_width * frames_per_unit;
3500         center_screen_internal (get_preferred_edit_position(), page);
3501 }
3502
3503 void
3504 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3505 {
3506         begin_reversible_command (_("clear playlist"));
3507         XMLNode &before = playlist->get_state();
3508         playlist->clear ();
3509         XMLNode &after = playlist->get_state();
3510         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3511         commit_reversible_command ();
3512 }
3513
3514 void
3515 Editor::nudge_track (bool use_edit, bool forwards)
3516 {
3517         boost::shared_ptr<Playlist> playlist; 
3518         nframes_t distance;
3519         nframes_t next_distance;
3520         nframes_t start;
3521
3522         if (use_edit) {
3523                 start = get_preferred_edit_position();
3524         } else {
3525                 start = 0;
3526         }
3527
3528         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3529                 return;
3530         }
3531         
3532         if (selection->tracks.empty()) {
3533                 return;
3534         }
3535         
3536         begin_reversible_command (_("nudge track"));
3537         
3538         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3539
3540                 if ((playlist = (*i)->playlist()) == 0) {
3541                         continue;
3542                 }               
3543                 
3544                 XMLNode &before = playlist->get_state();
3545                 playlist->nudge_after (start, distance, forwards);
3546                 XMLNode &after = playlist->get_state();
3547                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3548         }
3549         
3550         commit_reversible_command ();                   
3551 }
3552
3553 void
3554 Editor::remove_last_capture ()
3555 {
3556         vector<string> choices;
3557         string prompt;
3558         
3559         if (!session) {
3560                 return;
3561         }
3562
3563         if (Config->get_verify_remove_last_capture()) {
3564                 prompt  = _("Do you really want to destroy the last capture?"
3565                             "\n(This is destructive and cannot be undone)");
3566
3567                 choices.push_back (_("No, do nothing."));
3568                 choices.push_back (_("Yes, destroy it."));
3569                 
3570                 Gtkmm2ext::Choice prompter (prompt, choices);
3571                 
3572                 if (prompter.run () == 1) {
3573                         session->remove_last_capture ();
3574                 }
3575
3576         } else {
3577                 session->remove_last_capture();
3578         }
3579 }
3580
3581 void
3582 Editor::normalize_region ()
3583 {
3584         if (!session) {
3585                 return;
3586         }
3587
3588         if (selection->regions.empty()) {
3589                 return;
3590         }
3591
3592         begin_reversible_command (_("normalize"));
3593
3594         track_canvas.get_window()->set_cursor (*wait_cursor);
3595         gdk_flush ();
3596
3597         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3598                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3599                 if (!arv)
3600                         continue;
3601                 XMLNode &before = arv->region()->get_state();
3602                 arv->audio_region()->normalize_to (0.0f);
3603                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3604         }
3605
3606         commit_reversible_command ();
3607         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3608 }
3609
3610
3611 void
3612 Editor::denormalize_region ()
3613 {
3614         if (!session) {
3615                 return;
3616         }
3617
3618         if (selection->regions.empty()) {
3619                 return;
3620         }
3621
3622         begin_reversible_command ("denormalize");
3623
3624         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3625                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3626                 if (!arv)
3627                         continue;
3628                 XMLNode &before = arv->region()->get_state();
3629                 arv->audio_region()->set_scale_amplitude (1.0f);
3630                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3631         }
3632
3633         commit_reversible_command ();
3634 }
3635
3636
3637 void
3638 Editor::reverse_region ()
3639 {
3640         if (!session) {
3641                 return;
3642         }
3643
3644         Reverse rev (*session);
3645         apply_filter (rev, _("reverse regions"));
3646 }
3647
3648 void
3649 Editor::apply_filter (AudioFilter& filter, string command)
3650 {
3651         if (selection->regions.empty()) {
3652                 return;
3653         }
3654
3655         begin_reversible_command (command);
3656
3657         track_canvas.get_window()->set_cursor (*wait_cursor);
3658         gdk_flush ();
3659
3660         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3661                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3662                 if (!arv)
3663                         continue;
3664
3665                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3666
3667                 RegionSelection::iterator tmp;
3668                 
3669                 tmp = r;
3670                 ++tmp;
3671
3672                 if (arv->audio_region()->apply (filter) == 0) {
3673
3674                         XMLNode &before = playlist->get_state();
3675                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3676                         XMLNode &after = playlist->get_state();
3677                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3678                 } else {
3679                         goto out;
3680                 }
3681
3682                 r = tmp;
3683         }
3684
3685         commit_reversible_command ();
3686         selection->regions.clear ();
3687
3688   out:
3689         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3690 }
3691
3692 void
3693 Editor::region_selection_op (void (Region::*pmf)(void))
3694 {
3695         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3696                 Region* region = (*i)->region().get();
3697                 (region->*pmf)();
3698         }
3699 }
3700
3701
3702 void
3703 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3704 {
3705         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3706                 Region* region = (*i)->region().get();
3707                 (region->*pmf)(arg);
3708         }
3709 }
3710
3711 void
3712 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3713 {
3714         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3715                 Region* region = (*i)->region().get();
3716                 (region->*pmf)(yn);
3717         }
3718 }
3719
3720 void
3721 Editor::external_edit_region ()
3722 {
3723         if (!clicked_regionview) {
3724                 return;
3725         }
3726
3727         /* more to come */
3728 }
3729
3730 void
3731 Editor::brush (nframes_t pos)
3732 {
3733         RegionSelection sel;
3734         snap_to (pos);
3735
3736         if (selection->regions.empty()) {
3737                 /* XXX get selection from region list */
3738         } else { 
3739                 sel = selection->regions;
3740         }
3741
3742         if (sel.empty()) {
3743                 return;
3744         }
3745
3746         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3747                 mouse_brush_insert_region ((*i), pos);
3748         }
3749 }
3750
3751 void
3752 Editor::reset_region_gain_envelopes ()
3753 {
3754         if (!session || selection->regions.empty()) {
3755                 return;
3756         }
3757
3758         session->begin_reversible_command (_("reset region gain"));
3759
3760         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3761                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3762                 if (arv) {
3763                         AutomationList& alist (arv->audio_region()->envelope());
3764                         XMLNode& before (alist.get_state());
3765
3766                         arv->audio_region()->set_default_envelope ();
3767                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3768                 }
3769         }
3770
3771         session->commit_reversible_command ();
3772 }
3773
3774 void
3775 Editor::toggle_gain_envelope_visibility ()
3776 {
3777         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3778                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3779                 if (arv) {
3780                         bool x = region_envelope_visible_item->get_active();
3781                         if (x != arv->envelope_visible()) {
3782                                 arv->set_envelope_visible (x);
3783                         }
3784                 }
3785         }
3786 }
3787
3788 void
3789 Editor::toggle_gain_envelope_active ()
3790 {
3791         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3792                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3793                 if (arv) {
3794                         bool x = region_envelope_active_item->get_active();
3795                         if (x != arv->audio_region()->envelope_active()) {
3796                                 arv->audio_region()->set_envelope_active (x);
3797                         }
3798                 }
3799         }
3800 }
3801
3802 void
3803 Editor::toggle_region_lock ()
3804 {
3805         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3806                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3807                 if (arv) {
3808                         bool x = region_lock_item->get_active();
3809                         if (x != arv->audio_region()->locked()) {
3810                                 arv->audio_region()->set_locked (x);
3811                         }
3812                 }
3813         }
3814 }
3815
3816 void
3817 Editor::toggle_region_mute ()
3818 {
3819         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3820                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3821                 if (arv) {
3822                         bool x = region_mute_item->get_active();
3823                         if (x != arv->audio_region()->muted()) {
3824                                 arv->audio_region()->set_muted (x);
3825                         }
3826                 }
3827         }
3828 }
3829
3830 void
3831 Editor::toggle_region_opaque ()
3832 {
3833         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3834                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3835                 if (arv) {
3836                         bool x = region_opaque_item->get_active();
3837                         if (x != arv->audio_region()->opaque()) {
3838                                 arv->audio_region()->set_opaque (x);
3839                         }
3840                 }
3841         }
3842 }
3843
3844 void
3845 Editor::set_fade_length (bool in)
3846 {
3847         /* we need a region to measure the offset from the start */
3848
3849         RegionView* rv;
3850
3851         if (entered_regionview) {
3852                 rv = entered_regionview;
3853         } else if (!selection->regions.empty()) {
3854                 rv = selection->regions.front();
3855         } else {
3856                 return;
3857         }
3858
3859         nframes64_t pos = get_preferred_edit_position();
3860         nframes_t len;
3861         char* cmd;
3862
3863         if (in) {
3864                 if (pos <= rv->region()->start()) {
3865                         /* can't do it */
3866                         return;
3867                 }
3868                 len = pos - rv->region()->start();
3869                 cmd = _("set fade in length");
3870         } else {
3871                 if (pos >= rv->region()->last_frame()) {
3872                         /* can't do it */
3873                         return;
3874                 }
3875                 len = rv->region()->last_frame() - pos;
3876                 cmd = _("set fade out length");
3877         }
3878
3879         begin_reversible_command (cmd);
3880
3881         cerr << "start " << cmd << " with len = " << len << endl;
3882
3883         RegionSelection& rs (get_regions_for_action());
3884
3885         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3886                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3887
3888                 if (!tmp) {
3889                         return;
3890                 }
3891
3892                 AutomationList& alist = tmp->audio_region()->fade_in();
3893                 XMLNode &before = alist.get_state();
3894
3895                 if (in) {
3896                         tmp->audio_region()->set_fade_in_length (len);
3897                 } else {
3898                         tmp->audio_region()->set_fade_out_length (len);
3899                 }
3900                 
3901                 XMLNode &after = alist.get_state();
3902                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3903         }
3904
3905         commit_reversible_command ();
3906 }
3907
3908 void
3909 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
3910 {
3911         begin_reversible_command (_("set fade in shape"));
3912
3913         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3914                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3915
3916                 if (!tmp) {
3917                         return;
3918                 }
3919
3920                 AutomationList& alist = tmp->audio_region()->fade_in();
3921                 XMLNode &before = alist.get_state();
3922
3923                 tmp->audio_region()->set_fade_in_shape (shape);
3924                 
3925                 XMLNode &after = alist.get_state();
3926                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3927         }
3928
3929         commit_reversible_command ();
3930 }
3931
3932 void
3933 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
3934 {
3935         begin_reversible_command (_("set fade out shape"));
3936
3937         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3938                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3939
3940                 if (!tmp) {
3941                         return;
3942                 }
3943
3944                 AutomationList& alist = tmp->audio_region()->fade_out();
3945                 XMLNode &before = alist.get_state();
3946
3947                 tmp->audio_region()->set_fade_out_shape (shape);
3948                 
3949                 XMLNode &after = alist.get_state();
3950                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3951         }
3952
3953         commit_reversible_command ();
3954 }
3955
3956 void
3957 Editor::set_fade_in_active (bool yn)
3958 {
3959         begin_reversible_command (_("set fade in active"));
3960
3961         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3962                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3963
3964                 if (!tmp) {
3965                         return;
3966                 }
3967
3968
3969                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3970
3971                 XMLNode &before = ar->get_state();
3972
3973                 ar->set_fade_in_active (yn);
3974                 
3975                 XMLNode &after = ar->get_state();
3976                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3977         }
3978 }
3979
3980 void
3981 Editor::set_fade_out_active (bool yn)
3982 {
3983         begin_reversible_command (_("set fade out active"));
3984
3985         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3986                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3987
3988                 if (!tmp) {
3989                         return;
3990                 }
3991
3992                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3993
3994                 XMLNode &before = ar->get_state();
3995
3996                 ar->set_fade_out_active (yn);
3997                 
3998                 XMLNode &after = ar->get_state();
3999                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4000         }
4001 }
4002
4003
4004 /** Update crossfade visibility after its configuration has been changed */
4005 void
4006 Editor::update_xfade_visibility ()
4007 {
4008         _xfade_visibility = Config->get_xfades_visible ();
4009         
4010         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4011                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4012                 if (v) {
4013                         if (_xfade_visibility) {
4014                                 v->show_all_xfades ();
4015                         } else {
4016                                 v->hide_all_xfades ();
4017                         }
4018                 }
4019         }
4020 }
4021
4022 void
4023 Editor::set_edit_point ()
4024 {
4025         nframes64_t where;
4026         bool ignored;
4027
4028         if (!mouse_frame (where, ignored)) {
4029                 return;
4030         }
4031         
4032         snap_to (where);
4033
4034         if (selection->markers.empty()) {
4035                 
4036                 mouse_add_new_marker (where);
4037
4038         } else {
4039                 bool ignored;
4040
4041                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4042
4043                 if (loc) {
4044                         loc->move_to (where);
4045                 }
4046         }
4047 }
4048
4049 void
4050 Editor::set_playhead_cursor ()
4051 {
4052         if (entered_marker) {
4053                 session->request_locate (entered_marker->position(), session->transport_rolling());
4054         } else {
4055                 nframes64_t where;
4056                 bool ignored;
4057
4058                 if (!mouse_frame (where, ignored)) {
4059                         return;
4060                 }
4061                         
4062                 snap_to (where);
4063                 
4064                 if (session) {
4065                         session->request_locate (where, session->transport_rolling());
4066                 }
4067         }
4068 }
4069
4070 void
4071 Editor::split ()
4072 {
4073         nframes64_t where = get_preferred_edit_position();
4074
4075         if (!selection->regions.empty()) {
4076                 
4077                 split_regions_at (where, selection->regions);
4078
4079         } else {
4080                 
4081                 RegionSelection rs;
4082                 rs = get_regions_at (where, selection->tracks);
4083                 split_regions_at (where, rs);
4084         }
4085 }