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