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