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