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