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