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