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