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