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