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