added "separate" as a new bindable editor operation; bound to F4 by default; fixed...
[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 || 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 || 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 || 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_regions_between (const TimeSelection& ts)
2220 {
2221         bool doing_undo = false;
2222         boost::shared_ptr<Playlist> playlist;
2223         RegionSelection new_selection;
2224                 
2225         sort_track_selection ();
2226
2227         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2228
2229                 AudioTimeAxisView* atv;
2230
2231                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2232
2233                         if (atv->is_audio_track()) {
2234
2235                                 /* no edits to destructive tracks */
2236
2237                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2238                                         continue;
2239                                 }
2240                                         
2241                                 if ((playlist = atv->playlist()) != 0) {
2242
2243                                         if (!doing_undo) {
2244                                                 begin_reversible_command (_("separate"));
2245                                                 doing_undo = true;
2246                                         }
2247
2248                                         XMLNode *before;
2249
2250                                         if (doing_undo) {
2251                                             before = &(playlist->get_state());
2252                                         }
2253
2254                                         /* XXX need to consider musical time selections here at some point */
2255
2256                                         double speed = atv->get_diskstream()->speed();
2257
2258                                         sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2259                                         latest_regionviews.clear ();
2260
2261                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2262                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2263                                         }
2264
2265                                         c.disconnect ();
2266
2267                                         if (!latest_regionviews.empty()) {
2268
2269                                                 /* here is a trick: partitioning will generally create 1 or 2 new regions.
2270                                                    if the region spanned the selection, the first new one will be the middle
2271                                                    of the original. if the region overlapped one end of the selection,
2272                                                    the first (and only) new one will be the area corresponding to the
2273                                                    selection.
2274
2275                                                    we want to select this region and return to mouse object mode ...
2276                                                 */
2277
2278                                                 new_selection.push_back (latest_regionviews.front());
2279
2280                                                 if (doing_undo) {
2281                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2282                                                 }
2283                                         } else {
2284                                                 cerr << " no new rv's for " << playlist->name() << endl;
2285                                         }
2286                                 }
2287                         }
2288                 }
2289         }
2290
2291         selection->set (new_selection);
2292         set_mouse_mode (MouseObject);
2293
2294         if (doing_undo) {
2295                 commit_reversible_command ();
2296         }
2297 }
2298
2299 void
2300 Editor::separate_region_from_selection ()
2301 {
2302         /* preferentially use *all* ranges in the time selection if we're in range mode
2303            to allow discontiguous operation, since get_edit_op_range() currently
2304            returns a single range.
2305         */
2306         if (mouse_mode == MouseRange && !selection->time.empty()) {
2307
2308                 separate_regions_between (selection->time);
2309
2310         } else {
2311
2312                 nframes64_t start;
2313                 nframes64_t end;
2314                 
2315                 if (get_edit_op_range (start, end)) {
2316                         
2317                         AudioRange ar (start, end, 1);
2318                         TimeSelection ts;
2319                         ts.push_back (ar);
2320
2321                         /* force track selection */
2322
2323                         ensure_entered_selected ();
2324                         
2325                         separate_regions_between (ts);
2326                 }
2327         }
2328 }
2329
2330 void
2331 Editor::separate_regions_using_location (Location& loc)
2332 {
2333         if (loc.is_mark()) {
2334                 return;
2335         }
2336
2337         AudioRange ar (loc.start(), loc.end(), 1);
2338         TimeSelection ts;
2339
2340         ts.push_back (ar);
2341
2342         separate_regions_between (ts);
2343 }
2344
2345 void
2346 Editor::crop_region_to_selection ()
2347 {
2348         ensure_entered_selected ();
2349
2350         if (!selection->time.empty()) {
2351
2352                 crop_region_to (selection->time.start(), selection->time.end_frame());
2353
2354         } else if (_edit_point != EditAtPlayhead) {
2355
2356                 nframes64_t start;
2357                 nframes64_t end;
2358
2359                 if (get_edit_op_range (start, end)) {
2360                         crop_region_to (start, end);
2361                 }
2362         }
2363                 
2364 }               
2365
2366 void
2367 Editor::crop_region_to (nframes_t start, nframes_t end)
2368 {
2369         vector<boost::shared_ptr<Playlist> > playlists;
2370         boost::shared_ptr<Playlist> playlist;
2371         TrackSelection* ts;
2372
2373         ensure_entered_selected ();
2374
2375         if (selection->tracks.empty()) {
2376                 ts = &track_views;
2377         } else {
2378                 sort_track_selection ();
2379                 ts = &selection->tracks;
2380         }
2381         
2382         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
2383                 
2384                 AudioTimeAxisView* atv;
2385                 
2386                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2387                         
2388                         if (atv->is_audio_track()) {
2389                                 
2390                                 /* no edits to destructive tracks */
2391
2392                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2393                                         continue;
2394                                 }
2395
2396                                 if ((playlist = atv->playlist()) != 0) {
2397                                         playlists.push_back (playlist);
2398                                 }
2399                         }
2400                 }
2401         }
2402
2403         if (playlists.empty()) {
2404                 return;
2405         }
2406                 
2407         nframes_t the_start;
2408         nframes_t the_end;
2409         nframes_t cnt;
2410         
2411         begin_reversible_command (_("trim to selection"));
2412         
2413         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2414                 
2415                 boost::shared_ptr<Region> region;
2416         
2417                 the_start = start;
2418         
2419                 if ((region = (*i)->top_region_at(the_start)) == 0) {
2420                         continue;
2421                 }
2422                 
2423                 /* now adjust lengths to that we do the right thing
2424                    if the selection extends beyond the region
2425                 */
2426                 
2427                 the_start = max (the_start, region->position());
2428                 if (max_frames - the_start < region->length()) {
2429                         the_end = the_start + region->length() - 1;
2430                 } else {
2431                         the_end = max_frames;
2432                 }
2433                 the_end = min (end, the_end);
2434                 cnt = the_end - the_start + 1;
2435                 
2436                 XMLNode &before = (*i)->get_state();
2437                 region->trim_to (the_start, cnt, this);
2438                 XMLNode &after = (*i)->get_state();
2439                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2440         }
2441         
2442         commit_reversible_command ();
2443 }               
2444
2445 void
2446 Editor::region_fill_track ()
2447 {
2448         nframes_t end;
2449
2450         if (!session || selection->regions.empty()) {
2451                 return;
2452         }
2453
2454         end = session->current_end_frame ();
2455
2456         begin_reversible_command (_("region fill"));
2457
2458         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2459
2460                 boost::shared_ptr<Region> region ((*i)->region());
2461                 
2462                 // FIXME
2463                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2464                 if (!ar)
2465                         continue;
2466
2467                 boost::shared_ptr<Playlist> pl = region->playlist();
2468
2469                 if (end <= region->last_frame()) {
2470                         return;
2471                 }
2472
2473                 double times = (double) (end - region->last_frame()) / (double) region->length();
2474
2475                 if (times == 0) {
2476                         return;
2477                 }
2478
2479                 XMLNode &before = pl->get_state();
2480                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2481                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2482         }
2483
2484         commit_reversible_command ();
2485 }
2486
2487 void
2488 Editor::region_fill_selection ()
2489 {
2490         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2491                 return;
2492         }
2493
2494         if (selection->time.empty()) {
2495                 return;
2496         }
2497
2498
2499         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2500
2501         if (selected->count_selected_rows() != 1) {
2502                 return;
2503         }
2504
2505         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2506         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2507
2508         nframes_t start = selection->time[clicked_selection].start;
2509         nframes_t end = selection->time[clicked_selection].end;
2510
2511         boost::shared_ptr<Playlist> playlist; 
2512
2513         if (selection->tracks.empty()) {
2514                 return;
2515         }
2516
2517         nframes_t selection_length = end - start;
2518         float times = (float)selection_length / region->length();
2519         
2520         begin_reversible_command (_("fill selection"));
2521         
2522         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2523
2524                 if ((playlist = (*i)->playlist()) == 0) {
2525                         continue;
2526                 }               
2527                 
2528                 XMLNode &before = playlist->get_state();
2529                 playlist->add_region (RegionFactory::create (region), start, times);
2530                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2531         }
2532         
2533         commit_reversible_command ();                   
2534 }
2535
2536 void
2537 Editor::set_a_regions_sync_position (boost::shared_ptr<Region> region, nframes_t position)
2538 {
2539
2540         if (!region->covers (position)) {
2541           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2542                 return;
2543         }
2544         begin_reversible_command (_("set region sync position"));
2545         XMLNode &before = region->playlist()->get_state();
2546         region->set_sync_position (position);
2547         XMLNode &after = region->playlist()->get_state();
2548         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2549         commit_reversible_command ();
2550 }
2551
2552 void
2553 Editor::set_region_sync_from_edit_point ()
2554 {
2555         if (clicked_regionview == 0) {
2556                 return;
2557         }
2558
2559         if (!clicked_regionview->region()->covers (get_preferred_edit_position())) {
2560                 error << _("Place the edit point at the desired sync point") << endmsg;
2561                 return;
2562         }
2563
2564         boost::shared_ptr<Region> region (clicked_regionview->region());
2565         begin_reversible_command (_("set sync from edit point"));
2566         XMLNode &before = region->playlist()->get_state();
2567         region->set_sync_position (get_preferred_edit_position());
2568         XMLNode &after = region->playlist()->get_state();
2569         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2570         commit_reversible_command ();
2571 }
2572
2573 void
2574 Editor::remove_region_sync ()
2575 {
2576         if (clicked_regionview) {
2577                 boost::shared_ptr<Region> region (clicked_regionview->region());
2578                 begin_reversible_command (_("remove sync"));
2579                 XMLNode &before = region->playlist()->get_state();
2580                 region->clear_sync_position ();
2581                 XMLNode &after = region->playlist()->get_state();
2582                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2583                 commit_reversible_command ();
2584         }
2585 }
2586
2587 void
2588 Editor::naturalize ()
2589 {
2590         if (selection->regions.empty()) {
2591                 return;
2592         }
2593         begin_reversible_command (_("naturalize"));
2594         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2595                 XMLNode &before = (*i)->region()->get_state();
2596                 (*i)->region()->move_to_natural_position (this);
2597                 XMLNode &after = (*i)->region()->get_state();
2598                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2599         }
2600         commit_reversible_command ();
2601 }
2602
2603 void
2604 Editor::align (RegionPoint what)
2605 {
2606         ensure_entered_selected ();
2607
2608         nframes64_t where = get_preferred_edit_position();
2609
2610         if (!selection->regions.empty()) {
2611                 align_selection (what, where, selection->regions);
2612         } else {
2613
2614                 RegionSelection rs;
2615                 rs = get_regions_at (where, selection->tracks);
2616                 align_selection (what, where, rs);
2617         }
2618 }
2619
2620 void
2621 Editor::align_relative (RegionPoint what)
2622 {
2623         nframes64_t where = get_preferred_edit_position();
2624
2625         if (!selection->regions.empty()) {
2626                 align_selection_relative (what, where, selection->regions);
2627         } else {
2628
2629                 RegionSelection rs;
2630                 rs = get_regions_at (where, selection->tracks);
2631                 align_selection_relative (what, where, rs);
2632         }
2633 }
2634
2635 struct RegionSortByTime {
2636     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2637             return a->region()->position() < b->region()->position();
2638     }
2639 };
2640
2641 void
2642 Editor::align_selection_relative (RegionPoint point, nframes_t position, const RegionSelection& rs)
2643 {
2644         if (rs.empty()) {
2645                 return;
2646         }
2647
2648         nframes_t distance;
2649         nframes_t pos = 0;
2650         int dir;
2651
2652         list<RegionView*> sorted;
2653         rs.by_position (sorted);
2654         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2655
2656         switch (point) {
2657         case Start:
2658                 pos = r->first_frame ();
2659                 break;
2660
2661         case End:
2662                 pos = r->last_frame();
2663                 break;
2664
2665         case SyncPoint:
2666                 pos = r->adjust_to_sync (r->first_frame());
2667                 break;  
2668         }
2669
2670         if (pos > position) {
2671                 distance = pos - position;
2672                 dir = -1;
2673         } else {
2674                 distance = position - pos;
2675                 dir = 1;
2676         }
2677
2678         begin_reversible_command (_("align selection (relative)"));
2679
2680         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
2681
2682                 boost::shared_ptr<Region> region ((*i)->region());
2683
2684                 XMLNode &before = region->playlist()->get_state();
2685                 
2686                 if (dir > 0) {
2687                         region->set_position (region->position() + distance, this);
2688                 } else {
2689                         region->set_position (region->position() - distance, this);
2690                 }
2691
2692                 XMLNode &after = region->playlist()->get_state();
2693                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2694
2695         }
2696
2697         commit_reversible_command ();
2698 }
2699
2700 void
2701 Editor::align_selection (RegionPoint point, nframes_t position, const RegionSelection& rs)
2702 {
2703         if (rs.empty()) {
2704                 return;
2705         }
2706
2707         begin_reversible_command (_("align selection"));
2708
2709         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
2710                 align_region_internal ((*i)->region(), point, position);
2711         }
2712
2713         commit_reversible_command ();
2714 }
2715
2716 void
2717 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2718 {
2719         begin_reversible_command (_("align region"));
2720         align_region_internal (region, point, position);
2721         commit_reversible_command ();
2722 }
2723
2724 void
2725 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2726 {
2727         XMLNode &before = region->playlist()->get_state();
2728
2729         switch (point) {
2730         case SyncPoint:
2731                 region->set_position (region->adjust_to_sync (position), this);
2732                 break;
2733
2734         case End:
2735                 if (position > region->length()) {
2736                         region->set_position (position - region->length(), this);
2737                 }
2738                 break;
2739
2740         case Start:
2741                 region->set_position (position, this);
2742                 break;
2743         }
2744
2745         XMLNode &after = region->playlist()->get_state();
2746         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2747 }       
2748
2749 void
2750 Editor::trim_region_to_loop ()
2751 {
2752         Location* loc = session->locations()->auto_loop_location();
2753         if (!loc) {
2754                 return;
2755         }
2756         trim_region_to_location (*loc, _("trim to loop"));
2757 }
2758
2759 void
2760 Editor::trim_region_to_punch ()
2761 {
2762         Location* loc = session->locations()->auto_punch_location();
2763         if (!loc) {
2764                 return;
2765         }
2766         trim_region_to_location (*loc, _("trim to punch"));
2767 }
2768
2769 void
2770 Editor::trim_region_to_location (const Location& loc, const char* str)
2771 {
2772         ensure_entered_selected ();
2773
2774         RegionSelection& rs (get_regions_for_action ());
2775
2776         begin_reversible_command (str);
2777
2778         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2779                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2780
2781                 if (!arv) {
2782                         continue;
2783                 }
2784
2785                 /* require region to span proposed trim */
2786
2787                 switch (arv->region()->coverage (loc.start(), loc.end())) {
2788                 case OverlapInternal:
2789                         break;
2790                 default:
2791                         continue;
2792                 }
2793                                 
2794                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2795
2796                 if (!atav) {
2797                         return;
2798                 }
2799
2800                 float speed = 1.0;
2801                 nframes_t start;
2802                 nframes_t end;
2803
2804                 if (atav->get_diskstream() != 0) {
2805                         speed = atav->get_diskstream()->speed();
2806                 }
2807
2808                 start = session_frame_to_track_frame (loc.start(), speed);
2809                 end = session_frame_to_track_frame (loc.end(), speed);
2810
2811                 XMLNode &before = arv->region()->playlist()->get_state();
2812                 arv->region()->trim_to (start, (end - start), this);
2813                 XMLNode &after = arv->region()->playlist()->get_state();
2814                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2815         }
2816                 
2817         commit_reversible_command ();
2818 }
2819
2820 void
2821 Editor::trim_region_to_edit_point ()
2822 {
2823         RegionSelection& rs (get_regions_for_action ());
2824         nframes64_t where = get_preferred_edit_position();
2825
2826         begin_reversible_command (_("trim region start to edit point"));
2827
2828         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2829                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2830
2831                 if (!arv) {
2832                         continue;
2833                 }
2834
2835                 /* require region to cover trim */
2836
2837                 if (!arv->region()->covers (where)) {
2838                         continue;
2839                 }
2840
2841                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2842
2843                 if (!atav) {
2844                         return;
2845                 }
2846
2847                 float speed = 1.0;
2848
2849                 if (atav->get_diskstream() != 0) {
2850                         speed = atav->get_diskstream()->speed();
2851                 }
2852
2853                 XMLNode &before = arv->region()->playlist()->get_state();
2854                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
2855                 XMLNode &after = arv->region()->playlist()->get_state();
2856                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2857         }
2858                 
2859         commit_reversible_command ();
2860 }
2861
2862 void
2863 Editor::trim_region_from_edit_point ()
2864 {
2865         RegionSelection& rs (get_regions_for_action ());
2866         nframes64_t where = get_preferred_edit_position();
2867
2868         begin_reversible_command (_("trim region end to edit point"));
2869
2870         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
2871                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
2872
2873                 if (!arv) {
2874                         continue;
2875                 }
2876
2877                 /* require region to cover trim */
2878
2879                 if (!arv->region()->covers (where)) {
2880                         continue;
2881                 }
2882
2883                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
2884
2885                 if (!atav) {
2886                         return;
2887                 }
2888
2889                 float speed = 1.0;
2890
2891                 if (atav->get_diskstream() != 0) {
2892                         speed = atav->get_diskstream()->speed();
2893                 }
2894
2895                 XMLNode &before = arv->region()->playlist()->get_state();
2896                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
2897                 XMLNode &after = arv->region()->playlist()->get_state();
2898                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
2899         }
2900                 
2901         commit_reversible_command ();
2902 }
2903
2904 void
2905 Editor::unfreeze_route ()
2906 {
2907         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2908                 return;
2909         }
2910         
2911         clicked_audio_trackview->audio_track()->unfreeze ();
2912 }
2913
2914 void*
2915 Editor::_freeze_thread (void* arg)
2916 {
2917         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2918         return static_cast<Editor*>(arg)->freeze_thread ();
2919 }
2920
2921 void*
2922 Editor::freeze_thread ()
2923 {
2924         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2925         return 0;
2926 }
2927
2928 gint
2929 Editor::freeze_progress_timeout (void *arg)
2930 {
2931         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2932         return !(current_interthread_info->done || current_interthread_info->cancel);
2933 }
2934
2935 void
2936 Editor::freeze_route ()
2937 {
2938         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2939                 return;
2940         }
2941         
2942         InterThreadInfo itt;
2943
2944         if (interthread_progress_window == 0) {
2945                 build_interthread_progress_window ();
2946         }
2947
2948         WindowTitle title(Glib::get_application_name());
2949         title += _("Freeze");
2950         interthread_progress_window->set_title (title.get_string());
2951         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2952         interthread_progress_window->show_all ();
2953         interthread_progress_bar.set_fraction (0.0f);
2954         interthread_progress_label.set_text ("");
2955         interthread_cancel_label.set_text (_("Cancel Freeze"));
2956         current_interthread_info = &itt;
2957
2958         interthread_progress_connection = 
2959           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2960
2961         itt.done = false;
2962         itt.cancel = false;
2963         itt.progress = 0.0f;
2964         
2965         pthread_attr_t attr;
2966         pthread_attr_init(&attr);
2967         pthread_attr_setstacksize(&attr, 500000);
2968
2969         pthread_create (&itt.thread, &attr, _freeze_thread, this);
2970
2971         pthread_attr_destroy(&attr);
2972
2973         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2974
2975         while (!itt.done && !itt.cancel) {
2976                 gtk_main_iteration ();
2977         }
2978
2979         interthread_progress_connection.disconnect ();
2980         interthread_progress_window->hide_all ();
2981         current_interthread_info = 0;
2982         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2983 }
2984
2985 void
2986 Editor::bounce_range_selection ()
2987 {
2988         if (selection->time.empty()) {
2989                 return;
2990         }
2991
2992         TrackSelection views = selection->tracks;
2993
2994         nframes_t start = selection->time[clicked_selection].start;
2995         nframes_t end = selection->time[clicked_selection].end;
2996         nframes_t cnt = end - start + 1;
2997
2998         begin_reversible_command (_("bounce range"));
2999
3000         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3001
3002                 AudioTimeAxisView* atv;
3003
3004                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
3005                         continue;
3006                 }
3007                 
3008                 boost::shared_ptr<Playlist> playlist;
3009                 
3010                 if ((playlist = atv->playlist()) == 0) {
3011                         return;
3012                 }
3013
3014                 InterThreadInfo itt;
3015                 
3016                 itt.done = false;
3017                 itt.cancel = false;
3018                 itt.progress = false;
3019
3020                 XMLNode &before = playlist->get_state();
3021                 atv->audio_track()->bounce_range (start, cnt, itt);
3022                 XMLNode &after = playlist->get_state();
3023                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3024         }
3025         
3026         commit_reversible_command ();
3027 }
3028
3029 void
3030 Editor::cut ()
3031 {
3032         cut_copy (Cut);
3033 }
3034
3035 void
3036 Editor::copy ()
3037 {
3038         cut_copy (Copy);
3039 }
3040
3041 void 
3042 Editor::cut_copy (CutCopyOp op)
3043 {
3044         /* only cancel selection if cut/copy is successful.*/
3045
3046         string opname;
3047
3048         switch (op) {
3049         case Cut:
3050                 opname = _("cut");
3051                 break;
3052         case Copy:
3053                 opname = _("copy");
3054                 break;
3055         case Clear:
3056                 opname = _("clear");
3057                 break;
3058         }
3059         
3060         cut_buffer->clear ();
3061
3062         if (entered_marker) {
3063
3064                 /* cut/delete op while pointing at a marker */
3065
3066                 bool ignored;
3067                 Location* loc = find_location_from_marker (entered_marker, ignored);
3068
3069                 if (session && loc) {
3070                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3071                 }
3072
3073                 return;
3074         }
3075
3076         switch (current_mouse_mode()) {
3077         case MouseObject: 
3078                 if (!selection->regions.empty() || !selection->points.empty()) {
3079
3080                         begin_reversible_command (opname + _(" objects"));
3081
3082                         if (!selection->regions.empty()) {
3083                                 
3084                                 cut_copy_regions (op);
3085                                 
3086                                 if (op == Cut) {
3087                                         selection->clear_regions ();
3088                                 }
3089                         }
3090
3091                         if (!selection->points.empty()) {
3092                                 cut_copy_points (op);
3093
3094                                 if (op == Cut) {
3095                                         selection->clear_points ();
3096                                 }
3097                         }
3098
3099                         commit_reversible_command ();   
3100                         break; // terminate case statement here
3101                 } 
3102                 if (!selection->time.empty()) {
3103                         /* don't cause suprises */
3104                         break;
3105                 }
3106                 // fall thru if there was nothing selected
3107                 
3108         case MouseRange:
3109                 if (selection->time.empty()) {
3110                         nframes64_t start, end;
3111                         if (!get_edit_op_range (start, end)) {
3112                                 return;
3113                         }
3114                         selection->set (0, start, end);
3115                 }
3116                         
3117                 begin_reversible_command (opname + _(" range"));
3118                 cut_copy_ranges (op);
3119                 commit_reversible_command ();
3120                 
3121                 if (op == Cut) {
3122                         selection->clear_time ();
3123                 }
3124
3125                 break;
3126                 
3127         default:
3128                 break;
3129         }
3130 }
3131
3132 void
3133 Editor::cut_copy_points (CutCopyOp op)
3134 {
3135         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3136
3137                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3138
3139                 if (atv) {
3140                         atv->cut_copy_clear_objects (selection->points, op);
3141                 } 
3142         }
3143 }
3144
3145 struct PlaylistState {
3146     boost::shared_ptr<Playlist> playlist;
3147     XMLNode*  before;
3148 };
3149
3150 struct lt_playlist {
3151     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3152             return a.playlist < b.playlist;
3153     }
3154 };
3155         
3156 struct PlaylistMapping { 
3157     TimeAxisView* tv;
3158     boost::shared_ptr<AudioPlaylist> pl;
3159
3160     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3161 };
3162
3163 void
3164 Editor::cut_copy_regions (CutCopyOp op)
3165 {
3166         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3167            a map when we want ordered access to both elements. i think.
3168         */
3169
3170         vector<PlaylistMapping> pmap;
3171
3172         nframes_t first_position = max_frames;
3173         
3174         set<PlaylistState, lt_playlist> freezelist;
3175         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3176         
3177         /* get ordering correct before we cut/copy */
3178         
3179         selection->regions.sort_by_position_and_track ();
3180
3181         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3182
3183                 first_position = min ((*x)->region()->position(), first_position);
3184
3185                 if (op == Cut || op == Clear) {
3186                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3187
3188                         if (pl) {
3189
3190                                 PlaylistState before;
3191                                 before.playlist = pl;
3192                                 before.before = &pl->get_state();
3193                                 
3194                                 insert_result = freezelist.insert (before);
3195                                 
3196                                 if (insert_result.second) {
3197                                         pl->freeze ();
3198                                 }
3199                         }
3200                 }
3201
3202                 TimeAxisView* tv = &(*x)->get_trackview();
3203                 vector<PlaylistMapping>::iterator z;
3204
3205                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3206                         if ((*z).tv == tv) {
3207                                 break;
3208                         }
3209                 }
3210                 
3211                 if (z == pmap.end()) {
3212                         pmap.push_back (PlaylistMapping (tv));
3213                 }
3214         }
3215
3216         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3217
3218                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3219                 
3220                 if (!pl) {
3221                         /* impossible, but this handles it for the future */
3222                         continue;
3223                 }
3224
3225                 TimeAxisView& tv = (*x)->get_trackview();
3226                 boost::shared_ptr<AudioPlaylist> npl;
3227                 RegionSelection::iterator tmp;
3228                 
3229                 tmp = x;
3230                 ++tmp;
3231
3232                 vector<PlaylistMapping>::iterator z;
3233                 
3234                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3235                         if ((*z).tv == &tv) {
3236                                 break;
3237                         }
3238                 }
3239                 
3240                 assert (z != pmap.end());
3241                 
3242                 if (!(*z).pl) {
3243                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3244                         npl->freeze();
3245                         (*z).pl = npl;
3246                 } else {
3247                         npl = (*z).pl;
3248                 }
3249                 
3250                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3251                 boost::shared_ptr<Region> _xx;
3252                 
3253                 switch (op) {
3254                 case Cut:
3255                         if (!ar) break;
3256                         
3257                         _xx = RegionFactory::create ((*x)->region());
3258                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3259                         pl->remove_region (((*x)->region()));
3260                         break;
3261                         
3262                 case Copy:
3263                         if (!ar) break;
3264
3265                         /* copy region before adding, so we're not putting same object into two different playlists */
3266                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3267                         break;
3268                         
3269                 case Clear:
3270                         pl->remove_region (((*x)->region()));
3271                         break;
3272                 }
3273
3274                 x = tmp;
3275         }
3276         
3277         list<boost::shared_ptr<Playlist> > foo;
3278         
3279         /* the pmap is in the same order as the tracks in which selected regions occured */
3280         
3281         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3282                 (*i).pl->thaw();
3283                 foo.push_back ((*i).pl);
3284         }
3285         
3286
3287         if (!foo.empty()) {
3288                 cut_buffer->set (foo);
3289         }
3290
3291         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3292                 (*pl).playlist->thaw ();
3293                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3294         }
3295 }
3296
3297 void
3298 Editor::cut_copy_ranges (CutCopyOp op)
3299 {
3300         TrackSelection* ts;
3301
3302         if (selection->tracks.empty()) {
3303                 ts = &track_views;
3304         } else {
3305                 ts = &selection->tracks;
3306         }
3307
3308         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3309                 (*i)->cut_copy_clear (*selection, op);
3310         }
3311 }
3312
3313 void
3314 Editor::paste (float times)
3315 {
3316         paste_internal (get_preferred_edit_position(), times);
3317 }
3318
3319 void
3320 Editor::mouse_paste ()
3321 {
3322         nframes64_t where;
3323         bool ignored;
3324
3325         if (!mouse_frame (where, ignored)) {
3326                 return;
3327         }
3328
3329         snap_to (where);
3330         paste_internal (where, 1);
3331 }
3332
3333 void
3334 Editor::paste_internal (nframes_t position, float times)
3335 {
3336         bool commit = false;
3337
3338         if (cut_buffer->empty() || selection->tracks.empty()) {
3339                 return;
3340         }
3341
3342         if (position == max_frames) {
3343                 position = get_preferred_edit_position();
3344         }
3345
3346         begin_reversible_command (_("paste"));
3347
3348         TrackSelection::iterator i;
3349         size_t nth;
3350
3351         /* get everything in the correct order */
3352
3353         sort_track_selection ();
3354
3355         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3356
3357                 /* undo/redo is handled by individual tracks */
3358
3359                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3360                         commit = true;
3361                 }
3362         }
3363         
3364         if (commit) {
3365                 commit_reversible_command ();
3366         }
3367 }
3368
3369 void
3370 Editor::paste_named_selection (float times)
3371 {
3372         TrackSelection::iterator t;
3373
3374         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3375
3376         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3377                 return;
3378         }
3379
3380         TreeModel::iterator i = selected->get_selected();
3381         NamedSelection* ns = (*i)[named_selection_columns.selection];
3382
3383         list<boost::shared_ptr<Playlist> >::iterator chunk;
3384         list<boost::shared_ptr<Playlist> >::iterator tmp;
3385
3386         chunk = ns->playlists.begin();
3387                 
3388         begin_reversible_command (_("paste chunk"));
3389         
3390         sort_track_selection ();
3391
3392         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3393                 
3394                 AudioTimeAxisView* atv;
3395                 boost::shared_ptr<Playlist> pl;
3396                 boost::shared_ptr<AudioPlaylist> apl;
3397
3398                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3399                         continue;
3400                 }
3401
3402                 if ((pl = atv->playlist()) == 0) {
3403                         continue;
3404                 }
3405                 
3406                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3407                         continue;
3408                 }
3409
3410                 tmp = chunk;
3411                 ++tmp;
3412
3413                 XMLNode &before = apl->get_state();
3414                 apl->paste (*chunk, get_preferred_edit_position(), times);
3415                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3416
3417                 if (tmp != ns->playlists.end()) {
3418                         chunk = tmp;
3419                 }
3420         }
3421
3422         commit_reversible_command();
3423 }
3424
3425 void
3426 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3427 {
3428         boost::shared_ptr<Playlist> playlist; 
3429         RegionSelection sel = regions; // clear (below) will clear the argument list
3430                 
3431         begin_reversible_command (_("duplicate region"));
3432
3433         selection->clear_regions ();
3434
3435         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3436
3437                 boost::shared_ptr<Region> r ((*i)->region());
3438
3439                 TimeAxisView& tv = (*i)->get_time_axis_view();
3440                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3441                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3442                 
3443                 playlist = (*i)->region()->playlist();
3444                 XMLNode &before = playlist->get_state();
3445                 playlist->duplicate (r, r->last_frame() + 1, times);
3446                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3447
3448                 c.disconnect ();
3449
3450                 if (!latest_regionviews.empty()) {
3451                         selection->add (latest_regionviews);
3452                 }
3453         }
3454                 
3455
3456         commit_reversible_command ();
3457 }
3458
3459 void
3460 Editor::duplicate_selection (float times)
3461 {
3462         if (selection->time.empty() || selection->tracks.empty()) {
3463                 return;
3464         }
3465
3466         boost::shared_ptr<Playlist> playlist; 
3467         vector<boost::shared_ptr<AudioRegion> > new_regions;
3468         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3469                 
3470         create_region_from_selection (new_regions);
3471
3472         if (new_regions.empty()) {
3473                 return;
3474         }
3475         
3476         begin_reversible_command (_("duplicate selection"));
3477
3478         ri = new_regions.begin();
3479
3480         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3481                 if ((playlist = (*i)->playlist()) == 0) {
3482                         continue;
3483                 }
3484                 XMLNode &before = playlist->get_state();
3485                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3486                 XMLNode &after = playlist->get_state();
3487                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3488
3489                 ++ri;
3490                 if (ri == new_regions.end()) {
3491                         --ri;
3492                 }
3493         }
3494
3495         commit_reversible_command ();
3496 }
3497
3498 void
3499 Editor::reset_point_selection ()
3500 {
3501         /* reset all selected points to the relevant default value */
3502
3503         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3504                 
3505                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3506                 
3507                 if (atv) {
3508                         atv->reset_objects (selection->points);
3509                 } 
3510         }
3511 }
3512
3513 void
3514 Editor::center_playhead ()
3515 {
3516         float page = canvas_width * frames_per_unit;
3517         center_screen_internal (playhead_cursor->current_frame, page);
3518 }
3519
3520 void
3521 Editor::center_edit_point ()
3522 {
3523         float page = canvas_width * frames_per_unit;
3524         center_screen_internal (get_preferred_edit_position(), page);
3525 }
3526
3527 void
3528 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3529 {
3530         begin_reversible_command (_("clear playlist"));
3531         XMLNode &before = playlist->get_state();
3532         playlist->clear ();
3533         XMLNode &after = playlist->get_state();
3534         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3535         commit_reversible_command ();
3536 }
3537
3538 void
3539 Editor::nudge_track (bool use_edit, bool forwards)
3540 {
3541         boost::shared_ptr<Playlist> playlist; 
3542         nframes_t distance;
3543         nframes_t next_distance;
3544         nframes_t start;
3545
3546         if (use_edit) {
3547                 start = get_preferred_edit_position();
3548         } else {
3549                 start = 0;
3550         }
3551
3552         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3553                 return;
3554         }
3555         
3556         if (selection->tracks.empty()) {
3557                 return;
3558         }
3559         
3560         begin_reversible_command (_("nudge track"));
3561         
3562         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3563
3564                 if ((playlist = (*i)->playlist()) == 0) {
3565                         continue;
3566                 }               
3567                 
3568                 XMLNode &before = playlist->get_state();
3569                 playlist->nudge_after (start, distance, forwards);
3570                 XMLNode &after = playlist->get_state();
3571                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3572         }
3573         
3574         commit_reversible_command ();                   
3575 }
3576
3577 void
3578 Editor::remove_last_capture ()
3579 {
3580         vector<string> choices;
3581         string prompt;
3582         
3583         if (!session) {
3584                 return;
3585         }
3586
3587         if (Config->get_verify_remove_last_capture()) {
3588                 prompt  = _("Do you really want to destroy the last capture?"
3589                             "\n(This is destructive and cannot be undone)");
3590
3591                 choices.push_back (_("No, do nothing."));
3592                 choices.push_back (_("Yes, destroy it."));
3593                 
3594                 Gtkmm2ext::Choice prompter (prompt, choices);
3595                 
3596                 if (prompter.run () == 1) {
3597                         session->remove_last_capture ();
3598                 }
3599
3600         } else {
3601                 session->remove_last_capture();
3602         }
3603 }
3604
3605 void
3606 Editor::normalize_region ()
3607 {
3608         if (!session) {
3609                 return;
3610         }
3611
3612         if (selection->regions.empty()) {
3613                 return;
3614         }
3615
3616         begin_reversible_command (_("normalize"));
3617
3618         track_canvas.get_window()->set_cursor (*wait_cursor);
3619         gdk_flush ();
3620
3621         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3622                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3623                 if (!arv)
3624                         continue;
3625                 XMLNode &before = arv->region()->get_state();
3626                 arv->audio_region()->normalize_to (0.0f);
3627                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3628         }
3629
3630         commit_reversible_command ();
3631         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3632 }
3633
3634
3635 void
3636 Editor::denormalize_region ()
3637 {
3638         if (!session) {
3639                 return;
3640         }
3641
3642         if (selection->regions.empty()) {
3643                 return;
3644         }
3645
3646         begin_reversible_command ("denormalize");
3647
3648         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3649                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3650                 if (!arv)
3651                         continue;
3652                 XMLNode &before = arv->region()->get_state();
3653                 arv->audio_region()->set_scale_amplitude (1.0f);
3654                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3655         }
3656
3657         commit_reversible_command ();
3658 }
3659
3660
3661 void
3662 Editor::reverse_region ()
3663 {
3664         if (!session) {
3665                 return;
3666         }
3667
3668         Reverse rev (*session);
3669         apply_filter (rev, _("reverse regions"));
3670 }
3671
3672 void
3673 Editor::apply_filter (AudioFilter& filter, string command)
3674 {
3675         if (selection->regions.empty()) {
3676                 return;
3677         }
3678
3679         begin_reversible_command (command);
3680
3681         track_canvas.get_window()->set_cursor (*wait_cursor);
3682         gdk_flush ();
3683
3684         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3685                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3686                 if (!arv)
3687                         continue;
3688
3689                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3690
3691                 RegionSelection::iterator tmp;
3692                 
3693                 tmp = r;
3694                 ++tmp;
3695
3696                 if (arv->audio_region()->apply (filter) == 0) {
3697
3698                         XMLNode &before = playlist->get_state();
3699                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3700                         XMLNode &after = playlist->get_state();
3701                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3702                 } else {
3703                         goto out;
3704                 }
3705
3706                 r = tmp;
3707         }
3708
3709         commit_reversible_command ();
3710         selection->regions.clear ();
3711
3712   out:
3713         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3714 }
3715
3716 void
3717 Editor::region_selection_op (void (Region::*pmf)(void))
3718 {
3719         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3720                 Region* region = (*i)->region().get();
3721                 (region->*pmf)();
3722         }
3723 }
3724
3725
3726 void
3727 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3728 {
3729         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3730                 Region* region = (*i)->region().get();
3731                 (region->*pmf)(arg);
3732         }
3733 }
3734
3735 void
3736 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3737 {
3738         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3739                 Region* region = (*i)->region().get();
3740                 (region->*pmf)(yn);
3741         }
3742 }
3743
3744 void
3745 Editor::external_edit_region ()
3746 {
3747         if (!clicked_regionview) {
3748                 return;
3749         }
3750
3751         /* more to come */
3752 }
3753
3754 void
3755 Editor::brush (nframes_t pos)
3756 {
3757         RegionSelection sel;
3758         snap_to (pos);
3759
3760         if (selection->regions.empty()) {
3761                 /* XXX get selection from region list */
3762         } else { 
3763                 sel = selection->regions;
3764         }
3765
3766         if (sel.empty()) {
3767                 return;
3768         }
3769
3770         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3771                 mouse_brush_insert_region ((*i), pos);
3772         }
3773 }
3774
3775 void
3776 Editor::reset_region_gain_envelopes ()
3777 {
3778         if (!session || selection->regions.empty()) {
3779                 return;
3780         }
3781
3782         session->begin_reversible_command (_("reset region gain"));
3783
3784         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3785                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3786                 if (arv) {
3787                         AutomationList& alist (arv->audio_region()->envelope());
3788                         XMLNode& before (alist.get_state());
3789
3790                         arv->audio_region()->set_default_envelope ();
3791                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3792                 }
3793         }
3794
3795         session->commit_reversible_command ();
3796 }
3797
3798 void
3799 Editor::toggle_gain_envelope_visibility ()
3800 {
3801         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3802                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3803                 if (arv) {
3804                         bool x = region_envelope_visible_item->get_active();
3805                         if (x != arv->envelope_visible()) {
3806                                 arv->set_envelope_visible (x);
3807                         }
3808                 }
3809         }
3810 }
3811
3812 void
3813 Editor::toggle_gain_envelope_active ()
3814 {
3815         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3816                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3817                 if (arv) {
3818                         bool x = region_envelope_active_item->get_active();
3819                         if (x != arv->audio_region()->envelope_active()) {
3820                                 arv->audio_region()->set_envelope_active (x);
3821                         }
3822                 }
3823         }
3824 }
3825
3826 void
3827 Editor::toggle_region_lock ()
3828 {
3829         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3830                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3831                 if (arv) {
3832                         bool x = region_lock_item->get_active();
3833                         if (x != arv->audio_region()->locked()) {
3834                                 arv->audio_region()->set_locked (x);
3835                         }
3836                 }
3837         }
3838 }
3839
3840 void
3841 Editor::toggle_region_mute ()
3842 {
3843         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3844                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3845                 if (arv) {
3846                         bool x = region_mute_item->get_active();
3847                         if (x != arv->audio_region()->muted()) {
3848                                 arv->audio_region()->set_muted (x);
3849                         }
3850                 }
3851         }
3852 }
3853
3854 void
3855 Editor::toggle_region_opaque ()
3856 {
3857         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3858                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3859                 if (arv) {
3860                         bool x = region_opaque_item->get_active();
3861                         if (x != arv->audio_region()->opaque()) {
3862                                 arv->audio_region()->set_opaque (x);
3863                         }
3864                 }
3865         }
3866 }
3867
3868 void
3869 Editor::set_fade_length (bool in)
3870 {
3871         ensure_entered_selected ();
3872
3873         /* we need a region to measure the offset from the start */
3874
3875         RegionView* rv;
3876
3877         if (entered_regionview) {
3878                 rv = entered_regionview;
3879         } else if (!selection->regions.empty()) {
3880                 rv = selection->regions.front();
3881         } else {
3882                 return;
3883         }
3884
3885         nframes64_t pos = get_preferred_edit_position();
3886         nframes_t len;
3887         char* cmd;
3888
3889         if (in) {
3890                 if (pos <= rv->region()->position()) {
3891                         /* can't do it */
3892                         return;
3893                 }
3894                 len = pos - rv->region()->position();
3895                 cmd = _("set fade in length");
3896         } else {
3897                 if (pos >= rv->region()->last_frame()) {
3898                         /* can't do it */
3899                         return;
3900                 }
3901                 len = rv->region()->last_frame() - pos;
3902                 cmd = _("set fade out length");
3903         }
3904
3905         begin_reversible_command (cmd);
3906
3907         RegionSelection& rs (get_regions_for_action());
3908
3909         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3910                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3911
3912                 if (!tmp) {
3913                         return;
3914                 }
3915
3916                 AutomationList& alist = tmp->audio_region()->fade_in();
3917                 XMLNode &before = alist.get_state();
3918
3919                 if (in) {
3920                         tmp->audio_region()->set_fade_in_length (len);
3921                 } else {
3922                         tmp->audio_region()->set_fade_out_length (len);
3923                 }
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_in_shape (AudioRegion::FadeShape shape)
3934 {
3935         begin_reversible_command (_("set fade in 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_in();
3945                 XMLNode &before = alist.get_state();
3946
3947                 tmp->audio_region()->set_fade_in_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_out_shape (AudioRegion::FadeShape shape)
3958 {
3959         begin_reversible_command (_("set fade out shape"));
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                 AutomationList& alist = tmp->audio_region()->fade_out();
3969                 XMLNode &before = alist.get_state();
3970
3971                 tmp->audio_region()->set_fade_out_shape (shape);
3972                 
3973                 XMLNode &after = alist.get_state();
3974                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3975         }
3976
3977         commit_reversible_command ();
3978 }
3979
3980 void
3981 Editor::set_fade_in_active (bool yn)
3982 {
3983         begin_reversible_command (_("set fade in 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
3993                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3994
3995                 XMLNode &before = ar->get_state();
3996
3997                 ar->set_fade_in_active (yn);
3998                 
3999                 XMLNode &after = ar->get_state();
4000                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4001         }
4002 }
4003
4004 void
4005 Editor::set_fade_out_active (bool yn)
4006 {
4007         begin_reversible_command (_("set fade out active"));
4008
4009         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
4010                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4011
4012                 if (!tmp) {
4013                         return;
4014                 }
4015
4016                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4017
4018                 XMLNode &before = ar->get_state();
4019
4020                 ar->set_fade_out_active (yn);
4021                 
4022                 XMLNode &after = ar->get_state();
4023                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4024         }
4025 }
4026
4027
4028 /** Update crossfade visibility after its configuration has been changed */
4029 void
4030 Editor::update_xfade_visibility ()
4031 {
4032         _xfade_visibility = Config->get_xfades_visible ();
4033         
4034         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4035                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4036                 if (v) {
4037                         if (_xfade_visibility) {
4038                                 v->show_all_xfades ();
4039                         } else {
4040                                 v->hide_all_xfades ();
4041                         }
4042                 }
4043         }
4044 }
4045
4046 void
4047 Editor::set_edit_point ()
4048 {
4049         nframes64_t where;
4050         bool ignored;
4051
4052         if (!mouse_frame (where, ignored)) {
4053                 return;
4054         }
4055         
4056         snap_to (where);
4057
4058         if (selection->markers.empty()) {
4059                 
4060                 mouse_add_new_marker (where);
4061
4062         } else {
4063                 bool ignored;
4064
4065                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
4066
4067                 if (loc) {
4068                         loc->move_to (where);
4069                 }
4070         }
4071 }
4072
4073 void
4074 Editor::set_playhead_cursor ()
4075 {
4076         if (entered_marker) {
4077                 session->request_locate (entered_marker->position(), session->transport_rolling());
4078         } else {
4079                 nframes64_t where;
4080                 bool ignored;
4081
4082                 if (!mouse_frame (where, ignored)) {
4083                         return;
4084                 }
4085                         
4086                 snap_to (where);
4087                 
4088                 if (session) {
4089                         session->request_locate (where, session->transport_rolling());
4090                 }
4091         }
4092 }
4093
4094 void
4095 Editor::split ()
4096 {
4097         ensure_entered_selected ();
4098
4099         nframes64_t where = get_preferred_edit_position();
4100
4101         if (!selection->regions.empty()) {
4102                 
4103                 split_regions_at (where, selection->regions);
4104
4105         } else {
4106                 
4107                 RegionSelection rs;
4108                 rs = get_regions_at (where, selection->tracks);
4109                 split_regions_at (where, rs);
4110         }
4111 }
4112
4113 void
4114 Editor::ensure_entered_selected ()
4115 {
4116         if (entered_regionview) {
4117                 if (find (selection->regions.begin(), selection->regions.end(), entered_regionview) == selection->regions.end()) {
4118                         
4119                         /* do NOT clear any existing track selection when we do this */
4120
4121                         selection->set (entered_regionview, false);
4122                 }
4123         }
4124 }