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