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