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