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