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