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