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