save and restore clock modes
[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_create (&itt.thread, 0, _freeze_thread, this);
2784
2785         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2786
2787         while (!itt.done && !itt.cancel) {
2788                 gtk_main_iteration ();
2789         }
2790
2791         interthread_progress_connection.disconnect ();
2792         interthread_progress_window->hide_all ();
2793         current_interthread_info = 0;
2794         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2795 }
2796
2797 void
2798 Editor::bounce_range_selection ()
2799 {
2800         if (selection->time.empty()) {
2801                 return;
2802         }
2803
2804         TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
2805
2806         nframes_t start = selection->time[clicked_selection].start;
2807         nframes_t end = selection->time[clicked_selection].end;
2808         nframes_t cnt = end - start + 1;
2809         
2810         begin_reversible_command (_("bounce range"));
2811
2812         for (TrackViewList::iterator i = views->begin(); i != views->end(); ++i) {
2813
2814                 AudioTimeAxisView* atv;
2815
2816                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2817                         continue;
2818                 }
2819                 
2820                 boost::shared_ptr<Playlist> playlist;
2821                 
2822                 if ((playlist = atv->playlist()) == 0) {
2823                         return;
2824                 }
2825
2826                 InterThreadInfo itt;
2827                 
2828                 itt.done = false;
2829                 itt.cancel = false;
2830                 itt.progress = false;
2831                 
2832                 XMLNode &before = playlist->get_state();
2833                 atv->audio_track()->bounce_range (start, cnt, itt);
2834                 XMLNode &after = playlist->get_state();
2835                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
2836         }
2837         
2838         commit_reversible_command ();
2839         
2840         delete views;
2841 }
2842
2843 void
2844 Editor::cut ()
2845 {
2846         cut_copy (Cut);
2847 }
2848
2849 void
2850 Editor::copy ()
2851 {
2852         cut_copy (Copy);
2853 }
2854
2855 void 
2856 Editor::cut_copy (CutCopyOp op)
2857 {
2858         /* only cancel selection if cut/copy is successful.*/
2859
2860         string opname;
2861
2862         switch (op) {
2863         case Cut:
2864                 opname = _("cut");
2865                 break;
2866         case Copy:
2867                 opname = _("copy");
2868                 break;
2869         case Clear:
2870                 opname = _("clear");
2871                 break;
2872         }
2873         
2874         cut_buffer->clear ();
2875
2876         switch (current_mouse_mode()) {
2877         case MouseObject: 
2878                 if (!selection->regions.empty() || !selection->points.empty()) {
2879
2880                         begin_reversible_command (opname + _(" objects"));
2881
2882                         if (!selection->regions.empty()) {
2883                                 
2884                                 cut_copy_regions (op);
2885                                 
2886                                 if (op == Cut) {
2887                                         selection->clear_regions ();
2888                                 }
2889                         }
2890
2891                         if (!selection->points.empty()) {
2892                                 cut_copy_points (op);
2893
2894                                 if (op == Cut) {
2895                                         selection->clear_points ();
2896                                 }
2897                         }
2898
2899                         commit_reversible_command ();   
2900                 }
2901                 break;
2902                 
2903         case MouseRange:
2904                 if (!selection->time.empty()) {
2905
2906                         begin_reversible_command (opname + _(" range"));
2907                         cut_copy_ranges (op);
2908                         commit_reversible_command ();
2909
2910                         if (op == Cut) {
2911                                 selection->clear_time ();
2912                         }
2913                         
2914                 }
2915                 break;
2916                 
2917         default:
2918                 break;
2919         }
2920 }
2921
2922 void
2923 Editor::cut_copy_points (CutCopyOp op)
2924 {
2925         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
2926
2927                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
2928
2929                 if (atv) {
2930                         atv->cut_copy_clear_objects (selection->points, op);
2931                 } 
2932         }
2933 }
2934
2935 struct PlaylistState {
2936     boost::shared_ptr<Playlist> playlist;
2937     XMLNode*  before;
2938 };
2939
2940 struct lt_playlist {
2941     bool operator () (const PlaylistState& a, const PlaylistState& b) {
2942             return a.playlist < b.playlist;
2943     }
2944 };
2945         
2946 void
2947 Editor::cut_copy_regions (CutCopyOp op)
2948 {
2949         typedef std::map<boost::shared_ptr<AudioPlaylist>,boost::shared_ptr<AudioPlaylist> > PlaylistMapping;
2950         PlaylistMapping pmap;
2951         nframes_t first_position = max_frames;
2952
2953         set<PlaylistState, lt_playlist> freezelist;
2954         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
2955
2956         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2957                 first_position = min ((*x)->region()->position(), first_position);
2958
2959                 if (op == Cut || op == Clear) {
2960                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
2961
2962                         if (pl) {
2963
2964                                 PlaylistState before;
2965                                 before.playlist = pl;
2966                                 before.before = &pl->get_state();
2967                                 
2968                                 insert_result = freezelist.insert (before);
2969
2970                                 if (insert_result.second) {
2971                                         pl->freeze ();
2972                                 }
2973                         }
2974                 }
2975         }
2976
2977         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
2978
2979                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
2980                 boost::shared_ptr<AudioPlaylist> npl;
2981                 RegionSelection::iterator tmp;
2982                 
2983                 tmp = x;
2984                 ++tmp;
2985
2986                 if (pl) {
2987
2988                         PlaylistMapping::iterator pi = pmap.find (pl);
2989                         
2990                         if (pi == pmap.end()) {
2991                                 npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
2992                                 npl->freeze();
2993                                 pmap[pl] = npl;
2994                         } else {
2995                                 npl = pi->second;
2996                         }
2997
2998                         // FIXME
2999                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3000                         switch (op) {
3001                         case Cut:
3002                                 if (!ar) break;
3003
3004                                 npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3005                                 pl->remove_region (((*x)->region()));
3006                                 break;
3007
3008                         case Copy:
3009                                 if (!ar) break;
3010
3011                                 npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3012                                 break;
3013
3014                         case Clear:
3015                                 pl->remove_region (((*x)->region()));
3016                                 break;
3017                         }
3018                 }
3019
3020                 x = tmp;
3021         }
3022
3023         list<boost::shared_ptr<Playlist> > foo;
3024
3025         for (PlaylistMapping::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3026                 foo.push_back (i->second);
3027         }
3028
3029         if (!foo.empty()) {
3030                 cut_buffer->set (foo);
3031         }
3032         
3033         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3034                 (*pl).playlist->thaw ();
3035                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3036         }
3037 }
3038
3039 void
3040 Editor::cut_copy_ranges (CutCopyOp op)
3041 {
3042         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3043                 (*i)->cut_copy_clear (*selection, op);
3044         }
3045 }
3046
3047 void
3048 Editor::paste (float times)
3049 {
3050         paste_internal (edit_cursor->current_frame, times);
3051 }
3052
3053 void
3054 Editor::mouse_paste ()
3055 {
3056         int x, y;
3057         double wx, wy;
3058
3059         track_canvas.get_pointer (x, y);
3060         track_canvas.window_to_world (x, y, wx, wy);
3061         wx += horizontal_adjustment.get_value();
3062         wy += vertical_adjustment.get_value();
3063
3064         GdkEvent event;
3065         event.type = GDK_BUTTON_RELEASE;
3066         event.button.x = wx;
3067         event.button.y = wy;
3068         
3069         nframes_t where = event_frame (&event, 0, 0);
3070         snap_to (where);
3071         paste_internal (where, 1);
3072 }
3073
3074 void
3075 Editor::paste_internal (nframes_t position, float times)
3076 {
3077         bool commit = false;
3078
3079         if (cut_buffer->empty() || selection->tracks.empty()) {
3080                 return;
3081         }
3082
3083         if (position == max_frames) {
3084                 position = edit_cursor->current_frame;
3085         }
3086
3087         begin_reversible_command (_("paste"));
3088
3089         TrackSelection::iterator i;
3090         size_t nth;
3091
3092         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3093                 
3094                 /* undo/redo is handled by individual tracks */
3095
3096                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3097                         commit = true;
3098                 }
3099         }
3100
3101         if (commit) {
3102                 commit_reversible_command ();
3103         }
3104 }
3105
3106 void
3107 Editor::paste_named_selection (float times)
3108 {
3109         TrackSelection::iterator t;
3110
3111         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3112
3113         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3114                 return;
3115         }
3116
3117         TreeModel::iterator i = selected->get_selected();
3118         NamedSelection* ns = (*i)[named_selection_columns.selection];
3119
3120         list<boost::shared_ptr<Playlist> >::iterator chunk;
3121         list<boost::shared_ptr<Playlist> >::iterator tmp;
3122
3123         chunk = ns->playlists.begin();
3124                 
3125         begin_reversible_command (_("paste chunk"));
3126
3127         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3128                 
3129                 AudioTimeAxisView* atv;
3130                 boost::shared_ptr<Playlist> pl;
3131                 boost::shared_ptr<AudioPlaylist> apl;
3132
3133                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3134                         continue;
3135                 }
3136
3137                 if ((pl = atv->playlist()) == 0) {
3138                         continue;
3139                 }
3140                 
3141                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3142                         continue;
3143                 }
3144
3145                 tmp = chunk;
3146                 ++tmp;
3147
3148                 XMLNode &before = apl->get_state();
3149                 apl->paste (*chunk, edit_cursor->current_frame, times);
3150                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3151
3152                 if (tmp != ns->playlists.end()) {
3153                         chunk = tmp;
3154                 }
3155         }
3156
3157         commit_reversible_command();
3158 }
3159
3160 void
3161 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3162 {
3163         boost::shared_ptr<Playlist> playlist; 
3164         RegionSelection sel = regions; // clear (below) will clear the argument list
3165                 
3166         begin_reversible_command (_("duplicate region"));
3167
3168         selection->clear_regions ();
3169
3170         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3171
3172                 boost::shared_ptr<Region> r ((*i)->region());
3173
3174                 TimeAxisView& tv = (*i)->get_time_axis_view();
3175                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3176                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3177                 
3178                 playlist = (*i)->region()->playlist();
3179                 XMLNode &before = playlist->get_state();
3180                 playlist->duplicate (r, r->last_frame(), times);
3181                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3182
3183                 c.disconnect ();
3184
3185                 if (latest_regionview) {
3186                         selection->add (latest_regionview);
3187                 }
3188         }
3189                 
3190
3191         commit_reversible_command ();
3192 }
3193
3194 void
3195 Editor::duplicate_selection (float times)
3196 {
3197         if (selection->time.empty() || selection->tracks.empty()) {
3198                 return;
3199         }
3200
3201         boost::shared_ptr<Playlist> playlist; 
3202         vector<boost::shared_ptr<AudioRegion> > new_regions;
3203         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3204                 
3205         create_region_from_selection (new_regions);
3206
3207         if (new_regions.empty()) {
3208                 return;
3209         }
3210         
3211         begin_reversible_command (_("duplicate selection"));
3212
3213         ri = new_regions.begin();
3214
3215         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3216                 if ((playlist = (*i)->playlist()) == 0) {
3217                         continue;
3218                 }
3219                 XMLNode &before = playlist->get_state();
3220                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3221                 XMLNode &after = playlist->get_state();
3222                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3223
3224                 ++ri;
3225                 if (ri == new_regions.end()) {
3226                         --ri;
3227                 }
3228         }
3229
3230         commit_reversible_command ();
3231 }
3232
3233 void
3234 Editor::reset_point_selection ()
3235 {
3236         /* reset all selected points to the relevant default value */
3237
3238         cerr << "point selection has " << selection->points.size() << " entries\n";
3239         
3240         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3241                 
3242                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3243                 
3244                 if (atv) {
3245                         atv->reset_objects (selection->points);
3246                 } 
3247         }
3248 }
3249
3250 void
3251 Editor::center_playhead ()
3252 {
3253         float page = canvas_width * frames_per_unit;
3254
3255         center_screen_internal (playhead_cursor->current_frame, page);
3256 }
3257
3258 void
3259 Editor::center_edit_cursor ()
3260 {
3261         float page = canvas_width * frames_per_unit;
3262
3263         center_screen_internal (edit_cursor->current_frame, page);
3264 }
3265
3266 void
3267 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3268 {
3269         begin_reversible_command (_("clear playlist"));
3270         XMLNode &before = playlist->get_state();
3271         playlist->clear ();
3272         XMLNode &after = playlist->get_state();
3273         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3274         commit_reversible_command ();
3275 }
3276
3277 void
3278 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3279 {
3280         boost::shared_ptr<Playlist> playlist; 
3281         nframes_t distance;
3282         nframes_t next_distance;
3283         nframes_t start;
3284
3285         if (use_edit_cursor) {
3286                 start = edit_cursor->current_frame;
3287         } else {
3288                 start = 0;
3289         }
3290
3291         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3292                 return;
3293         }
3294         
3295         if (selection->tracks.empty()) {
3296                 return;
3297         }
3298         
3299         begin_reversible_command (_("nudge track"));
3300         
3301         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3302
3303                 if ((playlist = (*i)->playlist()) == 0) {
3304                         continue;
3305                 }               
3306                 
3307                 XMLNode &before = playlist->get_state();
3308                 playlist->nudge_after (start, distance, forwards);
3309                 XMLNode &after = playlist->get_state();
3310                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3311         }
3312         
3313         commit_reversible_command ();                   
3314 }
3315
3316 void
3317 Editor::remove_last_capture ()
3318 {
3319         vector<string> choices;
3320         string prompt;
3321         
3322         if (!session) {
3323                 return;
3324         }
3325
3326         if (Config->get_verify_remove_last_capture()) {
3327                 prompt  = _("Do you really want to destroy the last capture?"
3328                             "\n(This is destructive and cannot be undone)");
3329
3330                 choices.push_back (_("No, do nothing."));
3331                 choices.push_back (_("Yes, destroy it."));
3332                 
3333                 Gtkmm2ext::Choice prompter (prompt, choices);
3334                 
3335                 if (prompter.run () == 1) {
3336                         session->remove_last_capture ();
3337                 }
3338
3339         } else {
3340                 session->remove_last_capture();
3341         }
3342 }
3343
3344 void
3345 Editor::normalize_region ()
3346 {
3347         if (!session) {
3348                 return;
3349         }
3350
3351         if (selection->regions.empty()) {
3352                 return;
3353         }
3354
3355         begin_reversible_command (_("normalize"));
3356
3357         track_canvas.get_window()->set_cursor (*wait_cursor);
3358         gdk_flush ();
3359
3360         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3361                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3362                 if (!arv)
3363                         continue;
3364                 XMLNode &before = arv->region()->get_state();
3365                 arv->audio_region()->normalize_to (0.0f);
3366                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3367         }
3368
3369         commit_reversible_command ();
3370         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3371 }
3372
3373
3374 void
3375 Editor::denormalize_region ()
3376 {
3377         if (!session) {
3378                 return;
3379         }
3380
3381         if (selection->regions.empty()) {
3382                 return;
3383         }
3384
3385         begin_reversible_command ("denormalize");
3386
3387         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3388                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3389                 if (!arv)
3390                         continue;
3391                 XMLNode &before = arv->region()->get_state();
3392                 arv->audio_region()->set_scale_amplitude (1.0f);
3393                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3394         }
3395
3396         commit_reversible_command ();
3397 }
3398
3399
3400 void
3401 Editor::reverse_region ()
3402 {
3403         if (!session) {
3404                 return;
3405         }
3406
3407         Reverse rev (*session);
3408         apply_filter (rev, _("reverse regions"));
3409 }
3410
3411 void
3412 Editor::apply_filter (AudioFilter& filter, string command)
3413 {
3414         if (selection->regions.empty()) {
3415                 return;
3416         }
3417
3418         begin_reversible_command (command);
3419
3420         track_canvas.get_window()->set_cursor (*wait_cursor);
3421         gdk_flush ();
3422
3423         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3424                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3425                 if (!arv)
3426                         continue;
3427
3428                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3429
3430                 RegionSelection::iterator tmp;
3431                 
3432                 tmp = r;
3433                 ++tmp;
3434
3435                 if (arv->audio_region()->apply (filter) == 0) {
3436
3437                         XMLNode &before = playlist->get_state();
3438                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3439                         XMLNode &after = playlist->get_state();
3440                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3441                 } else {
3442                         goto out;
3443                 }
3444
3445                 r = tmp;
3446         }
3447
3448         commit_reversible_command ();
3449         selection->regions.clear ();
3450
3451   out:
3452         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3453 }
3454
3455 void
3456 Editor::region_selection_op (void (Region::*pmf)(void))
3457 {
3458         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3459                 Region* region = (*i)->region().get();
3460                 (region->*pmf)();
3461         }
3462 }
3463
3464
3465 void
3466 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3467 {
3468         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3469                 Region* region = (*i)->region().get();
3470                 (region->*pmf)(arg);
3471         }
3472 }
3473
3474 void
3475 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3476 {
3477         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3478                 Region* region = (*i)->region().get();
3479                 (region->*pmf)(yn);
3480         }
3481 }
3482
3483 void
3484 Editor::external_edit_region ()
3485 {
3486         if (!clicked_regionview) {
3487                 return;
3488         }
3489
3490         /* more to come */
3491 }
3492
3493 void
3494 Editor::brush (nframes_t pos)
3495 {
3496         RegionSelection sel;
3497         snap_to (pos);
3498
3499         if (selection->regions.empty()) {
3500                 /* XXX get selection from region list */
3501         } else { 
3502                 sel = selection->regions;
3503         }
3504
3505         if (sel.empty()) {
3506                 return;
3507         }
3508
3509         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3510                 mouse_brush_insert_region ((*i), pos);
3511         }
3512 }
3513
3514 void
3515 Editor::reset_region_gain_envelopes ()
3516 {
3517         if (!session || selection->regions.empty()) {
3518                 return;
3519         }
3520
3521         session->begin_reversible_command (_("reset region gain"));
3522
3523         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3524                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3525                 if (arv) {
3526                         AutomationList& alist (arv->audio_region()->envelope());
3527                         XMLNode& before (alist.get_state());
3528
3529                         arv->audio_region()->set_default_envelope ();
3530                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3531                 }
3532         }
3533
3534         session->commit_reversible_command ();
3535 }
3536
3537 void
3538 Editor::toggle_gain_envelope_visibility ()
3539 {
3540         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3541                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3542                 if (arv) {
3543                         bool x = region_envelope_visible_item->get_active();
3544                         if (x != arv->envelope_visible()) {
3545                                 arv->set_envelope_visible (x);
3546                         }
3547                 }
3548         }
3549 }
3550
3551 void
3552 Editor::toggle_gain_envelope_active ()
3553 {
3554         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3555                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3556                 if (arv) {
3557                         bool x = region_envelope_active_item->get_active();
3558                         if (x != arv->audio_region()->envelope_active()) {
3559                                 arv->audio_region()->set_envelope_active (x);
3560                         }
3561                 }
3562         }
3563 }
3564
3565 void
3566 Editor::toggle_region_lock ()
3567 {
3568         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3569                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3570                 if (arv) {
3571                         bool x = region_lock_item->get_active();
3572                         if (x != arv->audio_region()->locked()) {
3573                                 arv->audio_region()->set_locked (x);
3574                         }
3575                 }
3576         }
3577 }
3578
3579 void
3580 Editor::toggle_region_mute ()
3581 {
3582         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3583                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3584                 if (arv) {
3585                         bool x = region_mute_item->get_active();
3586                         if (x != arv->audio_region()->muted()) {
3587                                 arv->audio_region()->set_muted (x);
3588                         }
3589                 }
3590         }
3591 }
3592
3593 void
3594 Editor::toggle_region_opaque ()
3595 {
3596         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3597                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3598                 if (arv) {
3599                         bool x = region_opaque_item->get_active();
3600                         if (x != arv->audio_region()->opaque()) {
3601                                 arv->audio_region()->set_opaque (x);
3602                         }
3603                 }
3604         }
3605 }