probably fix cleanup issues, but testing required
[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                 boost::shared_ptr<Region> _xx;
2815                 
2816                 switch (op) {
2817                 case Cut:
2818                         if (!ar) break;
2819                         
2820                         _xx = RegionFactory::create ((*x)->region());
2821                         npl->add_region (_xx, (*x)->region()->position() - first_position);
2822                         pl->remove_region (((*x)->region()));
2823                         break;
2824                         
2825                 case Copy:
2826                         if (!ar) break;
2827                         
2828                         npl->add_region ((*x)->region(), (*x)->region()->position() - first_position);
2829                         break;
2830                         
2831                 case Clear:
2832                         pl->remove_region (((*x)->region()));
2833                         break;
2834                 }
2835
2836                 x = tmp;
2837         }
2838         
2839         list<boost::shared_ptr<Playlist> > foo;
2840         
2841         /* the pmap is in the same order as the tracks in which selected regions occured */
2842         
2843         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
2844                 (*i).pl->thaw();
2845                 foo.push_back ((*i).pl);
2846         }
2847         
2848
2849         if (!foo.empty()) {
2850                 cut_buffer->set (foo);
2851         }
2852
2853         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
2854                 (*pl).playlist->thaw ();
2855                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2856         }
2857 }
2858
2859 void
2860 Editor::cut_copy_ranges (CutCopyOp op)
2861 {
2862         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2863                 (*i)->cut_copy_clear (*selection, op);
2864         }
2865 }
2866
2867 void
2868 Editor::paste (float times)
2869 {
2870         paste_internal (edit_cursor->current_frame, times);
2871 }
2872
2873 void
2874 Editor::mouse_paste ()
2875 {
2876         int x, y;
2877         double wx, wy;
2878
2879         track_canvas.get_pointer (x, y);
2880         track_canvas.window_to_world (x, y, wx, wy);
2881         wx += horizontal_adjustment.get_value();
2882         wy += vertical_adjustment.get_value();
2883
2884         GdkEvent event;
2885         event.type = GDK_BUTTON_RELEASE;
2886         event.button.x = wx;
2887         event.button.y = wy;
2888         
2889         nframes_t where = event_frame (&event, 0, 0);
2890         snap_to (where);
2891         paste_internal (where, 1);
2892 }
2893
2894 void
2895 Editor::paste_internal (nframes_t position, float times)
2896 {
2897         bool commit = false;
2898
2899         if (cut_buffer->empty() || selection->tracks.empty()) {
2900                 return;
2901         }
2902
2903         if (position == max_frames) {
2904                 position = edit_cursor->current_frame;
2905         }
2906
2907         begin_reversible_command (_("paste"));
2908
2909         TrackSelection::iterator i;
2910         size_t nth;
2911
2912         /* get everything in the correct order */
2913
2914         sort_track_selection ();
2915
2916         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
2917
2918                 /* undo/redo is handled by individual tracks */
2919
2920                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
2921                         commit = true;
2922                 }
2923         }
2924         
2925         if (commit) {
2926                 commit_reversible_command ();
2927         }
2928 }
2929
2930 void
2931 Editor::paste_named_selection (float times)
2932 {
2933         TrackSelection::iterator t;
2934
2935         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
2936
2937         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
2938                 return;
2939         }
2940
2941         TreeModel::iterator i = selected->get_selected();
2942         NamedSelection* ns = (*i)[named_selection_columns.selection];
2943
2944         list<boost::shared_ptr<Playlist> >::iterator chunk;
2945         list<boost::shared_ptr<Playlist> >::iterator tmp;
2946
2947         chunk = ns->playlists.begin();
2948                 
2949         begin_reversible_command (_("paste chunk"));
2950         
2951         sort_track_selection ();
2952
2953         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
2954                 
2955                 AudioTimeAxisView* atv;
2956                 boost::shared_ptr<Playlist> pl;
2957                 boost::shared_ptr<AudioPlaylist> apl;
2958
2959                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
2960                         continue;
2961                 }
2962
2963                 if ((pl = atv->playlist()) == 0) {
2964                         continue;
2965                 }
2966                 
2967                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
2968                         continue;
2969                 }
2970
2971                 tmp = chunk;
2972                 ++tmp;
2973
2974                 XMLNode &before = apl->get_state();
2975                 apl->paste (*chunk, edit_cursor->current_frame, times);
2976                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
2977
2978                 if (tmp != ns->playlists.end()) {
2979                         chunk = tmp;
2980                 }
2981         }
2982
2983         commit_reversible_command();
2984 }
2985
2986 void
2987 Editor::duplicate_some_regions (RegionSelection& regions, float times)
2988 {
2989         boost::shared_ptr<Playlist> playlist; 
2990         RegionSelection sel = regions; // clear (below) will clear the argument list
2991                 
2992         begin_reversible_command (_("duplicate region"));
2993
2994         selection->clear_regions ();
2995
2996         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
2997
2998                 boost::shared_ptr<Region> r ((*i)->region());
2999
3000                 TimeAxisView& tv = (*i)->get_time_axis_view();
3001                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3002                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3003                 
3004                 playlist = (*i)->region()->playlist();
3005                 XMLNode &before = playlist->get_state();
3006                 playlist->duplicate (r, r->last_frame(), times);
3007                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3008
3009                 c.disconnect ();
3010
3011                 if (latest_regionview) {
3012                         selection->add (latest_regionview);
3013                 }
3014         }
3015                 
3016
3017         commit_reversible_command ();
3018 }
3019
3020 void
3021 Editor::duplicate_selection (float times)
3022 {
3023         if (selection->time.empty() || selection->tracks.empty()) {
3024                 return;
3025         }
3026
3027         boost::shared_ptr<Playlist> playlist; 
3028         vector<boost::shared_ptr<AudioRegion> > new_regions;
3029         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3030                 
3031         create_region_from_selection (new_regions);
3032
3033         if (new_regions.empty()) {
3034                 return;
3035         }
3036         
3037         begin_reversible_command (_("duplicate selection"));
3038
3039         ri = new_regions.begin();
3040
3041         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3042                 if ((playlist = (*i)->playlist()) == 0) {
3043                         continue;
3044                 }
3045                 XMLNode &before = playlist->get_state();
3046                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3047                 XMLNode &after = playlist->get_state();
3048                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3049
3050                 ++ri;
3051                 if (ri == new_regions.end()) {
3052                         --ri;
3053                 }
3054         }
3055
3056         commit_reversible_command ();
3057 }
3058
3059 void
3060 Editor::reset_point_selection ()
3061 {
3062         /* reset all selected points to the relevant default value */
3063
3064         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3065                 
3066                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3067                 
3068                 if (atv) {
3069                         atv->reset_objects (selection->points);
3070                 } 
3071         }
3072 }
3073
3074 void
3075 Editor::center_playhead ()
3076 {
3077         float page = canvas_width * frames_per_unit;
3078
3079         center_screen_internal (playhead_cursor->current_frame, page);
3080 }
3081
3082 void
3083 Editor::center_edit_cursor ()
3084 {
3085         float page = canvas_width * frames_per_unit;
3086
3087         center_screen_internal (edit_cursor->current_frame, page);
3088 }
3089
3090 void
3091 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3092 {
3093         begin_reversible_command (_("clear playlist"));
3094         XMLNode &before = playlist->get_state();
3095         playlist->clear ();
3096         XMLNode &after = playlist->get_state();
3097         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3098         commit_reversible_command ();
3099 }
3100
3101 void
3102 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3103 {
3104         boost::shared_ptr<Playlist> playlist; 
3105         nframes_t distance;
3106         nframes_t next_distance;
3107         nframes_t start;
3108
3109         if (use_edit_cursor) {
3110                 start = edit_cursor->current_frame;
3111         } else {
3112                 start = 0;
3113         }
3114
3115         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3116                 return;
3117         }
3118         
3119         if (selection->tracks.empty()) {
3120                 return;
3121         }
3122         
3123         begin_reversible_command (_("nudge track"));
3124         
3125         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3126
3127                 if ((playlist = (*i)->playlist()) == 0) {
3128                         continue;
3129                 }               
3130                 
3131                 XMLNode &before = playlist->get_state();
3132                 playlist->nudge_after (start, distance, forwards);
3133                 XMLNode &after = playlist->get_state();
3134                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3135         }
3136         
3137         commit_reversible_command ();                   
3138 }
3139
3140 void
3141 Editor::remove_last_capture ()
3142 {
3143         vector<string> choices;
3144         string prompt;
3145         
3146         if (!session) {
3147                 return;
3148         }
3149
3150         if (Config->get_verify_remove_last_capture()) {
3151                 prompt  = _("Do you really want to destroy the last capture?"
3152                             "\n(This is destructive and cannot be undone)");
3153
3154                 choices.push_back (_("No, do nothing."));
3155                 choices.push_back (_("Yes, destroy it."));
3156                 
3157                 Gtkmm2ext::Choice prompter (prompt, choices);
3158                 
3159                 if (prompter.run () == 1) {
3160                         session->remove_last_capture ();
3161                 }
3162
3163         } else {
3164                 session->remove_last_capture();
3165         }
3166 }
3167
3168 void
3169 Editor::normalize_region ()
3170 {
3171         if (!session) {
3172                 return;
3173         }
3174
3175         if (selection->regions.empty()) {
3176                 return;
3177         }
3178
3179         begin_reversible_command (_("normalize"));
3180
3181         track_canvas.get_window()->set_cursor (*wait_cursor);
3182         gdk_flush ();
3183
3184         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3185                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3186                 if (!arv)
3187                         continue;
3188                 XMLNode &before = arv->region()->get_state();
3189                 arv->audio_region()->normalize_to (0.0f);
3190                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3191         }
3192
3193         commit_reversible_command ();
3194         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3195 }
3196
3197
3198 void
3199 Editor::denormalize_region ()
3200 {
3201         if (!session) {
3202                 return;
3203         }
3204
3205         if (selection->regions.empty()) {
3206                 return;
3207         }
3208
3209         begin_reversible_command ("denormalize");
3210
3211         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3212                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3213                 if (!arv)
3214                         continue;
3215                 XMLNode &before = arv->region()->get_state();
3216                 arv->audio_region()->set_scale_amplitude (1.0f);
3217                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3218         }
3219
3220         commit_reversible_command ();
3221 }
3222
3223
3224 void
3225 Editor::reverse_region ()
3226 {
3227         if (!session) {
3228                 return;
3229         }
3230
3231         Reverse rev (*session);
3232         apply_filter (rev, _("reverse regions"));
3233 }
3234
3235 void
3236 Editor::apply_filter (AudioFilter& filter, string command)
3237 {
3238         if (selection->regions.empty()) {
3239                 return;
3240         }
3241
3242         begin_reversible_command (command);
3243
3244         track_canvas.get_window()->set_cursor (*wait_cursor);
3245         gdk_flush ();
3246
3247         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3248                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3249                 if (!arv)
3250                         continue;
3251
3252                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3253
3254                 RegionSelection::iterator tmp;
3255                 
3256                 tmp = r;
3257                 ++tmp;
3258
3259                 if (arv->audio_region()->apply (filter) == 0) {
3260
3261                         XMLNode &before = playlist->get_state();
3262                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3263                         XMLNode &after = playlist->get_state();
3264                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3265                 } else {
3266                         goto out;
3267                 }
3268
3269                 r = tmp;
3270         }
3271
3272         commit_reversible_command ();
3273         selection->regions.clear ();
3274
3275   out:
3276         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3277 }
3278
3279 void
3280 Editor::region_selection_op (void (Region::*pmf)(void))
3281 {
3282         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3283                 Region* region = (*i)->region().get();
3284                 (region->*pmf)();
3285         }
3286 }
3287
3288
3289 void
3290 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3291 {
3292         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3293                 Region* region = (*i)->region().get();
3294                 (region->*pmf)(arg);
3295         }
3296 }
3297
3298 void
3299 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3300 {
3301         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3302                 Region* region = (*i)->region().get();
3303                 (region->*pmf)(yn);
3304         }
3305 }
3306
3307 void
3308 Editor::external_edit_region ()
3309 {
3310         if (!clicked_regionview) {
3311                 return;
3312         }
3313
3314         /* more to come */
3315 }
3316
3317 void
3318 Editor::brush (nframes_t pos)
3319 {
3320         RegionSelection sel;
3321         snap_to (pos);
3322
3323         if (selection->regions.empty()) {
3324                 /* XXX get selection from region list */
3325         } else { 
3326                 sel = selection->regions;
3327         }
3328
3329         if (sel.empty()) {
3330                 return;
3331         }
3332
3333         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3334                 mouse_brush_insert_region ((*i), pos);
3335         }
3336 }
3337
3338 void
3339 Editor::reset_region_gain_envelopes ()
3340 {
3341         if (!session || selection->regions.empty()) {
3342                 return;
3343         }
3344
3345         session->begin_reversible_command (_("reset region gain"));
3346
3347         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3348                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3349                 if (arv) {
3350                         AutomationList& alist (arv->audio_region()->envelope());
3351                         XMLNode& before (alist.get_state());
3352
3353                         arv->audio_region()->set_default_envelope ();
3354                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3355                 }
3356         }
3357
3358         session->commit_reversible_command ();
3359 }
3360
3361 void
3362 Editor::toggle_gain_envelope_visibility ()
3363 {
3364         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3365                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3366                 if (arv) {
3367                         bool x = region_envelope_visible_item->get_active();
3368                         if (x != arv->envelope_visible()) {
3369                                 arv->set_envelope_visible (x);
3370                         }
3371                 }
3372         }
3373 }
3374
3375 void
3376 Editor::toggle_gain_envelope_active ()
3377 {
3378         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3379                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3380                 if (arv) {
3381                         bool x = region_envelope_active_item->get_active();
3382                         if (x != arv->audio_region()->envelope_active()) {
3383                                 arv->audio_region()->set_envelope_active (x);
3384                         }
3385                 }
3386         }
3387 }
3388
3389 void
3390 Editor::toggle_region_lock ()
3391 {
3392         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3393                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3394                 if (arv) {
3395                         bool x = region_lock_item->get_active();
3396                         if (x != arv->audio_region()->locked()) {
3397                                 arv->audio_region()->set_locked (x);
3398                         }
3399                 }
3400         }
3401 }
3402
3403 void
3404 Editor::toggle_region_mute ()
3405 {
3406         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3407                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3408                 if (arv) {
3409                         bool x = region_mute_item->get_active();
3410                         if (x != arv->audio_region()->muted()) {
3411                                 arv->audio_region()->set_muted (x);
3412                         }
3413                 }
3414         }
3415 }
3416
3417 void
3418 Editor::toggle_region_opaque ()
3419 {
3420         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3421                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3422                 if (arv) {
3423                         bool x = region_opaque_item->get_active();
3424                         if (x != arv->audio_region()->opaque()) {
3425                                 arv->audio_region()->set_opaque (x);
3426                         }
3427                 }
3428         }
3429 }
3430
3431 void
3432 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
3433 {
3434         begin_reversible_command (_("set fade in shape"));
3435
3436         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3437                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3438
3439                 if (!tmp) {
3440                         return;
3441                 }
3442
3443                 AutomationList& alist = tmp->audio_region()->fade_in();
3444                 XMLNode &before = alist.get_state();
3445
3446                 tmp->audio_region()->set_fade_in_shape (shape);
3447                 
3448                 XMLNode &after = alist.get_state();
3449                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3450         }
3451
3452         commit_reversible_command ();
3453 }
3454
3455 void
3456 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
3457 {
3458         begin_reversible_command (_("set fade out shape"));
3459
3460         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3461                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3462
3463                 if (!tmp) {
3464                         return;
3465                 }
3466
3467                 AutomationList& alist = tmp->audio_region()->fade_out();
3468                 XMLNode &before = alist.get_state();
3469
3470                 tmp->audio_region()->set_fade_out_shape (shape);
3471                 
3472                 XMLNode &after = alist.get_state();
3473                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
3474         }
3475
3476         commit_reversible_command ();
3477 }
3478
3479 void
3480 Editor::set_fade_in_active (bool yn)
3481 {
3482         begin_reversible_command (_("set fade in active"));
3483
3484         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3485                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3486
3487                 if (!tmp) {
3488                         return;
3489                 }
3490
3491
3492                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3493
3494                 XMLNode &before = ar->get_state();
3495
3496                 ar->set_fade_in_active (yn);
3497                 
3498                 XMLNode &after = ar->get_state();
3499                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3500         }
3501 }
3502
3503 void
3504 Editor::set_fade_out_active (bool yn)
3505 {
3506         begin_reversible_command (_("set fade out active"));
3507
3508         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3509                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
3510
3511                 if (!tmp) {
3512                         return;
3513                 }
3514
3515                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
3516
3517                 XMLNode &before = ar->get_state();
3518
3519                 ar->set_fade_out_active (yn);
3520                 
3521                 XMLNode &after = ar->get_state();
3522                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
3523         }
3524 }
3525