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