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