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