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