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