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