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