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