5ab343392a14658e374377efeffc52d6fa9e24c2
[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         reset_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         reset_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         begin_reversible_command (_("select all within"));
1355         switch (op) {
1356         case Selection::Add:
1357         case Selection::Toggle:
1358                 selection->add (touched);
1359                 break;
1360         case Selection::Set:
1361                 selection->set (touched);
1362                 break;
1363         case Selection::Extend:
1364                 /* not defined yet */
1365                 break;
1366         }
1367
1368         commit_reversible_command ();
1369         return !touched.empty();
1370 }
1371
1372 void
1373 Editor::set_selection_from_audio_region ()
1374 {
1375         if (selection->regions.empty()) {
1376                 return;
1377         }
1378
1379         RegionView* rv = *(selection->regions.begin());
1380         boost::shared_ptr<Region> region = rv->region();
1381         
1382         begin_reversible_command (_("set selection from region"));
1383         selection->set (0, region->position(), region->last_frame());
1384         commit_reversible_command ();
1385
1386         set_mouse_mode (Editing::MouseRange, false);
1387 }
1388
1389 void
1390 Editor::set_selection_from_punch()
1391 {
1392         Location* location;
1393
1394         if ((location = session->locations()->auto_punch_location()) == 0)  {
1395                 return;
1396         }
1397
1398         set_selection_from_range (*location);
1399 }
1400
1401 void
1402 Editor::set_selection_from_loop()
1403 {
1404         Location* location;
1405
1406         if ((location = session->locations()->auto_loop_location()) == 0)  {
1407                 return;
1408         }
1409         set_selection_from_range (*location);
1410 }
1411
1412 void
1413 Editor::set_selection_from_range (Location& loc)
1414 {
1415         begin_reversible_command (_("set selection from range"));
1416         selection->set (0, loc.start(), loc.end());
1417         commit_reversible_command ();
1418
1419         set_mouse_mode (Editing::MouseRange, false);
1420 }
1421
1422 void
1423 Editor::select_all_selectables_using_time_selection ()
1424 {
1425         list<Selectable *> touched;
1426
1427         if (selection->time.empty()) {
1428                 return;
1429         }
1430
1431         nframes_t start = selection->time[clicked_selection].start;
1432         nframes_t end = selection->time[clicked_selection].end;
1433
1434         if (end - start < 1)  {
1435                 return;
1436         }
1437
1438         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
1439                 if ((*iter)->hidden()) {
1440                         continue;
1441                 }
1442                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1443         }
1444
1445         begin_reversible_command (_("select all from range"));
1446         selection->set (touched);
1447         commit_reversible_command ();
1448 }
1449
1450
1451 void
1452 Editor::select_all_selectables_using_punch()
1453 {
1454         Location* location = session->locations()->auto_punch_location();
1455         list<Selectable *> touched;
1456
1457         if (location == 0 || (location->end() - location->start() <= 1))  {
1458                 return;
1459         }
1460
1461         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1462                 if ((*iter)->hidden()) {
1463                         continue;
1464                 }
1465                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1466         }
1467         begin_reversible_command (_("select all from punch"));
1468         selection->set (touched);
1469         commit_reversible_command ();
1470
1471 }
1472
1473 void
1474 Editor::select_all_selectables_using_loop()
1475 {
1476         Location* location = session->locations()->auto_loop_location();
1477         list<Selectable *> touched;
1478
1479         if (location == 0 || (location->end() - location->start() <= 1))  {
1480                 return;
1481         }
1482
1483         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1484                 if ((*iter)->hidden()) {
1485                         continue;
1486                 }
1487                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1488         }
1489         begin_reversible_command (_("select all from loop"));
1490         selection->set (touched);
1491         commit_reversible_command ();
1492
1493 }
1494
1495 void
1496 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1497 {
1498         nframes_t start;
1499         nframes_t end;
1500         list<Selectable *> touched;
1501
1502         if (after) {
1503                 begin_reversible_command (_("select all after cursor"));
1504                 start = cursor->current_frame ;
1505                 end = session->current_end_frame();
1506         } else {
1507                 if (cursor->current_frame > 0) {
1508                         begin_reversible_command (_("select all before cursor"));
1509                         start = 0;
1510                         end = cursor->current_frame - 1;
1511                 } else {
1512                         return;
1513                 }
1514         }
1515
1516         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1517                 if ((*iter)->hidden()) {
1518                         continue;
1519                 }
1520                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1521         }
1522         selection->set (touched);
1523         commit_reversible_command ();
1524 }
1525
1526 void
1527 Editor::select_all_selectables_between_cursors (Cursor *cursor, Cursor *other_cursor)
1528 {
1529         nframes_t start;
1530         nframes_t end;
1531         list<Selectable *> touched;
1532         bool  other_cursor_is_first = cursor->current_frame > other_cursor->current_frame;
1533
1534         if (cursor->current_frame == other_cursor->current_frame) {
1535                 return;
1536         }
1537
1538         begin_reversible_command (_("select all between cursors"));
1539         if (other_cursor_is_first) {
1540                 start = other_cursor->current_frame;
1541                 end = cursor->current_frame - 1;
1542                 
1543         } else {
1544                 start = cursor->current_frame;
1545                 end = other_cursor->current_frame - 1;
1546         }
1547         
1548         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1549                 if ((*iter)->hidden()) {
1550                         continue;
1551                 }
1552                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1553         }
1554         selection->set (touched);
1555         commit_reversible_command ();
1556 }
1557
1558 void
1559 Editor::amplitude_zoom_step (bool in)
1560 {
1561         gdouble zoom = 1.0;
1562
1563         if (in) {
1564                 zoom *= 2.0;
1565         } else {
1566                 if (zoom > 2.0) {
1567                         zoom /= 2.0;
1568                 } else {
1569                         zoom = 1.0;
1570                 }
1571         }
1572
1573 #ifdef FIX_FOR_CANVAS
1574         /* XXX DO SOMETHING */
1575 #endif
1576 }       
1577
1578
1579 /* DELETION */
1580
1581
1582 void
1583 Editor::delete_sample_forward ()
1584 {
1585 }
1586
1587 void
1588 Editor::delete_sample_backward ()
1589 {
1590 }
1591
1592 void
1593 Editor::delete_screen ()
1594 {
1595 }
1596
1597 /* SEARCH */
1598
1599 void
1600 Editor::search_backwards ()
1601 {
1602         /* what ? */
1603 }
1604
1605 void
1606 Editor::search_forwards ()
1607 {
1608         /* what ? */
1609 }
1610
1611 /* MARKS */
1612
1613 void
1614 Editor::jump_forward_to_mark ()
1615 {
1616         if (!session) {
1617                 return;
1618         }
1619         
1620         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
1621
1622         if (location) {
1623                 session->request_locate (location->start(), session->transport_rolling());
1624         } else {
1625                 session->request_locate (session->current_end_frame());
1626         }
1627 }
1628
1629 void
1630 Editor::jump_backward_to_mark ()
1631 {
1632         if (!session) {
1633                 return;
1634         }
1635
1636         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
1637         
1638         if (location) {
1639                 session->request_locate (location->start(), session->transport_rolling());
1640         } else {
1641                 session->goto_start ();
1642         }
1643 }
1644
1645 void
1646 Editor::set_mark ()
1647 {
1648         nframes_t pos;
1649         float prefix;
1650         bool was_floating;
1651         string markername;
1652
1653         if (get_prefix (prefix, was_floating)) {
1654                 pos = session->audible_frame ();
1655         } else {
1656                 if (was_floating) {
1657                         pos = (nframes_t) floor (prefix * session->frame_rate ());
1658                 } else {
1659                         pos = (nframes_t) floor (prefix);
1660                 }
1661         }
1662
1663         session->locations()->next_available_name(markername,"mark");
1664         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
1665 }
1666
1667 void
1668 Editor::clear_markers ()
1669 {
1670         if (session) {
1671                 session->begin_reversible_command (_("clear markers"));
1672                 XMLNode &before = session->locations()->get_state();
1673                 session->locations()->clear_markers ();
1674                 XMLNode &after = session->locations()->get_state();
1675                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1676                 session->commit_reversible_command ();
1677         }
1678 }
1679
1680 void
1681 Editor::clear_ranges ()
1682 {
1683         if (session) {
1684                 session->begin_reversible_command (_("clear ranges"));
1685                 XMLNode &before = session->locations()->get_state();
1686                 
1687                 Location * looploc = session->locations()->auto_loop_location();
1688                 Location * punchloc = session->locations()->auto_punch_location();
1689                 
1690                 session->locations()->clear_ranges ();
1691                 // re-add these
1692                 if (looploc) session->locations()->add (looploc);
1693                 if (punchloc) session->locations()->add (punchloc);
1694                 
1695                 XMLNode &after = session->locations()->get_state();
1696                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1697                 session->commit_reversible_command ();
1698         }
1699 }
1700
1701 void
1702 Editor::clear_locations ()
1703 {
1704         session->begin_reversible_command (_("clear locations"));
1705         XMLNode &before = session->locations()->get_state();
1706         session->locations()->clear ();
1707         XMLNode &after = session->locations()->get_state();
1708         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1709         session->commit_reversible_command ();
1710         session->locations()->clear ();
1711 }
1712
1713 void
1714 Editor::unhide_markers ()
1715 {
1716         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1717                 Location *l = (*i).first;
1718                 if (l->is_hidden() && l->is_mark()) {
1719                         l->set_hidden(false, this);
1720                 }
1721         }
1722 }
1723
1724 void
1725 Editor::unhide_ranges ()
1726 {
1727         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1728                 Location *l = (*i).first;
1729                 if (l->is_hidden() && l->is_range_marker()) { 
1730                         l->set_hidden(false, this);
1731                 }
1732         }
1733 }
1734
1735 /* INSERT/REPLACE */
1736
1737 void
1738 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
1739 {
1740         double wx, wy;
1741         double cx, cy;
1742         TimeAxisView *tv;
1743         nframes_t where;
1744         AudioTimeAxisView *atv = 0;
1745         boost::shared_ptr<Playlist> playlist;
1746         
1747         track_canvas.window_to_world (x, y, wx, wy);
1748         wx += horizontal_adjustment.get_value();
1749         wy += vertical_adjustment.get_value();
1750
1751         GdkEvent event;
1752         event.type = GDK_BUTTON_RELEASE;
1753         event.button.x = wx;
1754         event.button.y = wy;
1755         
1756         where = event_frame (&event, &cx, &cy);
1757
1758         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1759                 /* clearly outside canvas area */
1760                 return;
1761         }
1762         
1763         if ((tv = trackview_by_y_position (cy)) == 0) {
1764                 return;
1765         }
1766         
1767         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
1768                 return;
1769         }
1770
1771         if ((playlist = atv->playlist()) == 0) {
1772                 return;
1773         }
1774         
1775         cerr << "drop target playlist, UC  = " << playlist.use_count() << endl;
1776
1777         snap_to (where);
1778         
1779         begin_reversible_command (_("insert dragged region"));
1780         XMLNode &before = playlist->get_state();
1781         cerr << "pre add target playlist, UC  = " << playlist.use_count() << endl;
1782         playlist->add_region (RegionFactory::create (region), where, 1.0);
1783         cerr << "post add target playlist, UC  = " << playlist.use_count() << endl;
1784         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1785         commit_reversible_command ();
1786
1787         cerr << "post drop target playlist, UC  = " << playlist.use_count() << endl;
1788 }
1789
1790 void
1791 Editor::insert_region_list_selection (float times)
1792 {
1793         RouteTimeAxisView *tv = 0;
1794         boost::shared_ptr<Playlist> playlist;
1795
1796         if (clicked_audio_trackview != 0) {
1797                 tv = clicked_audio_trackview;
1798         } else if (!selection->tracks.empty()) {
1799                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
1800                         return;
1801                 }
1802         } else {
1803                 return;
1804         }
1805
1806         if ((playlist = tv->playlist()) == 0) {
1807                 return;
1808         }
1809         
1810         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
1811         
1812         if (selected->count_selected_rows() != 1) {
1813                 return;
1814         }
1815         
1816         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
1817
1818         /* only one row selected, so rows.begin() is it */
1819
1820         TreeIter iter;
1821
1822         if ((iter = region_list_model->get_iter (*rows.begin()))) {
1823
1824                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
1825                 
1826                 begin_reversible_command (_("insert region"));
1827                 XMLNode &before = playlist->get_state();
1828                 playlist->add_region ((RegionFactory::create (region)), edit_cursor->current_frame, times);
1829                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
1830                 commit_reversible_command ();
1831         } 
1832 }
1833
1834 /* BUILT-IN EFFECTS */
1835
1836 void
1837 Editor::reverse_selection ()
1838 {
1839
1840 }
1841
1842 /* GAIN ENVELOPE EDITING */
1843
1844 void
1845 Editor::edit_envelope ()
1846 {
1847 }
1848
1849 /* PLAYBACK */
1850
1851 void
1852 Editor::toggle_playback (bool with_abort)
1853 {
1854         if (!session) {
1855                 return;
1856         }
1857
1858         switch (Config->get_slave_source()) {
1859         case None:
1860         case JACK:
1861                 break;
1862         default:
1863                 /* transport controlled by the master */
1864                 return;
1865         }
1866
1867         if (session->is_auditioning()) {
1868                 session->cancel_audition ();
1869                 return;
1870         }
1871         
1872         if (session->transport_rolling()) {
1873                 session->request_stop (with_abort);
1874                 if (session->get_play_loop()) {
1875                         session->request_play_loop (false);
1876                 }
1877         } else {
1878                 session->request_transport_speed (1.0f);
1879         }
1880 }
1881
1882 void
1883 Editor::play_from_start ()
1884 {
1885         session->request_locate (session->current_start_frame(), true);
1886 }
1887
1888 void
1889 Editor::play_from_edit_cursor ()
1890 {
1891        session->request_locate (edit_cursor->current_frame, true);
1892 }
1893
1894 void
1895 Editor::play_selection ()
1896 {
1897         if (selection->time.empty()) {
1898                 return;
1899         }
1900
1901         session->request_play_range (true);
1902 }
1903
1904 void
1905 Editor::play_selected_region ()
1906 {
1907         if (!selection->regions.empty()) {
1908                 RegionView *rv = *(selection->regions.begin());
1909
1910                 session->request_bounded_roll (rv->region()->position(), rv->region()->last_frame());   
1911         }
1912 }
1913
1914 void
1915 Editor::loop_selected_region ()
1916 {
1917         if (!selection->regions.empty()) {
1918                 RegionView *rv = *(selection->regions.begin());
1919                 Location* tll;
1920
1921                 if ((tll = transport_loop_location()) != 0)  {
1922
1923                         tll->set (rv->region()->position(), rv->region()->last_frame());
1924                         
1925                         // enable looping, reposition and start rolling
1926
1927                         session->request_play_loop (true);
1928                         session->request_locate (tll->start(), false);
1929                         session->request_transport_speed (1.0f);
1930                 }
1931         }
1932 }
1933
1934 void
1935 Editor::play_location (Location& location)
1936 {
1937         if (location.start() <= location.end()) {
1938                 return;
1939         }
1940
1941         session->request_bounded_roll (location.start(), location.end());
1942 }
1943
1944 void
1945 Editor::loop_location (Location& location)
1946 {
1947         if (location.start() <= location.end()) {
1948                 return;
1949         }
1950
1951         Location* tll;
1952
1953         if ((tll = transport_loop_location()) != 0) {
1954                 tll->set (location.start(), location.end());
1955
1956                 // enable looping, reposition and start rolling
1957                 session->request_play_loop (true);
1958                 session->request_locate (tll->start(), true);
1959         }
1960 }
1961
1962 void
1963 Editor::raise_region ()
1964 {
1965         selection->foreach_region (&Region::raise);
1966 }
1967
1968 void
1969 Editor::raise_region_to_top ()
1970 {
1971         selection->foreach_region (&Region::raise_to_top);
1972 }
1973
1974 void
1975 Editor::lower_region ()
1976 {
1977         selection->foreach_region (&Region::lower);
1978 }
1979
1980 void
1981 Editor::lower_region_to_bottom ()
1982 {
1983         selection->foreach_region (&Region::lower_to_bottom);
1984 }
1985
1986 void
1987 Editor::edit_region ()
1988 {
1989         if (clicked_regionview == 0) {
1990                 return;
1991         }
1992         
1993         clicked_regionview->show_region_editor ();
1994 }
1995
1996 void
1997 Editor::rename_region ()
1998 {
1999         Dialog dialog;
2000         Entry  entry;
2001         Button ok_button (_("OK"));
2002         Button cancel_button (_("Cancel"));
2003
2004         if (selection->regions.empty()) {
2005                 return;
2006         }
2007
2008         dialog.set_title (_("ardour: rename region"));
2009         dialog.set_name ("RegionRenameWindow");
2010         dialog.set_size_request (300, -1);
2011         dialog.set_position (Gtk::WIN_POS_MOUSE);
2012         dialog.set_modal (true);
2013
2014         dialog.get_vbox()->set_border_width (10);
2015         dialog.get_vbox()->pack_start (entry);
2016         dialog.get_action_area()->pack_start (ok_button);
2017         dialog.get_action_area()->pack_start (cancel_button);
2018
2019         entry.set_name ("RegionNameDisplay");
2020         ok_button.set_name ("EditorGTKButton");
2021         cancel_button.set_name ("EditorGTKButton");
2022
2023         region_renamed = false;
2024
2025         entry.signal_activate().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2026         ok_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), true));
2027         cancel_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::rename_region_finished), false));
2028
2029         /* recurse */
2030
2031         dialog.show_all ();
2032         Main::run ();
2033
2034         if (region_renamed) {
2035                 (*selection->regions.begin())->region()->set_name (entry.get_text());
2036                 redisplay_regions ();
2037         }
2038 }
2039
2040 void
2041 Editor::rename_region_finished (bool status)
2042
2043 {
2044         region_renamed = status;
2045         Main::quit ();
2046 }
2047
2048 void
2049 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2050 {
2051         if (session->is_auditioning()) {
2052                 session->cancel_audition ();
2053         } 
2054
2055         // note: some potential for creativity here, because region doesn't
2056         // have to belong to the playlist that Route is handling
2057
2058         // bool was_soloed = route.soloed();
2059
2060         route.set_solo (true, this);
2061         
2062         session->request_bounded_roll (region->position(), region->position() + region->length());
2063         
2064         /* XXX how to unset the solo state ? */
2065 }
2066
2067 void
2068 Editor::audition_selected_region ()
2069 {
2070         if (!selection->regions.empty()) {
2071                 RegionView* rv = *(selection->regions.begin());
2072                 session->audition_region (rv->region());
2073         }
2074 }
2075
2076 void
2077 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2078 {
2079         session->audition_region (region);
2080 }
2081
2082 void
2083 Editor::build_interthread_progress_window ()
2084 {
2085         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2086
2087         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2088         
2089         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2090         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2091
2092         // GTK2FIX: this button needs a modifiable label
2093
2094         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2095         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2096
2097         interthread_cancel_button.add (interthread_cancel_label);
2098
2099         interthread_progress_window->set_default_size (200, 100);
2100 }
2101
2102 void
2103 Editor::interthread_cancel_clicked ()
2104 {
2105         if (current_interthread_info) {
2106                 current_interthread_info->cancel = true;
2107         }
2108 }
2109
2110 void
2111 Editor::region_from_selection ()
2112 {
2113         if (clicked_trackview == 0) {
2114                 return;
2115         }
2116
2117         if (selection->time.empty()) {
2118                 return;
2119         }
2120
2121         nframes_t start = selection->time[clicked_selection].start;
2122         nframes_t end = selection->time[clicked_selection].end;
2123
2124         nframes_t selection_cnt = end - start + 1;
2125         
2126         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2127                 boost::shared_ptr<AudioRegion> current;
2128                 boost::shared_ptr<Region> current_r;
2129                 boost::shared_ptr<Playlist> pl;
2130
2131                 nframes_t internal_start;
2132                 string new_name;
2133
2134                 if ((pl = (*i)->playlist()) == 0) {
2135                         continue;
2136                 }
2137
2138                 if ((current_r = pl->top_region_at (start)) == 0) {
2139                         continue;
2140                 }
2141
2142                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2143                 // FIXME: audio only
2144                 if (current != 0) {
2145                         internal_start = start - current->position();
2146                         session->region_name (new_name, current->name(), true);
2147                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2148                 }
2149         }
2150 }       
2151
2152 void
2153 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2154 {
2155         if (selection->time.empty() || selection->tracks.empty()) {
2156                 return;
2157         }
2158
2159         nframes_t start = selection->time[clicked_selection].start;
2160         nframes_t end = selection->time[clicked_selection].end;
2161         
2162         sort_track_selection ();
2163
2164         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2165
2166                 boost::shared_ptr<AudioRegion> current;
2167                 boost::shared_ptr<Region> current_r;
2168                 boost::shared_ptr<Playlist> playlist;
2169                 nframes_t internal_start;
2170                 string new_name;
2171
2172                 if ((playlist = (*i)->playlist()) == 0) {
2173                         continue;
2174                 }
2175
2176                 if ((current_r = playlist->top_region_at(start)) == 0) {
2177                         continue;
2178                 }
2179
2180                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2181                         continue;
2182                 }
2183         
2184                 internal_start = start - current->position();
2185                 session->region_name (new_name, current->name(), true);
2186                 
2187                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2188         }
2189 }
2190
2191 void
2192 Editor::split_multichannel_region ()
2193 {
2194         vector<AudioRegion*> v;
2195
2196         AudioRegionView* clicked_arv = dynamic_cast<AudioRegionView*>(clicked_regionview);
2197         
2198         if (!clicked_arv || clicked_arv->audio_region()->n_channels() < 2) {
2199                 return;
2200         }
2201
2202         clicked_arv->audio_region()->separate_by_channel (*session, v);
2203
2204         /* nothing else to do, really */
2205 }
2206
2207 void
2208 Editor::new_region_from_selection ()
2209 {
2210         region_from_selection ();
2211         cancel_selection ();
2212 }
2213
2214 void
2215 Editor::separate_region_from_selection ()
2216 {
2217         bool doing_undo = false;
2218
2219         if (selection->time.empty()) {
2220                 return;
2221         }
2222
2223         boost::shared_ptr<Playlist> playlist;
2224                 
2225         sort_track_selection ();
2226
2227         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2228
2229                 AudioTimeAxisView* atv;
2230
2231                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2232
2233                         if (atv->is_audio_track()) {
2234                                         
2235                                 if ((playlist = atv->playlist()) != 0) {
2236                                         if (!doing_undo) {
2237                                                 begin_reversible_command (_("separate"));
2238                                                 doing_undo = true;
2239                                         }
2240                                         XMLNode *before;
2241                                         if (doing_undo) 
2242                                             before = &(playlist->get_state());
2243                         
2244                                         /* XXX need to consider musical time selections here at some point */
2245
2246                                         double speed = atv->get_diskstream()->speed();
2247
2248                                         for (list<AudioRange>::iterator t = selection->time.begin(); t != selection->time.end(); ++t) {
2249                                                 playlist->partition ((nframes_t)((*t).start * speed), (nframes_t)((*t).end * speed), true);
2250                                         }
2251
2252                                         if (doing_undo) 
2253                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2254                                 }
2255                         }
2256                 }
2257         }
2258
2259         if (doing_undo) commit_reversible_command ();
2260 }
2261
2262 void
2263 Editor::separate_regions_using_location (Location& loc)
2264 {
2265         bool doing_undo = false;
2266
2267         if (loc.is_mark()) {
2268                 return;
2269         }
2270
2271         boost::shared_ptr<Playlist> playlist;
2272
2273         /* XXX i'm unsure as to whether this should operate on selected tracks only 
2274            or the entire enchillada. uncomment the below line to correct the behaviour 
2275            (currently set for all tracks)
2276         */
2277
2278         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
2279         //for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2280
2281                 AudioTimeAxisView* atv;
2282
2283                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2284
2285                         if (atv->is_audio_track()) {
2286                                         
2287                                 if ((playlist = atv->playlist()) != 0) {
2288                                         XMLNode *before;
2289                                         if (!doing_undo) {
2290                                                 begin_reversible_command (_("separate"));
2291                                                 doing_undo = true;
2292                                         }
2293                                         if (doing_undo) 
2294                                             before = &(playlist->get_state());
2295                                             
2296                         
2297                                         /* XXX need to consider musical time selections here at some point */
2298
2299                                         double speed = atv->get_diskstream()->speed();
2300
2301
2302                                         playlist->partition ((nframes_t)(loc.start() * speed), (nframes_t)(loc.end() * speed), true);
2303                                         if (doing_undo) 
2304                                             session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2305                                 }
2306                         }
2307                 }
2308         }
2309
2310         if (doing_undo) commit_reversible_command ();
2311 }
2312
2313 void
2314 Editor::crop_region_to_selection ()
2315 {
2316         if (selection->time.empty()) {
2317                 return;
2318         }
2319
2320         vector<boost::shared_ptr<Playlist> > playlists;
2321         boost::shared_ptr<Playlist> playlist;
2322
2323         if (clicked_trackview != 0) {
2324
2325                 if ((playlist = clicked_trackview->playlist()) == 0) {
2326                         return;
2327                 }
2328
2329                 playlists.push_back (playlist);
2330
2331         } else {
2332                 
2333                 sort_track_selection ();
2334
2335                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2336
2337                         AudioTimeAxisView* atv;
2338
2339                         if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2340
2341                                 if (atv->is_audio_track()) {
2342                                         
2343                                         if ((playlist = atv->playlist()) != 0) {
2344                                                 playlists.push_back (playlist);
2345                                         }
2346                                 }
2347                         }
2348                 }
2349         }
2350
2351         if (!playlists.empty()) {
2352
2353                 nframes_t start;
2354                 nframes_t end;
2355                 nframes_t cnt;
2356
2357                 begin_reversible_command (_("trim to selection"));
2358
2359                 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2360                         
2361                         boost::shared_ptr<Region> region;
2362                         
2363                         start = selection->time.start();
2364
2365                         if ((region = (*i)->top_region_at(start)) == 0) {
2366                                 continue;
2367                         }
2368                         
2369                         /* now adjust lengths to that we do the right thing
2370                            if the selection extends beyond the region
2371                         */
2372                         
2373                         start = max (start, region->position());
2374                         end = min (selection->time.end_frame(), start + region->length() - 1);
2375                         cnt = end - start + 1;
2376
2377                         XMLNode &before = (*i)->get_state();
2378                         region->trim_to (start, cnt, this);
2379                         XMLNode &after = (*i)->get_state();
2380                         session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
2381                 }
2382
2383                 commit_reversible_command ();
2384         }
2385 }               
2386
2387 void
2388 Editor::region_fill_track ()
2389 {
2390         nframes_t end;
2391
2392         if (!session || selection->regions.empty()) {
2393                 return;
2394         }
2395
2396         end = session->current_end_frame ();
2397
2398         begin_reversible_command (_("region fill"));
2399
2400         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2401
2402                 boost::shared_ptr<Region> region ((*i)->region());
2403                 
2404                 // FIXME
2405                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
2406                 if (!ar)
2407                         continue;
2408
2409                 boost::shared_ptr<Playlist> pl = region->playlist();
2410
2411                 if (end <= region->last_frame()) {
2412                         return;
2413                 }
2414
2415                 double times = (double) (end - region->last_frame()) / (double) region->length();
2416
2417                 if (times == 0) {
2418                         return;
2419                 }
2420
2421                 XMLNode &before = pl->get_state();
2422                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
2423                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
2424         }
2425
2426         commit_reversible_command ();
2427 }
2428
2429 void
2430 Editor::region_fill_selection ()
2431 {
2432         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2433                 return;
2434         }
2435
2436         if (selection->time.empty()) {
2437                 return;
2438         }
2439
2440
2441         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2442
2443         if (selected->count_selected_rows() != 1) {
2444                 return;
2445         }
2446
2447         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
2448         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
2449
2450         nframes_t start = selection->time[clicked_selection].start;
2451         nframes_t end = selection->time[clicked_selection].end;
2452
2453         boost::shared_ptr<Playlist> playlist; 
2454
2455         if (selection->tracks.empty()) {
2456                 return;
2457         }
2458
2459         nframes_t selection_length = end - start;
2460         float times = (float)selection_length / region->length();
2461         
2462         begin_reversible_command (_("fill selection"));
2463         
2464         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2465
2466                 if ((playlist = (*i)->playlist()) == 0) {
2467                         continue;
2468                 }               
2469                 
2470                 XMLNode &before = playlist->get_state();
2471                 playlist->add_region (RegionFactory::create (region), start, times);
2472                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2473         }
2474         
2475         commit_reversible_command ();                   
2476 }
2477
2478 void
2479 Editor::set_a_regions_sync_position (boost::shared_ptr<Region> region, nframes_t position)
2480 {
2481
2482         if (!region->covers (position)) {
2483           error << _("Programming error. that region doesn't cover that position") << __FILE__ << " +" << __LINE__ << endmsg;
2484                 return;
2485         }
2486         begin_reversible_command (_("set region sync position"));
2487         XMLNode &before = region->playlist()->get_state();
2488         region->set_sync_position (position);
2489         XMLNode &after = region->playlist()->get_state();
2490         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2491         commit_reversible_command ();
2492 }
2493
2494 void
2495 Editor::set_region_sync_from_edit_cursor ()
2496 {
2497         if (clicked_regionview == 0) {
2498                 return;
2499         }
2500
2501         if (!clicked_regionview->region()->covers (edit_cursor->current_frame)) {
2502                 error << _("Place the edit cursor at the desired sync point") << endmsg;
2503                 return;
2504         }
2505
2506         boost::shared_ptr<Region> region (clicked_regionview->region());
2507         begin_reversible_command (_("set sync from edit cursor"));
2508         XMLNode &before = region->playlist()->get_state();
2509         region->set_sync_position (edit_cursor->current_frame);
2510         XMLNode &after = region->playlist()->get_state();
2511         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2512         commit_reversible_command ();
2513 }
2514
2515 void
2516 Editor::remove_region_sync ()
2517 {
2518         if (clicked_regionview) {
2519                 boost::shared_ptr<Region> region (clicked_regionview->region());
2520                 begin_reversible_command (_("remove sync"));
2521                 XMLNode &before = region->playlist()->get_state();
2522                 region->clear_sync_position ();
2523                 XMLNode &after = region->playlist()->get_state();
2524                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2525                 commit_reversible_command ();
2526         }
2527 }
2528
2529 void
2530 Editor::naturalize ()
2531 {
2532         if (selection->regions.empty()) {
2533                 return;
2534         }
2535         begin_reversible_command (_("naturalize"));
2536         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2537                 XMLNode &before = (*i)->region()->get_state();
2538                 (*i)->region()->move_to_natural_position (this);
2539                 XMLNode &after = (*i)->region()->get_state();
2540                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
2541         }
2542         commit_reversible_command ();
2543 }
2544
2545 void
2546 Editor::align (RegionPoint what)
2547 {
2548         align_selection (what, edit_cursor->current_frame);
2549 }
2550
2551 void
2552 Editor::align_relative (RegionPoint what)
2553 {
2554         align_selection_relative (what, edit_cursor->current_frame);
2555 }
2556
2557 struct RegionSortByTime {
2558     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
2559             return a->region()->position() < b->region()->position();
2560     }
2561 };
2562
2563 void
2564 Editor::align_selection_relative (RegionPoint point, nframes_t position)
2565 {
2566         if (selection->regions.empty()) {
2567                 return;
2568         }
2569
2570         nframes_t distance;
2571         nframes_t pos = 0;
2572         int dir;
2573
2574         list<RegionView*> sorted;
2575         selection->regions.by_position (sorted);
2576         boost::shared_ptr<Region> r ((*sorted.begin())->region());
2577
2578         switch (point) {
2579         case Start:
2580                 pos = r->first_frame ();
2581                 break;
2582
2583         case End:
2584                 pos = r->last_frame();
2585                 break;
2586
2587         case SyncPoint:
2588                 pos = r->adjust_to_sync (r->first_frame());
2589                 break;  
2590         }
2591
2592         if (pos > position) {
2593                 distance = pos - position;
2594                 dir = -1;
2595         } else {
2596                 distance = position - pos;
2597                 dir = 1;
2598         }
2599
2600         begin_reversible_command (_("align selection (relative)"));
2601
2602         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2603
2604                 boost::shared_ptr<Region> region ((*i)->region());
2605
2606                 XMLNode &before = region->playlist()->get_state();
2607                 
2608                 if (dir > 0) {
2609                         region->set_position (region->position() + distance, this);
2610                 } else {
2611                         region->set_position (region->position() - distance, this);
2612                 }
2613
2614                 XMLNode &after = region->playlist()->get_state();
2615                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2616
2617         }
2618
2619         commit_reversible_command ();
2620 }
2621
2622 void
2623 Editor::align_selection (RegionPoint point, nframes_t position)
2624 {
2625         if (selection->regions.empty()) {
2626                 return;
2627         }
2628
2629         begin_reversible_command (_("align selection"));
2630
2631         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2632                 align_region_internal ((*i)->region(), point, position);
2633         }
2634
2635         commit_reversible_command ();
2636 }
2637
2638 void
2639 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2640 {
2641         begin_reversible_command (_("align region"));
2642         align_region_internal (region, point, position);
2643         commit_reversible_command ();
2644 }
2645
2646 void
2647 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes_t position)
2648 {
2649         XMLNode &before = region->playlist()->get_state();
2650
2651         switch (point) {
2652         case SyncPoint:
2653                 region->set_position (region->adjust_to_sync (position), this);
2654                 break;
2655
2656         case End:
2657                 if (position > region->length()) {
2658                         region->set_position (position - region->length(), this);
2659                 }
2660                 break;
2661
2662         case Start:
2663                 region->set_position (position, this);
2664                 break;
2665         }
2666
2667         XMLNode &after = region->playlist()->get_state();
2668         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2669 }       
2670
2671 void
2672 Editor::trim_region_to_edit_cursor ()
2673 {
2674         if (clicked_regionview == 0) {
2675                 return;
2676         }
2677
2678         boost::shared_ptr<Region> region (clicked_regionview->region());
2679
2680         float speed = 1.0f;
2681         AudioTimeAxisView *atav;
2682
2683         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2684                 if (atav->get_diskstream() != 0) {
2685                         speed = atav->get_diskstream()->speed();
2686                 }
2687         }
2688
2689         begin_reversible_command (_("trim to edit"));
2690         XMLNode &before = region->playlist()->get_state();
2691         region->trim_end( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2692         XMLNode &after = region->playlist()->get_state();
2693         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2694         commit_reversible_command ();
2695 }
2696
2697 void
2698 Editor::trim_region_from_edit_cursor ()
2699 {
2700         if (clicked_regionview == 0) {
2701                 return;
2702         }
2703
2704         boost::shared_ptr<Region> region (clicked_regionview->region());
2705
2706         float speed = 1.0f;
2707         AudioTimeAxisView *atav;
2708
2709         if ( clicked_trackview != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(clicked_trackview)) != 0 ) {
2710                 if (atav->get_diskstream() != 0) {
2711                         speed = atav->get_diskstream()->speed();
2712                 }
2713         }
2714
2715         begin_reversible_command (_("trim to edit"));
2716         XMLNode &before = region->playlist()->get_state();
2717         region->trim_front ( session_frame_to_track_frame(edit_cursor->current_frame, speed), this);
2718         XMLNode &after = region->playlist()->get_state();
2719         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
2720         commit_reversible_command ();
2721 }
2722
2723 void
2724 Editor::unfreeze_route ()
2725 {
2726         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2727                 return;
2728         }
2729         
2730         clicked_audio_trackview->audio_track()->unfreeze ();
2731 }
2732
2733 void*
2734 Editor::_freeze_thread (void* arg)
2735 {
2736         PBD::ThreadCreated (pthread_self(), X_("Freeze"));
2737         return static_cast<Editor*>(arg)->freeze_thread ();
2738 }
2739
2740 void*
2741 Editor::freeze_thread ()
2742 {
2743         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
2744         return 0;
2745 }
2746
2747 gint
2748 Editor::freeze_progress_timeout (void *arg)
2749 {
2750         interthread_progress_bar.set_fraction (current_interthread_info->progress/100);
2751         return !(current_interthread_info->done || current_interthread_info->cancel);
2752 }
2753
2754 void
2755 Editor::freeze_route ()
2756 {
2757         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
2758                 return;
2759         }
2760         
2761         InterThreadInfo itt;
2762
2763         if (interthread_progress_window == 0) {
2764                 build_interthread_progress_window ();
2765         }
2766         
2767         interthread_progress_window->set_title (_("ardour: freeze"));
2768         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
2769         interthread_progress_window->show_all ();
2770         interthread_progress_bar.set_fraction (0.0f);
2771         interthread_progress_label.set_text ("");
2772         interthread_cancel_label.set_text (_("Cancel Freeze"));
2773         current_interthread_info = &itt;
2774
2775         interthread_progress_connection = 
2776           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
2777
2778         itt.done = false;
2779         itt.cancel = false;
2780         itt.progress = 0.0f;
2781         
2782         pthread_attr_t attr;
2783         pthread_attr_init(&attr);
2784         pthread_attr_setstacksize(&attr, 500000);
2785
2786         pthread_create (&itt.thread, &attr, _freeze_thread, this);
2787
2788         pthread_attr_destroy(&attr);
2789
2790         track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
2791
2792         while (!itt.done && !itt.cancel) {
2793                 gtk_main_iteration ();
2794         }
2795
2796         interthread_progress_connection.disconnect ();
2797         interthread_progress_window->hide_all ();
2798         current_interthread_info = 0;
2799         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
2800 }
2801
2802 void
2803 Editor::bounce_range_selection ()
2804 {
2805         if (selection->time.empty()) {
2806                 return;
2807         }
2808
2809         TrackViewList *views = get_valid_views (selection->time.track, selection->time.group);
2810
2811         nframes_t start = selection->time[clicked_selection].start;
2812         nframes_t end = selection->time[clicked_selection].end;
2813         nframes_t cnt = end - start + 1;
2814         
2815         begin_reversible_command (_("bounce range"));
2816
2817         for (TrackViewList::iterator i = views->begin(); i != views->end(); ++i) {
2818
2819                 AudioTimeAxisView* atv;
2820
2821                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
2822                         continue;
2823                 }
2824                 
2825                 boost::shared_ptr<Playlist> playlist;
2826                 
2827                 if ((playlist = atv->playlist()) == 0) {
2828                         return;
2829                 }
2830
2831                 InterThreadInfo itt;
2832                 
2833                 itt.done = false;
2834                 itt.cancel = false;
2835                 itt.progress = false;
2836                 
2837                 XMLNode &before = playlist->get_state();
2838                 atv->audio_track()->bounce_range (start, cnt, itt);
2839                 XMLNode &after = playlist->get_state();
2840                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
2841         }
2842         
2843         commit_reversible_command ();
2844         
2845         delete views;
2846 }
2847
2848 void
2849 Editor::cut ()
2850 {
2851         cut_copy (Cut);
2852 }
2853
2854 void
2855 Editor::copy ()
2856 {
2857         cut_copy (Copy);
2858 }
2859
2860 void 
2861 Editor::cut_copy (CutCopyOp op)
2862 {
2863         /* only cancel selection if cut/copy is successful.*/
2864
2865         string opname;
2866
2867         switch (op) {
2868         case Cut:
2869                 opname = _("cut");
2870                 break;
2871         case Copy:
2872                 opname = _("copy");
2873                 break;
2874         case Clear:
2875                 opname = _("clear");
2876                 break;
2877         }
2878         
2879         cut_buffer->clear ();
2880
2881         switch (current_mouse_mode()) {
2882         case MouseObject: 
2883                 if (!selection->regions.empty() || !selection->points.empty()) {
2884
2885                         begin_reversible_command (opname + _(" objects"));
2886
2887                         if (!selection->regions.empty()) {
2888                                 
2889                                 cut_copy_regions (op);
2890                                 
2891                                 if (op == Cut) {
2892                                         selection->clear_regions ();
2893                                 }
2894                         }
2895
2896                         if (!selection->points.empty()) {
2897                                 cut_copy_points (op);
2898
2899                                 if (op == Cut) {
2900                                         selection->clear_points ();
2901                                 }
2902                         }
2903
2904                         commit_reversible_command ();   
2905                 }
2906                 break;
2907                 
2908         case MouseRange:
2909                 if (!selection->time.empty()) {
2910
2911                         begin_reversible_command (opname + _(" range"));
2912                         cut_copy_ranges (op);
2913                         commit_reversible_command ();
2914
2915                         if (op == Cut) {
2916                                 selection->clear_time ();
2917                         }
2918                         
2919                 }
2920                 break;
2921                 
2922         default:
2923                 break;
2924         }
2925 }
2926
2927 void
2928 Editor::cut_copy_points (CutCopyOp op)
2929 {
2930         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
2931
2932                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
2933
2934                 if (atv) {
2935                         atv->cut_copy_clear_objects (selection->points, op);
2936                 } 
2937         }
2938 }
2939
2940 struct PlaylistState {
2941     boost::shared_ptr<Playlist> playlist;
2942     XMLNode*  before;
2943 };
2944
2945 struct lt_playlist {
2946     bool operator () (const PlaylistState& a, const PlaylistState& b) {
2947             return a.playlist < b.playlist;
2948     }
2949 };
2950         
2951 struct PlaylistMapping { 
2952     TimeAxisView* tv;
2953     boost::shared_ptr<AudioPlaylist> pl;
2954
2955     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
2956 };
2957
2958 void
2959 Editor::cut_copy_regions (CutCopyOp op)
2960 {
2961         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
2962            a map when we want ordered access to both elements. i think.
2963         */
2964
2965         vector<PlaylistMapping> pmap;
2966
2967         nframes_t first_position = max_frames;
2968         
2969         set<PlaylistState, lt_playlist> freezelist;
2970         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
2971         
2972         /* get ordering correct before we cut/copy */
2973         
2974         selection->regions.sort_by_position_and_track ();
2975
2976         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
2977
2978                 first_position = min ((*x)->region()->position(), first_position);
2979
2980                 if (op == Cut || op == Clear) {
2981                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
2982
2983                         if (pl) {
2984
2985                                 PlaylistState before;
2986                                 before.playlist = pl;
2987                                 before.before = &pl->get_state();
2988                                 
2989                                 insert_result = freezelist.insert (before);
2990                                 
2991                                 if (insert_result.second) {
2992                                         pl->freeze ();
2993                                 }
2994                         }
2995                 }
2996
2997                 TimeAxisView* tv = &(*x)->get_trackview();
2998                 vector<PlaylistMapping>::iterator z;
2999
3000                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3001                         if ((*z).tv == tv) {
3002                                 break;
3003                         }
3004                 }
3005                 
3006                 if (z == pmap.end()) {
3007                         pmap.push_back (PlaylistMapping (tv));
3008                 }
3009         }
3010
3011         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ) {
3012
3013                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3014                 
3015                 if (!pl) {
3016                         /* impossible, but this handles it for the future */
3017                         continue;
3018                 }
3019
3020                 TimeAxisView& tv = (*x)->get_trackview();
3021                 boost::shared_ptr<AudioPlaylist> npl;
3022                 RegionSelection::iterator tmp;
3023                 
3024                 tmp = x;
3025                 ++tmp;
3026
3027                 vector<PlaylistMapping>::iterator z;
3028                 
3029                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3030                         if ((*z).tv == &tv) {
3031                                 break;
3032                         }
3033                 }
3034                 
3035                 assert (z != pmap.end());
3036                 
3037                 if (!(*z).pl) {
3038                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3039                         npl->freeze();
3040                         (*z).pl = npl;
3041                 } else {
3042                         npl = (*z).pl;
3043                 }
3044                 
3045                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3046                 
3047                 switch (op) {
3048                 case Cut:
3049                         if (!ar) break;
3050                         
3051                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3052                         pl->remove_region (((*x)->region()));
3053                         break;
3054                         
3055                 case Copy:
3056                         if (!ar) break;
3057                         
3058                         npl->add_region (RegionFactory::create (ar), (*x)->region()->position() - first_position);
3059                         break;
3060                         
3061                 case Clear:
3062                         pl->remove_region (((*x)->region()));
3063                         break;
3064                 }
3065
3066                 x = tmp;
3067         }
3068         
3069         list<boost::shared_ptr<Playlist> > foo;
3070         
3071         /* the pmap is in the same order as the tracks in which selected regions occured */
3072         
3073         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
3074                 (*i).pl->thaw();
3075                 foo.push_back ((*i).pl);
3076         }
3077         
3078         if (!foo.empty()) {
3079                 cut_buffer->set (foo);
3080         }
3081         
3082         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
3083                 (*pl).playlist->thaw ();
3084                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3085         }
3086 }
3087
3088 void
3089 Editor::cut_copy_ranges (CutCopyOp op)
3090 {
3091         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3092                 (*i)->cut_copy_clear (*selection, op);
3093         }
3094 }
3095
3096 void
3097 Editor::paste (float times)
3098 {
3099         paste_internal (edit_cursor->current_frame, times);
3100 }
3101
3102 void
3103 Editor::mouse_paste ()
3104 {
3105         int x, y;
3106         double wx, wy;
3107
3108         track_canvas.get_pointer (x, y);
3109         track_canvas.window_to_world (x, y, wx, wy);
3110         wx += horizontal_adjustment.get_value();
3111         wy += vertical_adjustment.get_value();
3112
3113         GdkEvent event;
3114         event.type = GDK_BUTTON_RELEASE;
3115         event.button.x = wx;
3116         event.button.y = wy;
3117         
3118         nframes_t where = event_frame (&event, 0, 0);
3119         snap_to (where);
3120         paste_internal (where, 1);
3121 }
3122
3123 void
3124 Editor::paste_internal (nframes_t position, float times)
3125 {
3126         bool commit = false;
3127
3128         if (cut_buffer->empty() || selection->tracks.empty()) {
3129                 return;
3130         }
3131
3132         if (position == max_frames) {
3133                 position = edit_cursor->current_frame;
3134         }
3135
3136         begin_reversible_command (_("paste"));
3137
3138         TrackSelection::iterator i;
3139         size_t nth;
3140
3141         /* get everything in the correct order */
3142
3143         sort_track_selection ();
3144
3145         for (nth = 0, i = selection->tracks.begin(); i != selection->tracks.end(); ++i, ++nth) {
3146
3147                 /* undo/redo is handled by individual tracks */
3148
3149                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
3150                         commit = true;
3151                 }
3152         }
3153         
3154         if (commit) {
3155                 commit_reversible_command ();
3156         }
3157 }
3158
3159 void
3160 Editor::paste_named_selection (float times)
3161 {
3162         TrackSelection::iterator t;
3163
3164         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
3165
3166         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
3167                 return;
3168         }
3169
3170         TreeModel::iterator i = selected->get_selected();
3171         NamedSelection* ns = (*i)[named_selection_columns.selection];
3172
3173         list<boost::shared_ptr<Playlist> >::iterator chunk;
3174         list<boost::shared_ptr<Playlist> >::iterator tmp;
3175
3176         chunk = ns->playlists.begin();
3177                 
3178         begin_reversible_command (_("paste chunk"));
3179         
3180         sort_track_selection ();
3181
3182         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
3183                 
3184                 AudioTimeAxisView* atv;
3185                 boost::shared_ptr<Playlist> pl;
3186                 boost::shared_ptr<AudioPlaylist> apl;
3187
3188                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
3189                         continue;
3190                 }
3191
3192                 if ((pl = atv->playlist()) == 0) {
3193                         continue;
3194                 }
3195                 
3196                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
3197                         continue;
3198                 }
3199
3200                 tmp = chunk;
3201                 ++tmp;
3202
3203                 XMLNode &before = apl->get_state();
3204                 apl->paste (*chunk, edit_cursor->current_frame, times);
3205                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
3206
3207                 if (tmp != ns->playlists.end()) {
3208                         chunk = tmp;
3209                 }
3210         }
3211
3212         commit_reversible_command();
3213 }
3214
3215 void
3216 Editor::duplicate_some_regions (RegionSelection& regions, float times)
3217 {
3218         boost::shared_ptr<Playlist> playlist; 
3219         RegionSelection sel = regions; // clear (below) will clear the argument list
3220                 
3221         begin_reversible_command (_("duplicate region"));
3222
3223         selection->clear_regions ();
3224
3225         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
3226
3227                 boost::shared_ptr<Region> r ((*i)->region());
3228
3229                 TimeAxisView& tv = (*i)->get_time_axis_view();
3230                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
3231                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
3232                 
3233                 playlist = (*i)->region()->playlist();
3234                 XMLNode &before = playlist->get_state();
3235                 playlist->duplicate (r, r->last_frame(), times);
3236                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3237
3238                 c.disconnect ();
3239
3240                 if (latest_regionview) {
3241                         selection->add (latest_regionview);
3242                 }
3243         }
3244                 
3245
3246         commit_reversible_command ();
3247 }
3248
3249 void
3250 Editor::duplicate_selection (float times)
3251 {
3252         if (selection->time.empty() || selection->tracks.empty()) {
3253                 return;
3254         }
3255
3256         boost::shared_ptr<Playlist> playlist; 
3257         vector<boost::shared_ptr<AudioRegion> > new_regions;
3258         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
3259                 
3260         create_region_from_selection (new_regions);
3261
3262         if (new_regions.empty()) {
3263                 return;
3264         }
3265         
3266         begin_reversible_command (_("duplicate selection"));
3267
3268         ri = new_regions.begin();
3269
3270         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3271                 if ((playlist = (*i)->playlist()) == 0) {
3272                         continue;
3273                 }
3274                 XMLNode &before = playlist->get_state();
3275                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
3276                 XMLNode &after = playlist->get_state();
3277                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3278
3279                 ++ri;
3280                 if (ri == new_regions.end()) {
3281                         --ri;
3282                 }
3283         }
3284
3285         commit_reversible_command ();
3286 }
3287
3288 void
3289 Editor::reset_point_selection ()
3290 {
3291         /* reset all selected points to the relevant default value */
3292
3293         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3294                 
3295                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3296                 
3297                 if (atv) {
3298                         atv->reset_objects (selection->points);
3299                 } 
3300         }
3301 }
3302
3303 void
3304 Editor::center_playhead ()
3305 {
3306         float page = canvas_width * frames_per_unit;
3307
3308         center_screen_internal (playhead_cursor->current_frame, page);
3309 }
3310
3311 void
3312 Editor::center_edit_cursor ()
3313 {
3314         float page = canvas_width * frames_per_unit;
3315
3316         center_screen_internal (edit_cursor->current_frame, page);
3317 }
3318
3319 void
3320 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
3321 {
3322         begin_reversible_command (_("clear playlist"));
3323         XMLNode &before = playlist->get_state();
3324         playlist->clear ();
3325         XMLNode &after = playlist->get_state();
3326         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
3327         commit_reversible_command ();
3328 }
3329
3330 void
3331 Editor::nudge_track (bool use_edit_cursor, bool forwards)
3332 {
3333         boost::shared_ptr<Playlist> playlist; 
3334         nframes_t distance;
3335         nframes_t next_distance;
3336         nframes_t start;
3337
3338         if (use_edit_cursor) {
3339                 start = edit_cursor->current_frame;
3340         } else {
3341                 start = 0;
3342         }
3343
3344         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
3345                 return;
3346         }
3347         
3348         if (selection->tracks.empty()) {
3349                 return;
3350         }
3351         
3352         begin_reversible_command (_("nudge track"));
3353         
3354         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3355
3356                 if ((playlist = (*i)->playlist()) == 0) {
3357                         continue;
3358                 }               
3359                 
3360                 XMLNode &before = playlist->get_state();
3361                 playlist->nudge_after (start, distance, forwards);
3362                 XMLNode &after = playlist->get_state();
3363                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
3364         }
3365         
3366         commit_reversible_command ();                   
3367 }
3368
3369 void
3370 Editor::remove_last_capture ()
3371 {
3372         vector<string> choices;
3373         string prompt;
3374         
3375         if (!session) {
3376                 return;
3377         }
3378
3379         if (Config->get_verify_remove_last_capture()) {
3380                 prompt  = _("Do you really want to destroy the last capture?"
3381                             "\n(This is destructive and cannot be undone)");
3382
3383                 choices.push_back (_("No, do nothing."));
3384                 choices.push_back (_("Yes, destroy it."));
3385                 
3386                 Gtkmm2ext::Choice prompter (prompt, choices);
3387                 
3388                 if (prompter.run () == 1) {
3389                         session->remove_last_capture ();
3390                 }
3391
3392         } else {
3393                 session->remove_last_capture();
3394         }
3395 }
3396
3397 void
3398 Editor::normalize_region ()
3399 {
3400         if (!session) {
3401                 return;
3402         }
3403
3404         if (selection->regions.empty()) {
3405                 return;
3406         }
3407
3408         begin_reversible_command (_("normalize"));
3409
3410         track_canvas.get_window()->set_cursor (*wait_cursor);
3411         gdk_flush ();
3412
3413         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3414                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3415                 if (!arv)
3416                         continue;
3417                 XMLNode &before = arv->region()->get_state();
3418                 arv->audio_region()->normalize_to (0.0f);
3419                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3420         }
3421
3422         commit_reversible_command ();
3423         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3424 }
3425
3426
3427 void
3428 Editor::denormalize_region ()
3429 {
3430         if (!session) {
3431                 return;
3432         }
3433
3434         if (selection->regions.empty()) {
3435                 return;
3436         }
3437
3438         begin_reversible_command ("denormalize");
3439
3440         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
3441                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3442                 if (!arv)
3443                         continue;
3444                 XMLNode &before = arv->region()->get_state();
3445                 arv->audio_region()->set_scale_amplitude (1.0f);
3446                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
3447         }
3448
3449         commit_reversible_command ();
3450 }
3451
3452
3453 void
3454 Editor::reverse_region ()
3455 {
3456         if (!session) {
3457                 return;
3458         }
3459
3460         Reverse rev (*session);
3461         apply_filter (rev, _("reverse regions"));
3462 }
3463
3464 void
3465 Editor::apply_filter (AudioFilter& filter, string command)
3466 {
3467         if (selection->regions.empty()) {
3468                 return;
3469         }
3470
3471         begin_reversible_command (command);
3472
3473         track_canvas.get_window()->set_cursor (*wait_cursor);
3474         gdk_flush ();
3475
3476         for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ) {
3477                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
3478                 if (!arv)
3479                         continue;
3480
3481                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
3482
3483                 RegionSelection::iterator tmp;
3484                 
3485                 tmp = r;
3486                 ++tmp;
3487
3488                 if (arv->audio_region()->apply (filter) == 0) {
3489
3490                         XMLNode &before = playlist->get_state();
3491                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
3492                         XMLNode &after = playlist->get_state();
3493                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
3494                 } else {
3495                         goto out;
3496                 }
3497
3498                 r = tmp;
3499         }
3500
3501         commit_reversible_command ();
3502         selection->regions.clear ();
3503
3504   out:
3505         track_canvas.get_window()->set_cursor (*current_canvas_cursor);
3506 }
3507
3508 void
3509 Editor::region_selection_op (void (Region::*pmf)(void))
3510 {
3511         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3512                 Region* region = (*i)->region().get();
3513                 (region->*pmf)();
3514         }
3515 }
3516
3517
3518 void
3519 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
3520 {
3521         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3522                 Region* region = (*i)->region().get();
3523                 (region->*pmf)(arg);
3524         }
3525 }
3526
3527 void
3528 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
3529 {
3530         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3531                 Region* region = (*i)->region().get();
3532                 (region->*pmf)(yn);
3533         }
3534 }
3535
3536 void
3537 Editor::external_edit_region ()
3538 {
3539         if (!clicked_regionview) {
3540                 return;
3541         }
3542
3543         /* more to come */
3544 }
3545
3546 void
3547 Editor::brush (nframes_t pos)
3548 {
3549         RegionSelection sel;
3550         snap_to (pos);
3551
3552         if (selection->regions.empty()) {
3553                 /* XXX get selection from region list */
3554         } else { 
3555                 sel = selection->regions;
3556         }
3557
3558         if (sel.empty()) {
3559                 return;
3560         }
3561
3562         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3563                 mouse_brush_insert_region ((*i), pos);
3564         }
3565 }
3566
3567 void
3568 Editor::reset_region_gain_envelopes ()
3569 {
3570         if (!session || selection->regions.empty()) {
3571                 return;
3572         }
3573
3574         session->begin_reversible_command (_("reset region gain"));
3575
3576         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3577                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3578                 if (arv) {
3579                         AutomationList& alist (arv->audio_region()->envelope());
3580                         XMLNode& before (alist.get_state());
3581
3582                         arv->audio_region()->set_default_envelope ();
3583                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
3584                 }
3585         }
3586
3587         session->commit_reversible_command ();
3588 }
3589
3590 void
3591 Editor::toggle_gain_envelope_visibility ()
3592 {
3593         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3594                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3595                 if (arv) {
3596                         bool x = region_envelope_visible_item->get_active();
3597                         if (x != arv->envelope_visible()) {
3598                                 arv->set_envelope_visible (x);
3599                         }
3600                 }
3601         }
3602 }
3603
3604 void
3605 Editor::toggle_gain_envelope_active ()
3606 {
3607         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3608                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3609                 if (arv) {
3610                         bool x = region_envelope_active_item->get_active();
3611                         if (x != arv->audio_region()->envelope_active()) {
3612                                 arv->audio_region()->set_envelope_active (x);
3613                         }
3614                 }
3615         }
3616 }
3617
3618 void
3619 Editor::toggle_region_lock ()
3620 {
3621         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3622                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3623                 if (arv) {
3624                         bool x = region_lock_item->get_active();
3625                         if (x != arv->audio_region()->locked()) {
3626                                 arv->audio_region()->set_locked (x);
3627                         }
3628                 }
3629         }
3630 }
3631
3632 void
3633 Editor::toggle_region_mute ()
3634 {
3635         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3636                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3637                 if (arv) {
3638                         bool x = region_mute_item->get_active();
3639                         if (x != arv->audio_region()->muted()) {
3640                                 arv->audio_region()->set_muted (x);
3641                         }
3642                 }
3643         }
3644 }
3645
3646 void
3647 Editor::toggle_region_opaque ()
3648 {
3649         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
3650                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
3651                 if (arv) {
3652                         bool x = region_opaque_item->get_active();
3653                         if (x != arv->audio_region()->opaque()) {
3654                                 arv->audio_region()->set_opaque (x);
3655                         }
3656                 }
3657         }
3658 }