Merging undo branch into trunk. It compiles and works for limited tests. Keep
[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_audio_trackview == 0 || clicked_regionview == 0) {
230                 return;
231         }
232
233         Playlist* playlist = clicked_audio_trackview->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_trackview) {
589
590                         TrackViewList t;
591                         t.push_back (clicked_trackview);
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_trackview) {
729                 
730                 TrackViewList t;
731                 t.push_back (clicked_trackview);
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_trackview == 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_trackview) {
1346                 return;
1347         }
1348         
1349         clicked_trackview->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_trackview) {
1396                 return;
1397         }
1398         
1399         clicked_trackview->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_audio_trackview != 0) {
1849                 tv = clicked_audio_trackview;
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_trackview == 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                 AudioRegion *region;
2189                 AudioRegion *current;
2190                 Region* current_r;
2191                 Playlist *pl;
2192
2193                 jack_nframes_t internal_start;
2194                 string new_name;
2195
2196                 if ((pl = (*i)->playlist()) == 0) {
2197                         continue;
2198                 }
2199
2200                 if ((current_r = pl->top_region_at (start)) == 0) {
2201                         continue;
2202                 }
2203
2204                 current = dynamic_cast<AudioRegion*> (current_r);
2205                 // FIXME: audio only
2206                 if (current != 0) {
2207                         internal_start = start - current->position();
2208                         session->region_name (new_name, current->name(), true);
2209                         region = new AudioRegion (*current, internal_start, selection_cnt, new_name);
2210                 }
2211         }
2212 }       
2213
2214 void
2215 Editor::create_region_from_selection (vector<AudioRegion *>& new_regions)
2216 {
2217         if (selection->time.empty() || selection->tracks.empty()) {
2218                 return;
2219         }
2220
2221         jack_nframes_t start = selection->time[clicked_selection].start;
2222         jack_nframes_t end = selection->time[clicked_selection].end;
2223         
2224         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2225
2226                 AudioRegion* current;
2227                 Region* current_r;
2228                 Playlist* playlist;
2229                 jack_nframes_t internal_start;
2230                 string new_name;
2231
2232                 if ((playlist = (*i)->playlist()) == 0) {
2233                         continue;
2234                 }
2235
2236                 if ((current_r = playlist->top_region_at(start)) == 0) {
2237                         continue;
2238                 }
2239
2240                 if ((current = dynamic_cast<AudioRegion*>(current_r)) == 0) {
2241                         continue;
2242                 }
2243         
2244                 internal_start = start - current->position();
2245                 session->region_name (new_name, current->name(), true);
2246                 
2247                 new_regions.push_back (new AudioRegion (*current, internal_start, end - start + 1, new_name));
2248         }
2249 }
2250
2251 void
2252 Editor::split_multichannel_region ()
2253 {
2254         vector<AudioRegion*> v;
2255
2256         AudioRegionView* clicked_arv = dynamic_cast<AudioRegionView*>(clicked_regionview);
2257         
2258         if (!clicked_arv || clicked_arv->audio_region().n_channels() < 2) {
2259                 return;
2260         }
2261
2262         clicked_arv->audio_region().separate_by_channel (*session, v);
2263
2264         /* nothing else to do, really */
2265 }
2266
2267 void
2268 Editor::new_region_from_selection ()
2269 {
2270         region_from_selection ();
2271         cancel_selection ();
2272 }
2273
2274 void
2275 Editor::separate_region_from_selection ()
2276 {
2277         bool doing_undo = false;
2278
2279         if (selection->time.empty()) {
2280                 return;
2281         }
2282
2283         Playlist *playlist;
2284                 
2285         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2286
2287                 AudioTimeAxisView* atv;
2288
2289                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2290
2291                         if (atv->is_audio_track()) {
2292                                         
2293                                 if ((playlist = atv->playlist()) != 0) {
2294                                         if (!doing_undo) {
2295                                                 begin_reversible_command (_("separate"));
2296                                                 doing_undo = true;
2297                                         }
2298                                         XMLNode *before;
2299                                         if (doing_undo) 
2300                                             before = &(playlist->get_state());
2301                         
2302                                         /* XXX need to consider musical time selections here at some point */
2303
2304                                         double speed = atv->get_diskstream()->speed();
2305
2306                                         for (list<AudioRange>::iterator t = selection->time.begin(); t != selection->time.end(); ++t) {
2307                                                 playlist->partition ((jack_nframes_t)((*t).start * speed), (jack_nframes_t)((*t).end * speed), true);
2308                                         }
2309
2310                                         if (doing_undo) 
2311                                             session->add_command(new MementoCommand<Playlist>(*playlist, *before, playlist->get_state()));
2312                                 }
2313                         }
2314                 }
2315         }
2316
2317         if (doing_undo) commit_reversible_command ();
2318 }
2319
2320 void
2321 Editor::separate_regions_using_location (Location& loc)
2322 {
2323         bool doing_undo = false;
2324
2325         if (loc.is_mark()) {
2326                 return;
2327         }
2328
2329         Playlist *playlist;
2330
2331         /* XXX i'm unsure as to whether this should operate on selected tracks only 
2332            or the entire enchillada. uncomment the below line to correct the behaviour 
2333            (currently set for all tracks)
2334         */
2335
2336         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
2337         //for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2338
2339                 AudioTimeAxisView* atv;
2340
2341                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2342
2343                         if (atv->is_audio_track()) {
2344                                         
2345                                 if ((playlist = atv->playlist()) != 0) {
2346                                         XMLNode *before;
2347                                         if (!doing_undo) {
2348                                                 begin_reversible_command (_("separate"));
2349                                                 doing_undo = true;
2350                                         }
2351                                         if (doing_undo) 
2352                                             before = &(playlist->get_state());
2353                                             
2354                         
2355                                         /* XXX need to consider musical time selections here at some point */
2356
2357                                         double speed = atv->get_diskstream()->speed();
2358
2359
2360                                         playlist->partition ((jack_nframes_t)(loc.start() * speed), (jack_nframes_t)(loc.end() * speed), true);
2361                                         if (doing_undo) 
2362                                             session->add_command(new MementoCommand<Playlist>(*playlist, *before, playlist->get_state()));
2363                                 }
2364                         }
2365                 }
2366         }
2367
2368         if (doing_undo) commit_reversible_command ();
2369 }
2370
2371 void
2372 Editor::crop_region_to_selection ()
2373 {
2374         if (selection->time.empty()) {
2375                 return;
2376         }
2377
2378         vector<Playlist*> playlists;
2379         Playlist *playlist;
2380
2381         if (clicked_trackview != 0) {
2382
2383                 if ((playlist = clicked_trackview->playlist()) == 0) {
2384                         return;
2385                 }
2386
2387                 playlists.push_back (playlist);
2388
2389         } else {
2390                 
2391                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2392
2393                         AudioTimeAxisView* atv;
2394
2395                         if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2396
2397                                 if (atv->is_audio_track()) {
2398                                         
2399                                         if ((playlist = atv->playlist()) != 0) {
2400                                                 playlists.push_back (playlist);
2401                                         }
2402                                 }
2403                         }
2404                 }
2405         }
2406
2407         if (!playlists.empty()) {
2408
2409                 jack_nframes_t start;
2410                 jack_nframes_t end;
2411                 jack_nframes_t cnt;
2412
2413                 begin_reversible_command (_("trim to selection"));
2414
2415                 for (vector<Playlist*>::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2416                         
2417                         Region *region;
2418                         
2419                         start = selection->time.start();
2420
2421                         if ((region = (*i)->top_region_at(start)) == 0) {
2422                                 continue;
2423                         }
2424                         
2425                         /* now adjust lengths to that we do the right thing
2426                            if the selection extends beyond the region
2427                         */
2428                         
2429                         start = max (start, region->position());
2430                         end = min (selection->time.end_frame(), start + region->length() - 1);
2431                         cnt = end - start + 1;
2432
2433                         XMLNode &before = (*i)->get_state();
2434                         region->trim_to (start, cnt, this);
2435                         XMLNode &after = (*i)->get_state();
2436                         session->add_command (new MementoCommand<Playlist>(*(*i), before, after));
2437                 }
2438
2439                 commit_reversible_command ();
2440         }
2441 }               
2442
2443 void
2444 Editor::region_fill_track ()
2445 {
2446         jack_nframes_t end;
2447
2448         if (!session || selection->regions.empty()) {
2449                 return;
2450         }
2451
2452         end = session->current_end_frame ();
2453
2454         begin_reversible_command (_("region fill"));
2455
2456         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2457
2458                 Region& region ((*i)->region());
2459                 
2460                 // FIXME
2461                 AudioRegion* const ar = dynamic_cast<AudioRegion*>(&region);
2462                 if (!ar)
2463                         continue;
2464
2465                 Playlist* pl = region.playlist();
2466
2467                 if (end <= region.last_frame()) {
2468                         return;
2469                 }
2470
2471                 double times = (double) (end - region.last_frame()) / (double) region.length();
2472
2473                 if (times == 0) {
2474                         return;
2475                 }
2476
2477                 XMLNode &before = pl->get_state();
2478                 pl->add_region (*(new AudioRegion (*ar)), ar->last_frame(), times);
2479                 session->add_command (new MementoCommand<Playlist>(*pl, before, pl->get_state()));
2480         }
2481
2482         commit_reversible_command ();
2483 }
2484
2485 void
2486 Editor::region_fill_selection ()
2487 {
2488         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2489                 return;
2490         }
2491
2492         if (selection->time.empty()) {
2493                 return;
2494         }
2495
2496         Region *region;
2497
2498         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2499
2500         if (selected->count_selected_rows() != 1) {
2501                 return;
2502         }
2503
2504         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2505         region = (*i)[region_list_columns.region];
2506
2507         jack_nframes_t start = selection->time[clicked_selection].start;
2508         jack_nframes_t end = selection->time[clicked_selection].end;
2509
2510         Playlist *playlist; 
2511
2512         if (selection->tracks.empty()) {
2513                 return;
2514         }
2515
2516         jack_nframes_t selection_length = end - start;
2517         float times = (float)selection_length / region->length();
2518         
2519         begin_reversible_command (_("fill selection"));
2520         
2521         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2522
2523                 if ((playlist = (*i)->playlist()) == 0) {
2524                         continue;
2525                 }               
2526                 
2527                 XMLNode &before = playlist->get_state();
2528                 playlist->add_region (*(createRegion (*region)), start, times);
2529                 session->add_command (new MementoCommand<Playlist>(*playlist, before, playlist->get_state()));
2530         }
2531         
2532         commit_reversible_command ();                   
2533 }
2534
2535 void
2536 Editor::set_a_regions_sync_position (Region& region, jack_nframes_t position)
2537 {
2538
2539         if (!region.covers (position)) {
2540           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2541                 return;
2542         }
2543         begin_reversible_command (_("set region sync position"));
2544         XMLNode &before = region.playlist()->get_state();
2545         region.set_sync_position (position);
2546         XMLNode &after = region.playlist()->get_state();
2547         session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2548         commit_reversible_command ();
2549 }
2550
2551 void
2552 Editor::set_region_sync_from_edit_cursor ()
2553 {
2554         if (clicked_regionview == 0) {
2555                 return;
2556         }
2557
2558         if (!clicked_regionview->region().covers (edit_cursor->current_frame)) {
2559                 error << _("Place the edit cursor at the desired sync point") << endmsg;
2560                 return;
2561         }
2562
2563         Region& region (clicked_regionview->region());
2564         begin_reversible_command (_("set sync from edit cursor"));
2565         XMLNode &before = region.playlist()->get_state();
2566         region.set_sync_position (edit_cursor->current_frame);
2567         XMLNode &after = region.playlist()->get_state();
2568         session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2569         commit_reversible_command ();
2570 }
2571
2572 void
2573 Editor::remove_region_sync ()
2574 {
2575         if (clicked_regionview) {
2576                 Region& region (clicked_regionview->region());
2577                 begin_reversible_command (_("remove sync"));
2578                 XMLNode &before = region.playlist()->get_state();
2579                 region.clear_sync_position ();
2580                 XMLNode &after = region.playlist()->get_state();
2581                 session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2582                 commit_reversible_command ();
2583         }
2584 }
2585
2586 void
2587 Editor::naturalize ()
2588 {
2589         if (selection->regions.empty()) {
2590                 return;
2591         }
2592         begin_reversible_command (_("naturalize"));
2593         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2594                 XMLNode &before = (*i)->region().get_state();
2595                 (*i)->region().move_to_natural_position (this);
2596                 XMLNode &after = (*i)->region().get_state();
2597                 session->add_command (new MementoCommand<Region>((*i)->region(), before, after));
2598         }
2599         commit_reversible_command ();
2600 }
2601
2602 void
2603 Editor::align (RegionPoint what)
2604 {
2605         align_selection (what, edit_cursor->current_frame);
2606 }
2607
2608 void
2609 Editor::align_relative (RegionPoint what)
2610 {
2611         align_selection_relative (what, edit_cursor->current_frame);
2612 }
2613
2614 struct RegionSortByTime {
2615     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2616             return a->region().position() < b->region().position();
2617     }
2618 };
2619
2620 void
2621 Editor::align_selection_relative (RegionPoint point, jack_nframes_t position)
2622 {
2623         if (selection->regions.empty()) {
2624                 return;
2625         }
2626
2627         jack_nframes_t distance;
2628         jack_nframes_t pos = 0;
2629         int dir;
2630
2631         list<RegionView*> sorted;
2632         selection->regions.by_position (sorted);
2633         Region& r ((*sorted.begin())->region());
2634
2635         switch (point) {
2636         case Start:
2637                 pos = r.first_frame ();
2638                 break;
2639
2640         case End:
2641                 pos = r.last_frame();
2642                 break;
2643
2644         case SyncPoint:
2645                 pos = r.adjust_to_sync (r.first_frame());
2646                 break;  
2647         }
2648
2649         if (pos > position) {
2650                 distance = pos - position;
2651                 dir = -1;
2652         } else {
2653                 distance = position - pos;
2654                 dir = 1;
2655         }
2656
2657         begin_reversible_command (_("align selection (relative)"));
2658
2659         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2660
2661                 Region& region ((*i)->region());
2662
2663                 XMLNode &before = region.playlist()->get_state();
2664                 
2665                 if (dir > 0) {
2666                         region.set_position (region.position() + distance, this);
2667                 } else {
2668                         region.set_position (region.position() - distance, this);
2669                 }
2670
2671                 XMLNode &after = region.playlist()->get_state();
2672                 session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2673
2674         }
2675
2676         commit_reversible_command ();
2677 }
2678
2679 void
2680 Editor::align_selection (RegionPoint point, jack_nframes_t position)
2681 {
2682         if (selection->regions.empty()) {
2683                 return;
2684         }
2685
2686         begin_reversible_command (_("align selection"));
2687
2688         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2689                 align_region_internal ((*i)->region(), point, position);
2690         }
2691
2692         commit_reversible_command ();
2693 }
2694
2695 void
2696 Editor::align_region (Region& region, RegionPoint point, jack_nframes_t position)
2697 {
2698         begin_reversible_command (_("align region"));
2699         align_region_internal (region, point, position);
2700         commit_reversible_command ();
2701 }
2702
2703 void
2704 Editor::align_region_internal (Region& region, RegionPoint point, jack_nframes_t position)
2705 {
2706         XMLNode &before = region.playlist()->get_state();
2707
2708         switch (point) {
2709         case SyncPoint:
2710                 region.set_position (region.adjust_to_sync (position), this);
2711                 break;
2712
2713         case End:
2714                 if (position > region.length()) {
2715                         region.set_position (position - region.length(), this);
2716                 }
2717                 break;
2718
2719         case Start:
2720                 region.set_position (position, this);
2721                 break;
2722         }
2723
2724         XMLNode &after = region.playlist()->get_state();
2725         session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2726 }       
2727
2728 void
2729 Editor::trim_region_to_edit_cursor ()
2730 {
2731         if (clicked_regionview == 0) {
2732                 return;
2733         }
2734
2735         Region& region (clicked_regionview->region());
2736
2737         float speed = 1.0f;
2738         AudioTimeAxisView *atav;
2739
2740         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2741                 if (atav->get_diskstream() != 0) {
2742                         speed = atav->get_diskstream()->speed();
2743                 }
2744         }
2745
2746         begin_reversible_command (_("trim to edit"));
2747         XMLNode &before = region.playlist()->get_state();
2748         region.trim_end( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2749         XMLNode &after = region.playlist()->get_state();
2750         session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2751         commit_reversible_command ();
2752 }
2753
2754 void
2755 Editor::trim_region_from_edit_cursor ()
2756 {
2757         if (clicked_regionview == 0) {
2758                 return;
2759         }
2760
2761         Region& region (clicked_regionview->region());
2762
2763         float speed = 1.0f;
2764         AudioTimeAxisView *atav;
2765
2766         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2767                 if (atav->get_diskstream() != 0) {
2768                         speed = atav->get_diskstream()->speed();
2769                 }
2770         }
2771
2772         begin_reversible_command (_("trim to edit"));
2773         XMLNode &before = region.playlist()->get_state();
2774         region.trim_front ( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2775         XMLNode &after = region.playlist()->get_state();
2776         session->add_command(new MementoCommand<Playlist>(*(region.playlist()), before, after));
2777         commit_reversible_command ();
2778 }
2779
2780 void
2781 Editor::unfreeze_route ()
2782 {
2783         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2784                 return;
2785         }
2786         
2787         clicked_audio_trackview->audio_track()->unfreeze ();
2788 }
2789
2790 void*
2791 Editor::_freeze_thread (void* arg)
2792 {
2793         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2794         return static_cast<Editor*>(arg)->freeze_thread ();
2795 }
2796
2797 void*
2798 Editor::freeze_thread ()
2799 {
2800         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2801         return 0;
2802 }
2803
2804 gint
2805 Editor::freeze_progress_timeout (void *arg)
2806 {
2807         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2808         return !(current_interthread_info->done || current_interthread_info->cancel);
2809 }
2810
2811 void
2812 Editor::freeze_route ()
2813 {
2814         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2815                 return;
2816         }
2817         
2818         InterThreadInfo itt;
2819
2820         if (interthread_progress_window == 0) {
2821                 build_interthread_progress_window ();
2822         }
2823         
2824         interthread_progress_window->set_title (_("ardour: freeze"));
2825         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2826         interthread_progress_window->show_all ();
2827         interthread_progress_bar.set_fraction (0.0f);
2828         interthread_progress_label.set_text ("");
2829         interthread_cancel_label.set_text (_("Cancel Freeze"));
2830         current_interthread_info = &itt;
2831
2832         interthread_progress_connection = 
2833           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2834
2835         itt.done = false;
2836         itt.cancel = false;
2837         itt.progress = 0.0f;
2838
2839         pthread_create (&itt.thread, 0, _freeze_thread, this);
2840
2841         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2842
2843         while (!itt.done && !itt.cancel) {
2844                 gtk_main_iteration ();
2845         }
2846
2847         interthread_progress_connection.disconnect ();
2848         interthread_progress_window->hide_all ();
2849         current_interthread_info = 0;
2850         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2851 }
2852
2853 void
2854 Editor::bounce_range_selection ()
2855 {
2856         if (selection->time.empty()) {
2857                 return;
2858         }
2859
2860         TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
2861
2862         jack_nframes_t start = selection->time[clicked_selection].start;
2863         jack_nframes_t end = selection->time[clicked_selection].end;
2864         jack_nframes_t cnt = end - start + 1;
2865         
2866         begin_reversible_command (_("bounce range"));
2867
2868         for (TrackViewList::iterator i = views->begin(); i != views->end(); ++i) {
2869
2870                 AudioTimeAxisView* atv;
2871
2872                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2873                         continue;
2874                 }
2875                 
2876                 Playlist* playlist;
2877                 
2878                 if ((playlist = atv->playlist()) == 0) {
2879                         return;
2880                 }
2881
2882                 InterThreadInfo itt;
2883                 
2884                 itt.done = false;
2885                 itt.cancel = false;
2886                 itt.progress = false;
2887                 
2888                 XMLNode &before = playlist->get_state();
2889                 atv->audio_track()->bounce_range (start, cnt, itt);
2890                 XMLNode &after = playlist->get_state();
2891                 session->add_command (new MementoCommand<Playlist> (*playlist, before, after));
2892         }
2893         
2894         commit_reversible_command ();
2895         
2896         delete views;
2897 }
2898
2899 void
2900 Editor::cut ()
2901 {
2902         cut_copy (Cut);
2903 }
2904
2905 void
2906 Editor::copy ()
2907 {
2908         cut_copy (Copy);
2909 }
2910
2911 void 
2912 Editor::cut_copy (CutCopyOp op)
2913 {
2914         /* only cancel selection if cut/copy is successful.*/
2915
2916         string opname;
2917
2918         switch (op) {
2919         case Cut:
2920                 opname = _("cut");
2921                 break;
2922         case Copy:
2923                 opname = _("copy");
2924                 break;
2925         case Clear:
2926                 opname = _("clear");
2927                 break;
2928         }
2929         
2930         cut_buffer->clear ();
2931
2932         switch (current_mouse_mode()) {
2933         case MouseObject: 
2934                 if (!selection->regions.empty() || !selection->points.empty()) {
2935
2936                         begin_reversible_command (opname + _(" objects"));
2937
2938                         if (!selection->regions.empty()) {
2939                                 
2940                                 cut_copy_regions (op);
2941                                 
2942                                 if (op == Cut) {
2943                                         selection->clear_regions ();
2944                                 }
2945                         }
2946
2947                         if (!selection->points.empty()) {
2948                                 cut_copy_points (op);
2949
2950                                 if (op == Cut) {
2951                                         selection->clear_points ();
2952                                 }
2953                         }
2954
2955                         commit_reversible_command ();   
2956                 }
2957                 break;
2958                 
2959         case MouseRange:
2960                 if (!selection->time.empty()) {
2961
2962                         begin_reversible_command (opname + _(" range"));
2963                         cut_copy_ranges (op);
2964                         commit_reversible_command ();
2965
2966                         if (op == Cut) {
2967                                 selection->clear_time ();
2968                         }
2969                         
2970                 }
2971                 break;
2972                 
2973         default:
2974                 break;
2975         }
2976 }
2977
2978 void
2979 Editor::cut_copy_points (CutCopyOp op)
2980 {
2981         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
2982
2983                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
2984
2985                 if (atv) {
2986                         atv->cut_copy_clear_objects (selection->points, op);
2987                 } 
2988         }
2989 }
2990
2991 void
2992 Editor::cut_copy_regions (CutCopyOp op)
2993 {
2994         typedef std::map<AudioPlaylist*,AudioPlaylist*> PlaylistMapping;
2995         PlaylistMapping pmap;
2996         jack_nframes_t first_position = max_frames;
2997         set<Playlist*> freezelist;
2998         pair<set<Playlist*>::iterator,bool> insert_result;
2999
3000         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
3001                 first_position = min ((*x)->region().position(), first_position);
3002
3003                 if (op == Cut || op == Clear) {
3004                         AudioPlaylist *pl = dynamic_cast<AudioPlaylist*>((*x)->region().playlist());
3005                         if (pl) {
3006                                 insert_result = freezelist.insert (pl);
3007                                 if (insert_result.second) {
3008                                         pl->freeze ();
3009                                         session->add_command (new MementoUndoCommand<Playlist>(*pl, pl->get_state()));
3010                                 }
3011                         }
3012                 }
3013         }
3014
3015         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3016
3017                 AudioPlaylist *pl = dynamic_cast<AudioPlaylist*>((*x)->region().playlist());
3018                 AudioPlaylist* npl;
3019                 RegionSelection::iterator tmp;
3020                 
3021                 tmp = x;
3022                 ++tmp;
3023
3024                 if (pl) {
3025
3026                         PlaylistMapping::iterator pi = pmap.find (pl);
3027                         
3028                         if (pi == pmap.end()) {
3029                                 npl = new AudioPlaylist (*session, "cutlist", true);
3030                                 npl->freeze();
3031                                 pmap[pl] = npl;
3032                         } else {
3033                                 npl = pi->second;
3034                         }
3035
3036                         // FIXME
3037                         AudioRegion* const ar = dynamic_cast<AudioRegion*>(&(*x)->region());
3038                         switch (op) {
3039                         case Cut:
3040                                 if (!ar) break;
3041
3042                                 npl->add_region (*(new AudioRegion (*ar)), (*x)->region().position() - first_position);
3043                                 pl->remove_region (&((*x)->region()));
3044                                 break;
3045
3046                         case Copy:
3047                                 if (!ar) break;
3048
3049                                 npl->add_region (*(new AudioRegion (*ar)), (*x)->region().position() - first_position);
3050                                 break;
3051
3052                         case Clear:
3053                                 pl->remove_region (&((*x)->region()));
3054                                 break;
3055                         }
3056                 }
3057
3058                 x = tmp;
3059         }
3060
3061         list<Playlist*> foo;
3062
3063         for (PlaylistMapping::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3064                 foo.push_back (i->second);
3065         }
3066
3067         if (!foo.empty()) {
3068                 cut_buffer->set (foo);
3069         }
3070         
3071         for (set<Playlist*>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3072                 (*pl)->thaw ();
3073                 session->add_command (new MementoRedoCommand<Playlist>(*(*pl), (*pl)->get_state()));
3074         }
3075 }
3076
3077 void
3078 Editor::cut_copy_ranges (CutCopyOp op)
3079 {
3080         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3081                 (*i)->cut_copy_clear (*selection, op);
3082         }
3083 }
3084
3085 void
3086 Editor::paste (float times)
3087 {
3088         paste_internal (edit_cursor->current_frame, times);
3089 }
3090
3091 void
3092 Editor::mouse_paste ()
3093 {
3094         int x, y;
3095         double wx, wy;
3096
3097         track_canvas.get_pointer (x, y);
3098         track_canvas.window_to_world (x, y, wx, wy);
3099         wx += horizontal_adjustment.get_value();
3100         wy += vertical_adjustment.get_value();
3101
3102         GdkEvent event;
3103         event.type = GDK_BUTTON_RELEASE;
3104         event.button.x = wx;
3105         event.button.y = wy;
3106         
3107         jack_nframes_t where = event_frame (&event, 0, 0);
3108         snap_to (where);
3109         paste_internal (where, 1);
3110 }
3111
3112 void
3113 Editor::paste_internal (jack_nframes_t position, float times)
3114 {
3115         bool commit = false;
3116
3117         if (cut_buffer->empty() || selection->tracks.empty()) {
3118                 return;
3119         }
3120
3121         if (position == max_frames) {
3122                 position = edit_cursor->current_frame;
3123         }
3124
3125         begin_reversible_command (_("paste"));
3126
3127         TrackSelection::iterator i;
3128         size_t nth;
3129
3130         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3131                 
3132                 /* undo/redo is handled by individual tracks */
3133
3134                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3135                         commit = true;
3136                 }
3137         }
3138
3139         if (commit) {
3140                 commit_reversible_command ();
3141         }
3142 }
3143
3144 void
3145 Editor::paste_named_selection (float times)
3146 {
3147         TrackSelection::iterator t;
3148
3149         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3150
3151         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3152                 return;
3153         }
3154
3155         TreeModel::iterator i = selected->get_selected();
3156         NamedSelection* ns = (*i)[named_selection_columns.selection];
3157
3158         list<Playlist*>::iterator chunk;
3159         list<Playlist*>::iterator tmp;
3160
3161         chunk = ns->playlists.begin();
3162                 
3163         begin_reversible_command (_("paste chunk"));
3164
3165         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3166                 
3167                 AudioTimeAxisView* atv;
3168                 Playlist* pl;
3169                 AudioPlaylist* apl;
3170
3171                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3172                         continue;
3173                 }
3174
3175                 if ((pl = atv->playlist()) == 0) {
3176                         continue;
3177                 }
3178
3179                 if ((apl = dynamic_cast<AudioPlaylist*> (pl)) == 0) {
3180                         continue;
3181                 }
3182
3183                 tmp = chunk;
3184                 ++tmp;
3185
3186                 XMLNode &before = apl->get_state();
3187                 apl->paste (**chunk, edit_cursor->current_frame, times);
3188                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, before, apl->get_state()));
3189
3190                 if (tmp != ns->playlists.end()) {
3191                         chunk = tmp;
3192                 }
3193         }
3194
3195         commit_reversible_command();
3196 }
3197
3198 void
3199 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3200 {
3201         Playlist *playlist; 
3202         RegionSelection sel = regions; // clear (below) will clear the argument list
3203                 
3204         begin_reversible_command (_("duplicate region"));
3205
3206         selection->clear_regions ();
3207
3208         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3209
3210                 Region& r ((*i)->region());
3211
3212                 TimeAxisView& tv = (*i)->get_time_axis_view();
3213                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3214                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3215                 
3216                 playlist = (*i)->region().playlist();
3217                 XMLNode &before = playlist->get_state();
3218                 playlist->duplicate (r, r.last_frame(), times);
3219                 session->add_command(new MementoCommand<Playlist>(*playlist, before, playlist->get_state()));
3220
3221                 c.disconnect ();
3222
3223                 if (latest_regionview) {
3224                         selection->add (latest_regionview);
3225                 }
3226         }
3227                 
3228
3229         commit_reversible_command ();
3230 }
3231
3232 void
3233 Editor::duplicate_selection (float times)
3234 {
3235         if (selection->time.empty() || selection->tracks.empty()) {
3236                 return;
3237         }
3238
3239         Playlist *playlist; 
3240         vector<AudioRegion*> new_regions;
3241         vector<AudioRegion*>::iterator ri;
3242                 
3243         create_region_from_selection (new_regions);
3244
3245         if (new_regions.empty()) {
3246                 return;
3247         }
3248         
3249         begin_reversible_command (_("duplicate selection"));
3250
3251         ri = new_regions.begin();
3252
3253         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3254                 if ((playlist = (*i)->playlist()) == 0) {
3255                         continue;
3256                 }
3257                 XMLNode &before = playlist->get_state();
3258                 playlist->duplicate (**ri, selection->time[clicked_selection].end, times);
3259                 XMLNode &after = playlist->get_state();
3260                 session->add_command (new MementoCommand<Playlist>(*playlist, before, after));
3261
3262                 ++ri;
3263                 if (ri == new_regions.end()) {
3264                         --ri;
3265                 }
3266         }
3267
3268         commit_reversible_command ();
3269 }
3270
3271 void
3272 Editor::reset_point_selection ()
3273 {
3274         /* reset all selected points to the relevant default value */
3275
3276         cerr << "point selection has " << selection->points.size() << " entries\n";
3277         
3278         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3279                 
3280                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3281                 
3282                 if (atv) {
3283                         atv->reset_objects (selection->points);
3284                 } 
3285         }
3286 }
3287
3288 void
3289 Editor::center_playhead ()
3290 {
3291         float page = canvas_width * frames_per_unit;
3292
3293         center_screen_internal (playhead_cursor->current_frame, page);
3294 }
3295
3296 void
3297 Editor::center_edit_cursor ()
3298 {
3299         float page = canvas_width * frames_per_unit;
3300
3301         center_screen_internal (edit_cursor->current_frame, page);
3302 }
3303
3304 void
3305 Editor::clear_playlist (Playlist& playlist)
3306 {
3307         begin_reversible_command (_("clear playlist"));
3308         XMLNode &before = playlist.get_state();
3309         playlist.clear ();
3310         XMLNode &after = playlist.get_state();
3311         session->add_command (new MementoCommand<Playlist>(playlist, before, after));
3312         commit_reversible_command ();
3313 }
3314
3315 void
3316 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3317 {
3318         Playlist *playlist; 
3319         jack_nframes_t distance;
3320         jack_nframes_t next_distance;
3321         jack_nframes_t start;
3322
3323         if (use_edit_cursor) {
3324                 start = edit_cursor->current_frame;
3325         } else {
3326                 start = 0;
3327         }
3328
3329         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3330                 return;
3331         }
3332         
3333         if (selection->tracks.empty()) {
3334                 return;
3335         }
3336         
3337         begin_reversible_command (_("nudge track"));
3338         
3339         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3340
3341                 if ((playlist = (*i)->playlist()) == 0) {
3342                         continue;
3343                 }               
3344                 
3345                 XMLNode &before = playlist->get_state();
3346                 playlist->nudge_after (start, distance, forwards);
3347                 XMLNode &after = playlist->get_state();
3348                 session->add_command (new MementoCommand<Playlist>(*playlist, before, after));
3349         }
3350         
3351         commit_reversible_command ();                   
3352 }
3353
3354 void
3355 Editor::remove_last_capture ()
3356 {
3357         vector<string> choices;
3358         string prompt;
3359         
3360         if (!session) {
3361                 return;
3362         }
3363
3364         if (Config->get_verify_remove_last_capture()) {
3365                 prompt  = _("Do you really want to destroy the last capture?"
3366                             "\n(This is destructive and cannot be undone)");
3367
3368                 choices.push_back (_("No, do nothing."));
3369                 choices.push_back (_("Yes, destroy it."));
3370                 
3371                 Gtkmm2ext::Choice prompter (prompt, choices);
3372                 
3373                 if (prompter.run () == 1) {
3374                         session->remove_last_capture ();
3375                 }
3376
3377         } else {
3378                 session->remove_last_capture();
3379         }
3380 }
3381
3382 void
3383 Editor::normalize_region ()
3384 {
3385         if (!session) {
3386                 return;
3387         }
3388
3389         if (selection->regions.empty()) {
3390                 return;
3391         }
3392
3393         begin_reversible_command (_("normalize"));
3394
3395         track_canvas.get_window()->set_cursor (*wait_cursor);
3396         gdk_flush ();
3397
3398         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3399                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3400                 if (!arv)
3401                         continue;
3402                 XMLNode &before = arv->region().get_state();
3403                 arv->audio_region().normalize_to (0.0f);
3404                 session->add_command (new MementoCommand<Region>(arv->region(), before, arv->region().get_state()));
3405         }
3406
3407         commit_reversible_command ();
3408         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3409 }
3410
3411
3412 void
3413 Editor::denormalize_region ()
3414 {
3415         if (!session) {
3416                 return;
3417         }
3418
3419         if (selection->regions.empty()) {
3420                 return;
3421         }
3422
3423         begin_reversible_command ("denormalize");
3424
3425         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3426                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3427                 if (!arv)
3428                         continue;
3429                 XMLNode &before = arv->region().get_state();
3430                 arv->audio_region().set_scale_amplitude (1.0f);
3431                 session->add_command (new MementoCommand<Region>(arv->region(), before, arv->region().get_state()));
3432         }
3433
3434         commit_reversible_command ();
3435 }
3436
3437
3438 void
3439 Editor::reverse_region ()
3440 {
3441         if (!session) {
3442                 return;
3443         }
3444
3445         Reverse rev (*session);
3446         apply_filter (rev, _("reverse regions"));
3447 }
3448
3449 void
3450 Editor::apply_filter (AudioFilter& filter, string command)
3451 {
3452         if (selection->regions.empty()) {
3453                 return;
3454         }
3455
3456         begin_reversible_command (command);
3457
3458         track_canvas.get_window()->set_cursor (*wait_cursor);
3459         gdk_flush ();
3460
3461         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3462                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3463                 if (!arv)
3464                         continue;
3465
3466                 Playlist* playlist = arv->region().playlist();
3467
3468                 RegionSelection::iterator tmp;
3469                 
3470                 tmp = r;
3471                 ++tmp;
3472
3473                 if (arv->audio_region().apply (filter) == 0) {
3474
3475                         XMLNode &before = playlist->get_state();
3476                         playlist->replace_region (arv->region(), *(filter.results.front()), arv->region().position());
3477                         XMLNode &after = playlist->get_state();
3478                         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
3479                 } else {
3480                         goto out;
3481                 }
3482
3483                 r = tmp;
3484         }
3485
3486         commit_reversible_command ();
3487         selection->regions.clear ();
3488
3489   out:
3490         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3491 }
3492
3493 void
3494 Editor::region_selection_op (void (Region::*pmf)(void))
3495 {
3496         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3497                 ((*i)->region().*pmf)();
3498         }
3499 }
3500
3501
3502 void
3503 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3504 {
3505         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3506                 ((*i)->region().*pmf)(arg);
3507         }
3508 }
3509
3510 void
3511 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3512 {
3513         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3514                 ((*i)->region().*pmf)(yn);
3515         }
3516 }
3517
3518 void
3519 Editor::external_edit_region ()
3520 {
3521         if (!clicked_regionview) {
3522                 return;
3523         }
3524
3525         /* more to come */
3526 }
3527
3528 void
3529 Editor::brush (jack_nframes_t pos)
3530 {
3531         RegionSelection sel;
3532         snap_to (pos);
3533
3534         if (selection->regions.empty()) {
3535                 /* XXX get selection from region list */
3536         } else { 
3537                 sel = selection->regions;
3538         }
3539
3540         if (sel.empty()) {
3541                 return;
3542         }
3543
3544         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3545                 mouse_brush_insert_region ((*i), pos);
3546         }
3547 }
3548
3549 void
3550 Editor::toggle_gain_envelope_visibility ()
3551 {
3552         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3553                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3554                 if (arv)
3555                         arv->set_envelope_visible (!arv->envelope_visible());
3556         }
3557 }
3558
3559 void
3560 Editor::toggle_gain_envelope_active ()
3561 {
3562         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3563                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3564                 if (arv)
3565                         arv->audio_region().set_envelope_active (true);
3566         }
3567 }