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