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