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