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