loop handling changes: always turn off play_loop @ transport stop ; remove request_pl...
[ardour.git] / gtk2_ardour / editor_ops.cc
1 /*
2     Copyright (C) 2000-2004 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <unistd.h>
21
22 #include <cstdlib>
23 #include <cmath>
24 #include <string>
25 #include <map>
26 #include <set>
27
28 #include <pbd/error.h>
29 #include <pbd/basename.h>
30 #include <pbd/pthread_utils.h>
31 #include <pbd/memento_command.h>
32 #include <pbd/whitespace.h>
33
34 #include <gtkmm2ext/utils.h>
35 #include <gtkmm2ext/choice.h>
36 #include <gtkmm2ext/popup.h>
37
38 #include <ardour/audioengine.h>
39 #include <ardour/session.h>
40 #include <ardour/audioplaylist.h>
41 #include <ardour/audioregion.h>
42 #include <ardour/audio_diskstream.h>
43 #include <ardour/utils.h>
44 #include <ardour/location.h>
45 #include <ardour/named_selection.h>
46 #include <ardour/audio_track.h>
47 #include <ardour/audiofilesource.h>
48 #include <ardour/audioplaylist.h>
49 #include <ardour/region_factory.h>
50 #include <ardour/playlist_factory.h>
51 #include <ardour/reverse.h>
52 #include <ardour/transient_detector.h>
53 #include <ardour/dB.h>
54
55 #include "ardour_ui.h"
56 #include "editor.h"
57 #include "time_axis_view.h"
58 #include "route_time_axis.h"
59 #include "audio_time_axis.h"
60 #include "automation_time_axis.h"
61 #include "streamview.h"
62 #include "audio_streamview.h"
63 #include "audio_region_view.h"
64 #include "rgb_macros.h"
65 #include "selection_templates.h"
66 #include "selection.h"
67 #include "editing.h"
68 #include "gtk-custom-hruler.h"
69 #include "gui_thread.h"
70 #include "keyboard.h"
71 #include "utils.h"
72
73 #include "i18n.h"
74
75 using namespace std;
76 using namespace ARDOUR;
77 using namespace PBD;
78 using namespace sigc;
79 using namespace Gtk;
80 using namespace Gtkmm2ext;
81 using namespace Editing;
82
83 /***********************************************************************
84   Editor operations
85  ***********************************************************************/
86
87 void
88 Editor::undo (uint32_t n)
89 {
90         if (session) {
91                 session->undo (n);
92         }
93 }
94
95 void
96 Editor::redo (uint32_t n)
97 {
98         if (session) {
99                 session->redo (n);
100         }
101 }
102
103 void
104 Editor::split_region ()
105 {
106         split_region_at (get_preferred_edit_position());
107 }
108
109 void
110 Editor::split_region_at (nframes64_t where)
111 {
112         RegionSelection rs;
113
114         get_regions_for_action (rs);
115         split_regions_at (where, selection->regions);
116 }
117
118 void
119 Editor::split_regions_at (nframes64_t where, RegionSelection& regions)
120 {
121         if (regions.empty()) {
122                 return;
123         }
124
125         begin_reversible_command (_("split"));
126
127         // if splitting a single region, and snap-to is using
128         // region boundaries, don't pay attention to them
129
130         if (regions.size() == 1) {
131                 switch (snap_type) {
132                 case SnapToRegionStart:
133                 case SnapToRegionSync:
134                 case SnapToRegionEnd:
135                         break;
136                 default:
137                         snap_to (where);
138                 }
139         } else {
140                 snap_to (where);
141         }
142
143         for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
144
145                 RegionSelection::iterator tmp;
146
147                 /* XXX this test needs to be more complicated, to make sure we really
148                    have something to split.
149                 */
150                 
151                 if (!(*a)->region()->covers (where)) {
152                         ++a;
153                         continue;
154                 }
155
156                 tmp = a;
157                 ++tmp;
158
159                 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
160
161                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*a);
162
163                 if (arv) {
164                         _new_regionviews_show_envelope = arv->envelope_visible();
165                 }
166                 
167                 if (pl) {
168                         XMLNode &before = pl->get_state();
169                         pl->split_region ((*a)->region(), where);
170                         XMLNode &after = pl->get_state();
171                         session->add_command(new MementoCommand<Playlist>(*pl, &before, &after));
172                 }
173
174                 a = tmp;
175         }
176         
177         commit_reversible_command ();
178         _new_regionviews_show_envelope = false;
179 }
180
181 void
182 Editor::remove_clicked_region ()
183 {
184         if (clicked_audio_trackview == 0 || clicked_regionview == 0) {
185                 return;
186         }
187
188         boost::shared_ptr<Playlist> playlist = clicked_audio_trackview->playlist();
189         
190         begin_reversible_command (_("remove region"));
191         XMLNode &before = playlist->get_state();
192         playlist->remove_region (clicked_regionview->region());
193         XMLNode &after = playlist->get_state();
194         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
195         commit_reversible_command ();
196 }
197
198 void
199 Editor::remove_region ()
200 {
201
202         RegionSelection rs; 
203         get_regions_for_action (rs);
204         
205         if (!session) {
206                 return;
207         }
208
209         if (rs.empty()) {
210                 return;
211         }
212
213         begin_reversible_command (_("remove region"));
214
215         list<boost::shared_ptr<Region> > regions_to_remove;
216
217         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
218                 // we can't just remove the region(s) in this loop because
219                 // this removes them from the RegionSelection, and they thus
220                 // disappear from underneath the iterator, and the ++i above
221                 // SEGVs in a puzzling fashion.
222
223                 // so, first iterate over the regions to be removed from rs and
224                 // add them to the regions_to_remove list, and then
225                 // iterate over the list to actually remove them.
226                 
227                 regions_to_remove.push_back ((*i)->region());
228         }
229         
230         for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
231                 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
232                 if (!playlist) {
233                         // is this check necessary?
234                         continue;
235                 }
236
237                 XMLNode &before = playlist->get_state();
238                 playlist->remove_region (*rl);
239                 XMLNode &after = playlist->get_state();
240                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
241         }
242         commit_reversible_command ();
243 }
244
245 boost::shared_ptr<Region>
246 Editor::select_region_for_operation (int dir, TimeAxisView **tv)
247 {
248         RegionView* rv;
249         boost::shared_ptr<Region> region;
250         nframes64_t start = 0;
251
252         if (selection->time.start () == selection->time.end_frame ()) {
253                 
254                 /* no current selection-> is there a selected regionview? */
255
256                 if (selection->regions.empty()) {
257                         return region;
258                 }
259
260         } 
261
262         if (!selection->regions.empty()) {
263
264                 rv = *(selection->regions.begin());
265                 (*tv) = &rv->get_time_axis_view();
266                 region = rv->region();
267
268         } else if (!selection->tracks.empty()) {
269
270                 (*tv) = selection->tracks.front();
271
272                 RouteTimeAxisView* rtv;
273
274                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
275                         boost::shared_ptr<Playlist> pl;
276                         
277                         if ((pl = rtv->playlist()) == 0) {
278                                 return region;
279                         }
280                         
281                         region = pl->top_region_at (start);
282                 }
283         } 
284         
285         return region;
286 }
287         
288 void
289 Editor::extend_selection_to_end_of_region (bool next)
290 {
291         TimeAxisView *tv;
292         boost::shared_ptr<Region> region;
293         nframes64_t start;
294
295         if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
296                 return;
297         }
298
299         if (region && selection->time.start () == selection->time.end_frame ()) {
300                 start = region->position();
301         } else {
302                 start = selection->time.start ();
303         }
304
305         /* Try to leave the selection with the same route if possible */
306
307         if ((tv = selection->time.track) == 0) {
308                 return;
309         }
310
311         begin_reversible_command (_("extend selection"));
312         selection->set (tv, start, region->position() + region->length());
313         commit_reversible_command ();
314 }
315
316 void
317 Editor::extend_selection_to_start_of_region (bool previous)
318 {
319         TimeAxisView *tv;
320         boost::shared_ptr<Region> region;
321         nframes64_t end;
322
323         if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
324                 return;
325         }
326
327         if (region && selection->time.start () == selection->time.end_frame ()) {
328                 end = region->position() + region->length();
329         } else {
330                 end = selection->time.end_frame ();
331         }
332
333         /* Try to leave the selection with the same route if possible */
334         
335         if ((tv = selection->time.track) == 0) {
336                 return;
337         }
338
339         begin_reversible_command (_("extend selection"));
340         selection->set (tv, region->position(), end);
341         commit_reversible_command ();
342 }
343
344 bool
345 Editor::nudge_forward_release (GdkEventButton* ev)
346 {
347         if (ev->state & Keyboard::PrimaryModifier) {
348                 nudge_forward (false, true);
349         } else {
350                 nudge_forward (false, false);
351         }
352         return false;
353 }
354
355 bool
356 Editor::nudge_backward_release (GdkEventButton* ev)
357 {
358         if (ev->state & Keyboard::PrimaryModifier) {
359                 nudge_backward (false, true);
360         } else {
361                 nudge_backward (false, false);
362         }
363         return false;
364 }
365
366
367 void
368 Editor::nudge_forward (bool next, bool force_playhead)
369 {
370         nframes64_t distance;
371         nframes64_t next_distance;
372         RegionSelection rs; 
373
374         get_regions_for_action (rs);
375
376         if (!session) return;
377         
378         if (!force_playhead && !rs.empty()) {
379
380                 begin_reversible_command (_("nudge regions forward"));
381
382                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
383                         boost::shared_ptr<Region> r ((*i)->region());
384                         
385                         distance = get_nudge_distance (r->position(), next_distance);
386
387                         if (next) {
388                                 distance = next_distance;
389                         }
390
391                         XMLNode &before = r->playlist()->get_state();
392                         r->set_position (r->position() + distance, this);
393                         XMLNode &after = r->playlist()->get_state();
394                         session->add_command (new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
395                 }
396
397                 commit_reversible_command ();
398
399                 
400         } else if (!force_playhead && !selection->markers.empty()) {
401
402                 bool is_start;
403
404                 begin_reversible_command (_("nudge location forward"));
405                 
406                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
407                         
408                         Location* loc = find_location_from_marker ((*i), is_start);
409                         
410                         if (loc) {
411                                 
412                                 XMLNode& before (loc->get_state());
413                                 
414                                 if (is_start) {
415                                         distance = get_nudge_distance (loc->start(), next_distance);
416                                         if (next) {
417                                                 distance = next_distance;
418                                         }
419                                         if (max_frames - distance > loc->start() + loc->length()) {
420                                                 loc->set_start (loc->start() + distance);
421                                         } else {
422                                                 loc->set_start (max_frames - loc->length());
423                                         }
424                                 } else {
425                                         distance = get_nudge_distance (loc->end(), next_distance);
426                                         if (next) {
427                                                 distance = next_distance;
428                                         }
429                                         if (max_frames - distance > loc->end()) {
430                                                 loc->set_end (loc->end() + distance);
431                                         } else {
432                                                 loc->set_end (max_frames);
433                                         }
434                                 }
435                                 XMLNode& after (loc->get_state());
436                                 session->add_command (new MementoCommand<Location>(*loc, &before, &after));
437                         }
438                 }
439                 
440                 commit_reversible_command ();
441                 
442         } else {
443                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
444                 session->request_locate (playhead_cursor->current_frame + distance);
445         }
446 }
447                 
448 void
449 Editor::nudge_backward (bool next, bool force_playhead)
450 {
451         nframes64_t distance;
452         nframes64_t next_distance;
453         RegionSelection rs; 
454
455         get_regions_for_action (rs);
456
457         if (!session) return;
458         
459         if (!force_playhead && !rs.empty()) {
460
461                 begin_reversible_command (_("nudge regions backward"));
462
463                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
464                         boost::shared_ptr<Region> r ((*i)->region());
465
466                         distance = get_nudge_distance (r->position(), next_distance);
467                         
468                         if (next) {
469                                 distance = next_distance;
470                         }
471
472                         XMLNode &before = r->playlist()->get_state();
473                         
474                         if (r->position() > distance) {
475                                 r->set_position (r->position() - distance, this);
476                         } else {
477                                 r->set_position (0, this);
478                         }
479                         XMLNode &after = r->playlist()->get_state();
480                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
481                 }
482
483                 commit_reversible_command ();
484
485         } else if (!force_playhead && !selection->markers.empty()) {
486
487                 bool is_start;
488
489                 begin_reversible_command (_("nudge location forward"));
490
491                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
492
493                         Location* loc = find_location_from_marker ((*i), is_start);
494                         
495                         if (loc) {
496                                 
497                                 XMLNode& before (loc->get_state());
498                         
499                                 if (is_start) {
500                                         distance = get_nudge_distance (loc->start(), next_distance);
501                                         if (next) {
502                                                 distance = next_distance;
503                                         }
504                                         if (distance < loc->start()) {
505                                                 loc->set_start (loc->start() - distance);
506                                         } else {
507                                                 loc->set_start (0);
508                                         }
509                                 } else {
510                                         distance = get_nudge_distance (loc->end(), next_distance);
511                                         
512                                         if (next) {
513                                                 distance = next_distance;
514                                         }
515                                         
516                                         if (distance < loc->end() - loc->length()) {
517                                                 loc->set_end (loc->end() - distance);
518                                         } else {
519                                                 loc->set_end (loc->length());
520                                         }
521                                 }
522                                 
523                                 XMLNode& after (loc->get_state());
524                                 session->add_command (new MementoCommand<Location>(*loc, &before, &after));
525                         }
526                 }
527
528                 commit_reversible_command ();
529                         
530         } else {
531
532                 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
533
534                 if (playhead_cursor->current_frame > distance) {
535                         session->request_locate (playhead_cursor->current_frame - distance);
536                 } else {
537                         session->goto_start();
538                 }
539         }
540 }
541
542 void
543 Editor::nudge_forward_capture_offset ()
544 {
545         nframes64_t distance;
546         RegionSelection rs; 
547
548         get_regions_for_action (rs);
549
550         if (!session) return;
551         
552         if (!rs.empty()) {
553
554                 begin_reversible_command (_("nudge forward"));
555
556                 distance = session->worst_output_latency();
557
558                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
559                         boost::shared_ptr<Region> r ((*i)->region());
560                         
561                         XMLNode &before = r->playlist()->get_state();
562                         r->set_position (r->position() + distance, this);
563                         XMLNode &after = r->playlist()->get_state();
564                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
565                 }
566
567                 commit_reversible_command ();
568
569         } 
570 }
571                 
572 void
573 Editor::nudge_backward_capture_offset ()
574 {
575         nframes64_t distance;
576         RegionSelection rs; 
577
578         get_regions_for_action (rs);
579
580         if (!session) return;
581         
582         if (!rs.empty()) {
583
584                 begin_reversible_command (_("nudge forward"));
585
586                 distance = session->worst_output_latency();
587
588                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
589                         boost::shared_ptr<Region> r ((*i)->region());
590
591                         XMLNode &before = r->playlist()->get_state();
592                         
593                         if (r->position() > distance) {
594                                 r->set_position (r->position() - distance, this);
595                         } else {
596                                 r->set_position (0, this);
597                         }
598                         XMLNode &after = r->playlist()->get_state();
599                         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
600                 }
601
602                 commit_reversible_command ();
603         }
604 }
605
606 /* DISPLAY MOTION */
607
608 void
609 Editor::move_to_start ()
610 {
611         session->goto_start ();
612 }
613
614 void
615 Editor::move_to_end ()
616 {
617
618         session->request_locate (session->current_end_frame());
619 }
620
621 void
622 Editor::build_region_boundary_cache ()
623 {
624         nframes64_t pos = 0;
625         vector<RegionPoint> interesting_points;
626         boost::shared_ptr<Region> r;
627         TrackViewList tracks;
628         bool at_end = false;
629
630         region_boundary_cache.clear ();
631
632         if (session == 0) {
633                 return;
634         }
635         
636         switch (snap_type) {
637         case SnapToRegionStart:
638                 interesting_points.push_back (Start);
639                 break;
640         case SnapToRegionEnd:
641                 interesting_points.push_back (End);
642                 break;  
643         case SnapToRegionSync:
644                 interesting_points.push_back (SyncPoint);
645                 break;  
646         case SnapToRegionBoundary:
647                 interesting_points.push_back (Start);
648                 interesting_points.push_back (End);
649                 break;  
650         default:
651                 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), snap_type) << endmsg;
652                 /*NOTREACHED*/
653                 return;
654         }
655         
656         TimeAxisView *ontrack = 0;
657         TrackViewList tlist;
658
659         if (!selection->tracks.empty()) {
660                 tlist = selection->tracks;
661         } else {
662                 tlist = track_views;
663         }
664
665         while (pos < session->current_end_frame() && !at_end) {
666
667                 nframes64_t rpos;
668                 nframes64_t lpos = max_frames;
669
670                 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
671
672                         if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
673                                 if (*p == interesting_points.back()) {
674                                         at_end = true;
675                                 }
676                                 /* move to next point type */
677                                 continue;
678                         }
679
680                         switch (*p) {
681                         case Start:
682                                 rpos = r->first_frame();
683                                 break;
684
685                         case End:
686                                 rpos = r->last_frame();
687                                 break;  
688
689                         case SyncPoint:
690                                 rpos = r->sync_position ();
691                                 //r->adjust_to_sync (r->first_frame());
692                                 break;
693
694                         default:
695                                 break;
696                         }
697                         
698                         float speed = 1.0f;
699                         AudioTimeAxisView *atav;
700                         
701                         if (ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
702                                 if (atav->get_diskstream() != 0) {
703                                         speed = atav->get_diskstream()->speed();
704                                 }
705                         }
706                         
707                         rpos = track_frame_to_session_frame (rpos, speed);
708
709                         if (rpos < lpos) {
710                                 lpos = rpos;
711                         }
712
713                         /* prevent duplicates, but we don't use set<> because we want to be able
714                            to sort later.
715                         */
716
717                         vector<nframes64_t>::iterator ri; 
718                         
719                         for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
720                                 if (*ri == rpos) {
721                                         break;
722                                 }
723                         }
724
725                         if (ri == region_boundary_cache.end()) {
726                                 region_boundary_cache.push_back (rpos);
727                         }
728                 }
729
730                 pos = lpos + 1;
731         }
732
733         /* finally sort to be sure that the order is correct */
734
735         sort (region_boundary_cache.begin(), region_boundary_cache.end());
736 }
737
738 boost::shared_ptr<Region>
739 Editor::find_next_region (nframes64_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
740 {
741         TrackViewList::iterator i;
742         nframes64_t closest = max_frames;
743         boost::shared_ptr<Region> ret;
744         nframes64_t rpos = 0;
745
746         float track_speed;
747         nframes64_t track_frame;
748         AudioTimeAxisView *atav;
749
750         for (i = tracks.begin(); i != tracks.end(); ++i) {
751
752                 nframes64_t distance;
753                 boost::shared_ptr<Region> r;
754                 
755                 track_speed = 1.0f;
756                 if ( (atav = dynamic_cast<AudioTimeAxisView*>(*i)) != 0 ) {
757                         if (atav->get_diskstream()!=0)
758                                 track_speed = atav->get_diskstream()->speed();
759                 }
760
761                 track_frame = session_frame_to_track_frame(frame, track_speed);
762
763                 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
764                         continue;
765                 }
766
767                 switch (point) {
768                 case Start:
769                         rpos = r->first_frame ();
770                         break;
771
772                 case End:
773                         rpos = r->last_frame ();
774                         break;
775
776                 case SyncPoint:
777                         rpos = r->sync_position ();
778                         // r->adjust_to_sync (r->first_frame());
779                         break;
780                 }
781
782                 // rpos is a "track frame", converting it to "session frame"
783                 rpos = track_frame_to_session_frame(rpos, track_speed);
784
785                 if (rpos > frame) {
786                         distance = rpos - frame;
787                 } else {
788                         distance = frame - rpos;
789                 }
790
791                 if (distance < closest) {
792                         closest = distance;
793                         if (ontrack != 0)
794                                 *ontrack = (*i);
795                         ret = r;
796                 }
797         }
798
799         return ret;
800 }
801
802 nframes64_t
803 Editor::find_next_region_boundary (nframes64_t pos, int32_t dir, const TrackViewList& tracks)
804 {
805         nframes64_t distance = max_frames;
806         nframes64_t current_nearest = -1;
807
808
809         for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
810                 nframes64_t contender;
811                 nframes64_t d;
812
813                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
814
815                 if (!rtv) {
816                         continue;
817                 }
818
819                 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
820                         continue;
821                 }
822
823                 d = ::llabs (pos - contender);
824
825                 if (d < distance) {
826                         current_nearest = contender;
827                         distance = d;
828                 }
829         }
830         
831         return current_nearest;
832 }
833
834 nframes64_t
835 Editor::get_region_boundary (nframes64_t pos, int32_t dir, bool with_selection, bool only_onscreen)
836 {
837         nframes64_t target;
838         TrackViewList tvl;
839
840         if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
841
842                 if (!selection->tracks.empty()) {
843                         
844                         target = find_next_region_boundary (pos, dir, selection->tracks);
845                         
846                 } else {
847                         
848                         if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
849                                 get_onscreen_tracks (tvl);
850                                 target = find_next_region_boundary (pos, dir, tvl);
851                         } else {
852                                 target = find_next_region_boundary (pos, dir, track_views);
853                         }
854                 }
855                 
856         } else {
857
858                 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
859                         get_onscreen_tracks (tvl);
860                         target = find_next_region_boundary (pos, dir, tvl);
861                 } else {
862                         target = find_next_region_boundary (pos, dir, track_views);
863                 }
864         }
865         
866         return target;
867 }
868
869 void
870 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
871 {
872         nframes64_t pos = playhead_cursor->current_frame;
873         nframes64_t target;
874
875         if (!session) {
876                 return;
877         }
878
879         // so we don't find the current region again..
880         if (dir > 0 || pos > 0) {
881                 pos += dir;
882         }
883
884         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
885                 return;
886         }
887
888
889         session->request_locate (target);
890 }
891
892 void
893 Editor::cursor_to_next_region_boundary (bool with_selection)
894 {
895         cursor_to_region_boundary (with_selection, 1);
896 }
897
898 void
899 Editor::cursor_to_previous_region_boundary (bool with_selection)
900 {
901         cursor_to_region_boundary (with_selection, -1);
902 }
903
904 void
905 Editor::cursor_to_region_point (Cursor* cursor, RegionPoint point, int32_t dir)
906 {
907         boost::shared_ptr<Region> r;
908         nframes64_t pos = cursor->current_frame;
909
910         if (!session) {
911                 return;
912         }
913
914         TimeAxisView *ontrack = 0;
915
916         // so we don't find the current region again..
917         if (dir>0 || pos>0)
918                 pos+=dir;
919
920         if (!selection->tracks.empty()) {
921                 
922                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
923                 
924         } else if (clicked_trackview) {
925                 
926                 TrackViewList t;
927                 t.push_back (clicked_trackview);
928                 
929                 r = find_next_region (pos, point, dir, t, &ontrack);
930                 
931         } else {
932                 
933                 r = find_next_region (pos, point, dir, track_views, &ontrack);
934         }
935
936         if (r == 0) {
937                 return;
938         }
939         
940         switch (point){
941         case Start:
942                 pos = r->first_frame ();
943                 break;
944
945         case End:
946                 pos = r->last_frame ();
947                 break;
948
949         case SyncPoint:
950                 pos = r->sync_position ();
951                 // r->adjust_to_sync (r->first_frame());
952                 break;  
953         }
954         
955         float speed = 1.0f;
956         AudioTimeAxisView *atav;
957
958         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
959                 if (atav->get_diskstream() != 0) {
960                         speed = atav->get_diskstream()->speed();
961                 }
962         }
963
964         pos = track_frame_to_session_frame(pos, speed);
965         
966         if (cursor == playhead_cursor) {
967                 session->request_locate (pos);
968         } else {
969                 cursor->set_position (pos);
970         }
971 }
972
973 void
974 Editor::cursor_to_next_region_point (Cursor* cursor, RegionPoint point)
975 {
976         cursor_to_region_point (cursor, point, 1);
977 }
978
979 void
980 Editor::cursor_to_previous_region_point (Cursor* cursor, RegionPoint point)
981 {
982         cursor_to_region_point (cursor, point, -1);
983 }
984
985 void
986 Editor::cursor_to_selection_start (Cursor *cursor)
987 {
988         nframes64_t pos = 0;
989         RegionSelection rs; 
990
991         get_regions_for_action (rs);
992
993         switch (mouse_mode) {
994         case MouseObject:
995                 if (!rs.empty()) {
996                         pos = rs.start();
997                 }
998                 break;
999
1000         case MouseRange:
1001                 if (!selection->time.empty()) {
1002                         pos = selection->time.start ();
1003                 }
1004                 break;
1005
1006         default:
1007                 return;
1008         }
1009
1010         if (cursor == playhead_cursor) {
1011                 session->request_locate (pos);
1012         } else {
1013                 cursor->set_position (pos);
1014         }
1015 }
1016
1017 void
1018 Editor::cursor_to_selection_end (Cursor *cursor)
1019 {
1020         nframes64_t pos = 0;
1021         RegionSelection rs; 
1022
1023         get_regions_for_action (rs);
1024
1025         switch (mouse_mode) {
1026         case MouseObject:
1027                 if (!rs.empty()) {
1028                         pos = rs.end_frame();
1029                 }
1030                 break;
1031
1032         case MouseRange:
1033                 if (!selection->time.empty()) {
1034                         pos = selection->time.end_frame ();
1035                 }
1036                 break;
1037
1038         default:
1039                 return;
1040         }
1041
1042         if (cursor == playhead_cursor) {
1043                 session->request_locate (pos);
1044         } else {
1045                 cursor->set_position (pos);
1046         }
1047 }
1048
1049 void
1050 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1051 {
1052         nframes64_t target;
1053         Location* loc;
1054         bool ignored;
1055
1056         if (!session) {
1057                 return;
1058         }
1059
1060         if (selection->markers.empty()) {
1061                 nframes64_t mouse;
1062                 bool ignored;
1063
1064                 if (!mouse_frame (mouse, ignored)) {
1065                         return;
1066                 }
1067                 
1068                 add_location_mark (mouse);
1069         }
1070
1071         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1072                 return;
1073         }
1074
1075         nframes64_t pos = loc->start();
1076
1077         // so we don't find the current region again..
1078         if (dir > 0 || pos > 0) {
1079                 pos += dir;
1080         }
1081
1082         if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1083                 return;
1084         }
1085
1086         loc->move_to (target);
1087 }
1088
1089 void
1090 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1091 {
1092         selected_marker_to_region_boundary (with_selection, 1);
1093 }
1094
1095 void
1096 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1097 {
1098         selected_marker_to_region_boundary (with_selection, -1);
1099 }
1100
1101 void
1102 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1103 {
1104         boost::shared_ptr<Region> r;
1105         nframes64_t pos;
1106         Location* loc;
1107         bool ignored;
1108
1109         if (!session || selection->markers.empty()) {
1110                 return;
1111         }
1112
1113         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1114                 return;
1115         }
1116
1117         TimeAxisView *ontrack = 0;
1118
1119         pos = loc->start();
1120
1121         // so we don't find the current region again..
1122         if (dir>0 || pos>0)
1123                 pos+=dir;
1124
1125         if (!selection->tracks.empty()) {
1126                 
1127                 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1128                 
1129         } else if (clicked_trackview) {
1130                 
1131                 TrackViewList t;
1132                 t.push_back (clicked_trackview);
1133                 
1134                 r = find_next_region (pos, point, dir, t, &ontrack);
1135                 
1136         } else {
1137                 
1138                 r = find_next_region (pos, point, dir, track_views, &ontrack);
1139         }
1140
1141         if (r == 0) {
1142                 return;
1143         }
1144         
1145         switch (point){
1146         case Start:
1147                 pos = r->first_frame ();
1148                 break;
1149
1150         case End:
1151                 pos = r->last_frame ();
1152                 break;
1153
1154         case SyncPoint:
1155                 pos = r->adjust_to_sync (r->first_frame());
1156                 break;  
1157         }
1158         
1159         float speed = 1.0f;
1160         AudioTimeAxisView *atav;
1161
1162         if ( ontrack != 0 && (atav = dynamic_cast<AudioTimeAxisView*>(ontrack)) != 0 ) {
1163                 if (atav->get_diskstream() != 0) {
1164                         speed = atav->get_diskstream()->speed();
1165                 }
1166         }
1167
1168         pos = track_frame_to_session_frame(pos, speed);
1169
1170         loc->move_to (pos);
1171 }
1172
1173 void
1174 Editor::selected_marker_to_next_region_point (RegionPoint point)
1175 {
1176         selected_marker_to_region_point (point, 1);
1177 }
1178
1179 void
1180 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1181 {
1182         selected_marker_to_region_point (point, -1);
1183 }
1184
1185 void
1186 Editor::selected_marker_to_selection_start ()
1187 {
1188         nframes64_t pos = 0;
1189         Location* loc;
1190         bool ignored;
1191
1192         if (!session || selection->markers.empty()) {
1193                 return;
1194         }
1195
1196         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1197                 return;
1198         }
1199
1200         RegionSelection rs; 
1201
1202         get_regions_for_action (rs);
1203
1204         switch (mouse_mode) {
1205         case MouseObject:
1206                 if (!rs.empty()) {
1207                         pos = rs.start();
1208                 }
1209                 break;
1210
1211         case MouseRange:
1212                 if (!selection->time.empty()) {
1213                         pos = selection->time.start ();
1214                 }
1215                 break;
1216
1217         default:
1218                 return;
1219         }
1220
1221         loc->move_to (pos);
1222 }
1223
1224 void
1225 Editor::selected_marker_to_selection_end ()
1226 {
1227         nframes64_t pos = 0;
1228         Location* loc;
1229         bool ignored;
1230
1231         if (!session || selection->markers.empty()) {
1232                 return;
1233         }
1234
1235         if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1236                 return;
1237         }
1238
1239         RegionSelection rs; 
1240
1241         get_regions_for_action (rs);
1242
1243         switch (mouse_mode) {
1244         case MouseObject:
1245                 if (!rs.empty()) {
1246                         pos = rs.end_frame();
1247                 }
1248                 break;
1249
1250         case MouseRange:
1251                 if (!selection->time.empty()) {
1252                         pos = selection->time.end_frame ();
1253                 }
1254                 break;
1255
1256         default:
1257                 return;
1258         }
1259
1260         loc->move_to (pos);
1261 }
1262
1263 void
1264 Editor::scroll_playhead (bool forward)
1265 {
1266         nframes64_t pos = playhead_cursor->current_frame;
1267         nframes64_t delta = (nframes64_t) floor (current_page_frames() / 0.8);
1268
1269         if (forward) {
1270                 if (pos == max_frames) {
1271                         return;
1272                 }
1273
1274                 if (pos < max_frames - delta) {
1275                         pos += delta ;
1276                 } else {
1277                         pos = max_frames;
1278                 } 
1279
1280         } else {
1281
1282                 if (pos == 0) {
1283                         return;
1284                 } 
1285
1286                 if (pos > delta) {
1287                         pos -= delta;
1288                 } else {
1289                         pos = 0;
1290                 }
1291         }
1292
1293         session->request_locate (pos);
1294 }
1295
1296 void
1297 Editor::playhead_backward ()
1298 {
1299         nframes64_t pos;
1300         nframes64_t cnt;
1301         float prefix;
1302         bool was_floating;
1303
1304         if (get_prefix (prefix, was_floating)) {
1305                 cnt = 1;
1306         } else {
1307                 if (was_floating) {
1308                         cnt = (nframes64_t) floor (prefix * session->frame_rate ());
1309                 } else {
1310                         cnt = (nframes64_t) prefix;
1311                 }
1312         }
1313
1314         pos = playhead_cursor->current_frame;
1315
1316         if ((nframes64_t) pos < cnt) {
1317                 pos = 0;
1318         } else {
1319                 pos -= cnt;
1320         }
1321         
1322         /* XXX this is completely insane. with the current buffering
1323            design, we'll force a complete track buffer flush and
1324            reload, just to move 1 sample !!!
1325         */
1326
1327         session->request_locate (pos);
1328 }
1329
1330 void
1331 Editor::playhead_forward ()
1332 {
1333         nframes64_t pos;
1334         nframes64_t cnt;
1335         bool was_floating;
1336         float prefix;
1337
1338         if (get_prefix (prefix, was_floating)) {
1339                 cnt = 1;
1340         } else {
1341                 if (was_floating) {
1342                         cnt = (nframes64_t) floor (prefix * session->frame_rate ());
1343                 } else {
1344                         cnt = (nframes64_t) floor (prefix);
1345                 }
1346         }
1347
1348         pos = playhead_cursor->current_frame;
1349         
1350         /* XXX this is completely insane. with the current buffering
1351            design, we'll force a complete track buffer flush and
1352            reload, just to move 1 sample !!!
1353         */
1354
1355         session->request_locate (pos+cnt);
1356 }
1357
1358 void
1359 Editor::cursor_align (bool playhead_to_edit)
1360 {
1361         if (!session) {
1362                 return;
1363         }
1364
1365         if (playhead_to_edit) {
1366
1367                 if (selection->markers.empty()) {
1368                         return;
1369                 }
1370                 
1371                 session->request_locate (selection->markers.front()->position(), session->transport_rolling());
1372         
1373         } else {
1374
1375                 /* move selected markers to playhead */
1376                 
1377                 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1378                         bool ignored;
1379                         
1380                         Location* loc = find_location_from_marker (*i, ignored);
1381                         
1382                         if (loc->is_mark()) {
1383                                 loc->set_start (playhead_cursor->current_frame);
1384                         } else {
1385                                 loc->set (playhead_cursor->current_frame,
1386                                           playhead_cursor->current_frame + loc->length());
1387                         }
1388                 }
1389         }
1390 }
1391
1392 void
1393 Editor::edit_cursor_backward ()
1394 {
1395         nframes64_t pos;
1396         nframes64_t cnt;
1397         float prefix;
1398         bool was_floating;
1399
1400         if (get_prefix (prefix, was_floating)) {
1401                 cnt = 1;
1402         } else {
1403                 if (was_floating) {
1404                         cnt = (nframes64_t) floor (prefix * session->frame_rate ());
1405                 } else {
1406                         cnt = (nframes64_t) prefix;
1407                 }
1408         }
1409
1410         if ((pos = get_preferred_edit_position()) < 0) {
1411                 return;
1412         }
1413
1414         if (pos < cnt) {
1415                 pos = 0;
1416         } else {
1417                 pos -= cnt;
1418         }
1419         
1420         // EDIT CURSOR edit_cursor->set_position (pos);
1421 }
1422
1423 void
1424 Editor::edit_cursor_forward ()
1425 {
1426         //nframes64_t pos;
1427         nframes64_t cnt;
1428         bool was_floating;
1429         float prefix;
1430
1431         if (get_prefix (prefix, was_floating)) {
1432                 cnt = 1;
1433         } else {
1434                 if (was_floating) {
1435                         cnt = (nframes64_t) floor (prefix * session->frame_rate ());
1436                 } else {
1437                         cnt = (nframes64_t) floor (prefix);
1438                 }
1439         }
1440
1441         // pos = edit_cursor->current_frame;
1442         // EDIT CURSOR edit_cursor->set_position (pos+cnt);
1443 }
1444
1445 void
1446 Editor::goto_frame ()
1447 {
1448         float prefix;
1449         bool was_floating;
1450         nframes64_t frame;
1451
1452         if (get_prefix (prefix, was_floating)) {
1453                 return;
1454         }
1455
1456         if (was_floating) {
1457                 frame = (nframes64_t) floor (prefix * session->frame_rate());
1458         } else {
1459                 frame = (nframes64_t) floor (prefix);
1460         }
1461
1462         session->request_locate (frame);
1463 }
1464
1465 void
1466 Editor::scroll_backward (float pages)
1467 {
1468         nframes64_t frame;
1469         nframes64_t one_page = (nframes64_t) rint (canvas_width * frames_per_unit);
1470         bool was_floating;
1471         float prefix;
1472         nframes64_t cnt;
1473         
1474         if (get_prefix (prefix, was_floating)) {
1475                 cnt = (nframes64_t) floor (pages * one_page);
1476         } else {
1477                 if (was_floating) {
1478                         cnt = (nframes64_t) floor (prefix * session->frame_rate());
1479                 } else {
1480                         cnt = (nframes64_t) floor (prefix * one_page);
1481                 }
1482         }
1483
1484         if (leftmost_frame < cnt) {
1485                 frame = 0;
1486         } else {
1487                 frame = leftmost_frame - cnt;
1488         }
1489
1490         reset_x_origin (frame);
1491 }
1492
1493 void
1494 Editor::scroll_forward (float pages)
1495 {
1496         nframes64_t frame;
1497         nframes64_t one_page = (nframes64_t) rint (canvas_width * frames_per_unit);
1498         bool was_floating;
1499         float prefix;
1500         nframes64_t cnt;
1501         
1502         if (get_prefix (prefix, was_floating)) {
1503                 cnt = (nframes64_t) floor (pages * one_page);
1504         } else {
1505                 if (was_floating) {
1506                         cnt = (nframes64_t) floor (prefix * session->frame_rate());
1507                 } else {
1508                         cnt = (nframes64_t) floor (prefix * one_page);
1509                 }
1510         }
1511
1512         if (max_frames - cnt < leftmost_frame) {
1513                 frame = max_frames - cnt;
1514         } else {
1515                 frame = leftmost_frame + cnt;
1516         }
1517
1518         reset_x_origin (frame);
1519 }
1520
1521 void
1522 Editor::scroll_tracks_down ()
1523 {
1524         float prefix;
1525         bool was_floating;
1526         int cnt;
1527
1528         if (get_prefix (prefix, was_floating)) {
1529                 cnt = 1;
1530         } else {
1531                 cnt = (int) floor (prefix);
1532         }
1533
1534         double vert_value = vertical_adjustment.get_value() + (cnt *
1535                 vertical_adjustment.get_page_size());
1536         if (vert_value > vertical_adjustment.get_upper() - canvas_height) {
1537                 vert_value = vertical_adjustment.get_upper() - canvas_height;
1538         }
1539         vertical_adjustment.set_value (vert_value);
1540 }
1541
1542 void
1543 Editor::scroll_tracks_up ()
1544 {
1545         float prefix;
1546         bool was_floating;
1547         int cnt;
1548
1549         if (get_prefix (prefix, was_floating)) {
1550                 cnt = 1;
1551         } else {
1552                 cnt = (int) floor (prefix);
1553         }
1554
1555         vertical_adjustment.set_value (vertical_adjustment.get_value() - (cnt * vertical_adjustment.get_page_size()));
1556 }
1557
1558 void
1559 Editor::scroll_tracks_down_line ()
1560 {
1561
1562         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1563         double vert_value = adj->get_value() + 60;
1564
1565         if (vert_value>adj->get_upper() - canvas_height) {
1566                 vert_value = adj->get_upper() - canvas_height;
1567         }
1568         adj->set_value (vert_value);
1569 }
1570
1571 void
1572 Editor::scroll_tracks_up_line ()
1573 {
1574         Gtk::Adjustment* adj = edit_vscrollbar.get_adjustment();
1575         adj->set_value (adj->get_value() - 60);
1576 }
1577
1578 /* ZOOM */
1579
1580 void
1581 Editor::temporal_zoom_step (bool coarser)
1582 {
1583         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::temporal_zoom_step), coarser));
1584
1585         double nfpu;
1586
1587         nfpu = frames_per_unit;
1588         
1589         if (coarser) { 
1590                 nfpu *= 1.61803399;
1591         } else { 
1592                 nfpu = max(1.0,(nfpu/1.61803399));
1593         }
1594
1595         temporal_zoom (nfpu);
1596 }       
1597
1598 void
1599 Editor::temporal_zoom (gdouble fpu)
1600 {
1601         if (!session) return;
1602         
1603         nframes64_t current_page = current_page_frames();
1604         nframes64_t current_leftmost = leftmost_frame;
1605         nframes64_t current_rightmost;
1606         nframes64_t current_center;
1607         nframes64_t new_page_size;
1608         nframes64_t half_page_size;
1609         nframes64_t leftmost_after_zoom = 0;
1610         nframes64_t where;
1611         bool in_track_canvas;
1612         double nfpu;
1613         double l;
1614
1615         /* XXX this limit is also in ::set_frames_per_unit() */
1616
1617         if (frames_per_unit < 1.0 && fpu <= frames_per_unit) {
1618                 return;
1619         }
1620
1621         nfpu = fpu;
1622         
1623         new_page_size = (nframes64_t) floor (canvas_width * nfpu);
1624         half_page_size = new_page_size / 2;
1625
1626         switch (zoom_focus) {
1627         case ZoomFocusLeft:
1628                 leftmost_after_zoom = current_leftmost;
1629                 break;
1630                 
1631         case ZoomFocusRight:
1632                 current_rightmost = leftmost_frame + current_page;
1633                 if (current_rightmost < new_page_size) {
1634                         leftmost_after_zoom = 0;
1635                 } else {
1636                         leftmost_after_zoom = current_rightmost - new_page_size;
1637                 }
1638                 break;
1639                 
1640         case ZoomFocusCenter:
1641                 current_center = current_leftmost + (current_page/2); 
1642                 if (current_center < half_page_size) {
1643                         leftmost_after_zoom = 0;
1644                 } else {
1645                         leftmost_after_zoom = current_center - half_page_size;
1646                 }
1647                 break;
1648                 
1649         case ZoomFocusPlayhead:
1650                 /* try to keep the playhead in the same place */
1651
1652                 where = playhead_cursor->current_frame;
1653                 
1654                 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1655
1656                 if (l < 0) {
1657                         leftmost_after_zoom = 0;
1658                 } else if (l > max_frames) { 
1659                         leftmost_after_zoom = max_frames - new_page_size;
1660                 } else {
1661                         leftmost_after_zoom = (nframes64_t) l;
1662                 }
1663                 break;
1664
1665         case ZoomFocusMouse:
1666                 /* try to keep the mouse over the same point in the display */
1667
1668                 if (!mouse_frame (where, in_track_canvas)) {
1669                         /* use playhead instead */
1670                         where = playhead_cursor->current_frame;
1671
1672                         if (where < half_page_size) {
1673                                 leftmost_after_zoom = 0;
1674                         } else {
1675                                 leftmost_after_zoom = where - half_page_size;
1676                         }
1677
1678                 } else {
1679
1680                         l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1681
1682                         if (l < 0) {
1683                                 leftmost_after_zoom = 0;
1684                         } else if (l > max_frames) { 
1685                                 leftmost_after_zoom = max_frames - new_page_size;
1686                         } else {
1687                                 leftmost_after_zoom = (nframes64_t) l;
1688                         }
1689                 }
1690
1691                 break;
1692
1693         case ZoomFocusEdit:
1694                 /* try to keep the edit point in the same place */
1695                 where = get_preferred_edit_position ();
1696
1697                 if (where > 0) {
1698
1699                         double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1700
1701                         if (l < 0) {
1702                                 leftmost_after_zoom = 0;
1703                         } else if (l > max_frames) { 
1704                                 leftmost_after_zoom = max_frames - new_page_size;
1705                         } else {
1706                                 leftmost_after_zoom = (nframes64_t) l;
1707                         }
1708
1709                 } else {
1710                         /* edit point not defined */
1711                         return;
1712                 }
1713                 break;
1714                 
1715         }
1716  
1717         // leftmost_after_zoom = min (leftmost_after_zoom, session->current_end_frame());
1718
1719         reposition_and_zoom (leftmost_after_zoom, nfpu);
1720 }       
1721
1722 void
1723 Editor::temporal_zoom_region (bool both_axes)
1724 {
1725
1726         nframes64_t start = max_frames;
1727         nframes64_t end = 0;
1728         RegionSelection rs; 
1729         set<TimeAxisView*> tracks;
1730
1731         get_regions_for_action (rs);
1732
1733         if (rs.empty()) {
1734                 return;
1735         }
1736
1737         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1738
1739                 if ((*i)->region()->position() < start) {
1740                         start = (*i)->region()->position();
1741                 }
1742
1743                 if ((*i)->region()->last_frame() + 1 > end) {
1744                         end = (*i)->region()->last_frame() + 1;
1745                 }
1746
1747                 tracks.insert (&((*i)->get_time_axis_view()));
1748         }
1749
1750         /* now comes an "interesting" hack ... make sure we leave a little space
1751            at each end of the editor so that the zoom doesn't fit the region
1752            precisely to the screen.
1753         */
1754
1755         GdkScreen* screen = gdk_screen_get_default ();
1756         gint pixwidth = gdk_screen_get_width (screen);
1757         gint mmwidth = gdk_screen_get_width_mm (screen);
1758         double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1759         double one_centimeter_in_pixels = pix_per_mm * 10.0;
1760
1761         if ((start == 0 && end == 0) || end < start) {
1762                 return;
1763         }
1764
1765         nframes64_t range = end - start;
1766         double new_fpu = (double)range / (double)canvas_width;
1767         nframes64_t extra_samples = (nframes64_t) floor (one_centimeter_in_pixels * new_fpu);
1768
1769         if (start > extra_samples) {
1770                 start -= extra_samples;
1771         } else {
1772                 start = 0;
1773         } 
1774
1775         if (max_frames - extra_samples > end) {
1776                 end += extra_samples;
1777         } else {
1778                 end = max_frames;
1779         }
1780
1781         if (both_axes) {
1782                 /* save visual state with track states included, and prevent
1783                    set_frames_per_unit() from doing it again.
1784                 */
1785                 undo_visual_stack.push_back (current_visual_state(true));
1786                 no_save_visual = true;
1787         }
1788
1789         temporal_zoom_by_frame (start, end, "zoom to region");
1790
1791         if (both_axes) {
1792                 uint32_t per_track_height = (uint32_t) floor ((canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1793                 
1794                 /* set visible track heights appropriately */
1795                 
1796                 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1797                         (*t)->set_height (per_track_height);
1798                 }
1799                 
1800                 /* hide irrelevant tracks */
1801                 
1802                 no_route_list_redisplay = true;
1803
1804                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1805                         if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1806                                 hide_track_in_display (**i, true);
1807                         }
1808                 }
1809
1810                 no_route_list_redisplay = false;
1811                 redisplay_route_list ();
1812
1813                 vertical_adjustment.set_value (0);
1814                 no_save_visual = false;
1815         }
1816
1817         redo_visual_stack.push_back (current_visual_state());
1818 }
1819
1820 void
1821 Editor::toggle_zoom_region (bool both_axes)
1822 {
1823         temporal_zoom_region (both_axes);
1824 }
1825
1826 void
1827 Editor::temporal_zoom_selection ()
1828 {
1829         if (!selection) return;
1830         
1831         if (selection->time.empty()) {
1832                 return;
1833         }
1834
1835         nframes64_t start = selection->time[clicked_selection].start;
1836         nframes64_t end = selection->time[clicked_selection].end;
1837
1838         temporal_zoom_by_frame (start, end, "zoom to selection");
1839 }
1840
1841 void
1842 Editor::temporal_zoom_session ()
1843 {
1844         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::temporal_zoom_session));
1845
1846         if (session) {
1847                 temporal_zoom_by_frame (session->current_start_frame(), session->current_end_frame(), "zoom to session");
1848         }
1849 }
1850
1851 void
1852 Editor::temporal_zoom_by_frame (nframes64_t start, nframes64_t end, const string & op)
1853 {
1854         if (!session) return;
1855
1856         if ((start == 0 && end == 0) || end < start) {
1857                 return;
1858         }
1859
1860         nframes64_t range = end - start;
1861
1862         double new_fpu = (double)range / (double)canvas_width;
1863         
1864         nframes64_t new_page = (nframes64_t) floor (canvas_width * new_fpu);
1865         nframes64_t middle = (nframes64_t) floor( (double)start + ((double)range / 2.0f ));
1866         nframes64_t new_leftmost = (nframes64_t) floor( (double)middle - ((double)new_page/2.0f));
1867
1868         if (new_leftmost > middle) {
1869                 new_leftmost = 0;
1870         }
1871
1872         reposition_and_zoom (new_leftmost, new_fpu);
1873 }
1874
1875 void 
1876 Editor::temporal_zoom_to_frame (bool coarser, nframes64_t frame)
1877 {
1878         if (!session) {
1879                 return;
1880         }
1881         double range_before = frame - leftmost_frame;
1882         double new_fpu;
1883         
1884         new_fpu = frames_per_unit;
1885         
1886         if (coarser) { 
1887                 new_fpu *= 1.61803399;
1888                 range_before *= 1.61803399;
1889         } else { 
1890                 new_fpu = max(1.0,(new_fpu/1.61803399));
1891                 range_before /= 1.61803399;
1892         }
1893
1894         if (new_fpu == frames_per_unit)  {
1895                 return;
1896         }
1897
1898         nframes64_t new_leftmost = frame - (nframes64_t)range_before;
1899
1900         if (new_leftmost > frame) {
1901                 new_leftmost = 0;
1902         }
1903 //      begin_reversible_command (_("zoom to frame"));
1904 //      session->add_undo (bind (mem_fun(*this, &Editor::reposition_and_zoom), leftmost_frame, frames_per_unit));
1905 //      session->add_redo (bind (mem_fun(*this, &Editor::reposition_and_zoom), new_leftmost, new_fpu));
1906 //      commit_reversible_command ();
1907
1908         reposition_and_zoom (new_leftmost, new_fpu);
1909 }
1910
1911
1912 bool
1913 Editor::choose_new_marker_name(string &name) {
1914
1915         if (!Config->get_name_new_markers()) {
1916                 /* don't prompt user for a new name */
1917                 return true;
1918         }
1919
1920         ArdourPrompter dialog (true);
1921
1922         dialog.set_prompt (_("New Name:"));
1923
1924         dialog.set_title (_("New Location Marker"));
1925
1926         dialog.set_name ("MarkNameWindow");
1927         dialog.set_size_request (250, -1);
1928         dialog.set_position (Gtk::WIN_POS_MOUSE);
1929
1930         dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1931         dialog.set_initial_text (name);
1932
1933         dialog.show ();
1934
1935         switch (dialog.run ()) {
1936         case RESPONSE_ACCEPT:
1937                 break;
1938         default:
1939                 return false;
1940         }
1941         
1942         dialog.get_result(name);
1943         return true;
1944
1945 }
1946
1947
1948 void
1949 Editor::add_location_from_selection ()
1950 {
1951         string rangename;
1952
1953         if (selection->time.empty()) {
1954                 return;
1955         }
1956
1957         if (session == 0 || clicked_trackview == 0) {
1958                 return;
1959         }
1960
1961         nframes64_t start = selection->time[clicked_selection].start;
1962         nframes64_t end = selection->time[clicked_selection].end;
1963
1964         session->locations()->next_available_name(rangename,"selection");
1965         Location *location = new Location (start, end, rangename, Location::IsRangeMarker);
1966
1967         session->begin_reversible_command (_("add marker"));
1968         XMLNode &before = session->locations()->get_state();
1969         session->locations()->add (location, true);
1970         XMLNode &after = session->locations()->get_state();
1971         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1972         session->commit_reversible_command ();
1973 }
1974
1975 void
1976 Editor::add_location_mark (nframes64_t where)
1977 {
1978         string markername;
1979
1980         select_new_marker = true;
1981
1982         session->locations()->next_available_name(markername,"mark");
1983         if (!choose_new_marker_name(markername)) {
1984                 return;
1985         }
1986         Location *location = new Location (where, where, markername, Location::IsMark);
1987         session->begin_reversible_command (_("add marker"));
1988         XMLNode &before = session->locations()->get_state();
1989         session->locations()->add (location, true);
1990         XMLNode &after = session->locations()->get_state();
1991         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
1992         session->commit_reversible_command ();
1993 }
1994
1995 void
1996 Editor::add_location_from_playhead_cursor ()
1997 {
1998         add_location_mark (session->audible_frame());
1999 }
2000
2001 void
2002 Editor::add_locations_from_audio_region ()
2003 {
2004         RegionSelection rs; 
2005
2006         get_regions_for_action (rs);
2007
2008         if (rs.empty()) {
2009                 return;
2010         }
2011
2012         session->begin_reversible_command (rs.size () > 1 ? _("add markers") : _("add marker"));
2013         XMLNode &before = session->locations()->get_state();
2014         
2015         cerr << "Add locations\n";
2016
2017         for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2018                 
2019                 boost::shared_ptr<Region> region = (*i)->region ();
2020         
2021                 Location *location = new Location (region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2022                 
2023                 session->locations()->add (location, true);
2024         }
2025
2026         XMLNode &after = session->locations()->get_state();
2027         session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
2028         session->commit_reversible_command ();
2029 }
2030
2031 void
2032 Editor::add_location_from_audio_region ()
2033 {
2034         RegionSelection rs; 
2035
2036         get_regions_for_action (rs);
2037
2038         if (rs.empty()) {
2039                 return;
2040         }
2041
2042         session->begin_reversible_command (_("add marker"));
2043         XMLNode &before = session->locations()->get_state();
2044
2045         string markername;
2046
2047         if (rs.size() > 1) {            // more than one region selected
2048                 session->locations()->next_available_name(markername, "regions");
2049         } else {
2050                 RegionView* rv = *(rs.begin());
2051                 boost::shared_ptr<Region> region = rv->region();
2052                 markername = region->name();
2053         }
2054                 
2055         if (!choose_new_marker_name(markername)) {
2056                 return;
2057         }
2058
2059         cerr << "Add location\n";
2060
2061         // single range spanning all selected 
2062         Location *location = new Location (rs.start(), rs.end_frame(), markername, Location::IsRangeMarker);
2063         session->locations()->add (location, true);
2064
2065         XMLNode &after = session->locations()->get_state();
2066         session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
2067         session->commit_reversible_command ();
2068 }
2069
2070 void
2071 Editor::amplitude_zoom_step (bool in)
2072 {
2073         gdouble zoom = 1.0;
2074
2075         if (in) {
2076                 zoom *= 2.0;
2077         } else {
2078                 if (zoom > 2.0) {
2079                         zoom /= 2.0;
2080                 } else {
2081                         zoom = 1.0;
2082                 }
2083         }
2084
2085 #ifdef FIX_FOR_CANVAS
2086         /* XXX DO SOMETHING */
2087 #endif
2088 }       
2089
2090
2091 /* DELETION */
2092
2093
2094 void
2095 Editor::delete_sample_forward ()
2096 {
2097 }
2098
2099 void
2100 Editor::delete_sample_backward ()
2101 {
2102 }
2103
2104 void
2105 Editor::delete_screen ()
2106 {
2107 }
2108
2109 /* SEARCH */
2110
2111 void
2112 Editor::search_backwards ()
2113 {
2114         /* what ? */
2115 }
2116
2117 void
2118 Editor::search_forwards ()
2119 {
2120         /* what ? */
2121 }
2122
2123 /* MARKS */
2124
2125 void
2126 Editor::jump_forward_to_mark ()
2127 {
2128         if (!session) {
2129                 return;
2130         }
2131         
2132         Location *location = session->locations()->first_location_after (playhead_cursor->current_frame);
2133
2134         if (location) {
2135                 session->request_locate (location->start(), session->transport_rolling());
2136         } else {
2137                 session->request_locate (session->current_end_frame());
2138         }
2139 }
2140
2141 void
2142 Editor::jump_backward_to_mark ()
2143 {
2144         if (!session) {
2145                 return;
2146         }
2147
2148         Location *location = session->locations()->first_location_before (playhead_cursor->current_frame);
2149         
2150         if (location) {
2151                 session->request_locate (location->start(), session->transport_rolling());
2152         } else {
2153                 session->goto_start ();
2154         }
2155 }
2156
2157 void
2158 Editor::set_mark ()
2159 {
2160         nframes64_t pos;
2161         float prefix;
2162         bool was_floating;
2163         string markername;
2164
2165         if (get_prefix (prefix, was_floating)) {
2166                 pos = session->audible_frame ();
2167         } else {
2168                 if (was_floating) {
2169                         pos = (nframes64_t) floor (prefix * session->frame_rate ());
2170                 } else {
2171                         pos = (nframes64_t) floor (prefix);
2172                 }
2173         }
2174
2175         session->locations()->next_available_name(markername,"mark");
2176         if (!choose_new_marker_name(markername)) {
2177                 return;
2178         }
2179         session->locations()->add (new Location (pos, 0, markername, Location::IsMark), true);
2180 }
2181
2182 void
2183 Editor::clear_markers ()
2184 {
2185         if (session) {
2186                 session->begin_reversible_command (_("clear markers"));
2187                 XMLNode &before = session->locations()->get_state();
2188                 session->locations()->clear_markers ();
2189                 XMLNode &after = session->locations()->get_state();
2190                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2191                 session->commit_reversible_command ();
2192         }
2193 }
2194
2195 void
2196 Editor::clear_ranges ()
2197 {
2198         if (session) {
2199                 session->begin_reversible_command (_("clear ranges"));
2200                 XMLNode &before = session->locations()->get_state();
2201                 
2202                 Location * looploc = session->locations()->auto_loop_location();
2203                 Location * punchloc = session->locations()->auto_punch_location();
2204                 
2205                 session->locations()->clear_ranges ();
2206                 // re-add these
2207                 if (looploc) session->locations()->add (looploc);
2208                 if (punchloc) session->locations()->add (punchloc);
2209                 
2210                 XMLNode &after = session->locations()->get_state();
2211                 session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2212                 session->commit_reversible_command ();
2213         }
2214 }
2215
2216 void
2217 Editor::clear_locations ()
2218 {
2219         session->begin_reversible_command (_("clear locations"));
2220         XMLNode &before = session->locations()->get_state();
2221         session->locations()->clear ();
2222         XMLNode &after = session->locations()->get_state();
2223         session->add_command(new MementoCommand<Locations>(*(session->locations()), &before, &after));
2224         session->commit_reversible_command ();
2225         session->locations()->clear ();
2226 }
2227
2228 void
2229 Editor::unhide_markers ()
2230 {
2231         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2232                 Location *l = (*i).first;
2233                 if (l->is_hidden() && l->is_mark()) {
2234                         l->set_hidden(false, this);
2235                 }
2236         }
2237 }
2238
2239 void
2240 Editor::unhide_ranges ()
2241 {
2242         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2243                 Location *l = (*i).first;
2244                 if (l->is_hidden() && l->is_range_marker()) { 
2245                         l->set_hidden(false, this);
2246                 }
2247         }
2248 }
2249
2250 /* INSERT/REPLACE */
2251
2252 void
2253 Editor::insert_region_list_drag (boost::shared_ptr<AudioRegion> region, int x, int y)
2254 {
2255         double wx, wy;
2256         double cx, cy;
2257         TimeAxisView *tv;
2258         nframes64_t where;
2259         AudioTimeAxisView *atv = 0;
2260         boost::shared_ptr<Playlist> playlist;
2261         
2262         track_canvas->window_to_world (x, y, wx, wy);
2263         //wx += horizontal_adjustment.get_value();
2264         //wy += vertical_adjustment.get_value();
2265
2266         GdkEvent event;
2267         event.type = GDK_BUTTON_RELEASE;
2268         event.button.x = wx;
2269         event.button.y = wy;
2270         
2271         where = event_frame (&event, &cx, &cy);
2272
2273         if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
2274                 /* clearly outside canvas area */
2275                 return;
2276         }
2277         
2278         if ((tv = trackview_by_y_position (cy)) == 0) {
2279                 return;
2280         }
2281         
2282         if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) == 0) {
2283                 return;
2284         }
2285
2286         if ((playlist = atv->playlist()) == 0) {
2287                 return;
2288         }
2289         
2290         snap_to (where);
2291         
2292         begin_reversible_command (_("insert dragged region"));
2293         XMLNode &before = playlist->get_state();
2294         playlist->add_region (RegionFactory::create (region), where, 1.0);
2295         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2296         commit_reversible_command ();
2297 }
2298
2299 void
2300 Editor::insert_region_list_selection (float times)
2301 {
2302         RouteTimeAxisView *tv = 0;
2303         boost::shared_ptr<Playlist> playlist;
2304
2305         if (clicked_audio_trackview != 0) {
2306                 tv = clicked_audio_trackview;
2307         } else if (!selection->tracks.empty()) {
2308                 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2309                         return;
2310                 }
2311         } else if (entered_track != 0) {
2312                 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2313                         return;
2314                 }
2315         } else {
2316                 return;
2317         }
2318
2319         if ((playlist = tv->playlist()) == 0) {
2320                 return;
2321         }
2322         
2323         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
2324         
2325         if (selected->count_selected_rows() != 1) {
2326                 return;
2327         }
2328         
2329         TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
2330
2331         /* only one row selected, so rows.begin() is it */
2332
2333         TreeIter iter;
2334
2335         if ((iter = region_list_model->get_iter (*rows.begin()))) {
2336
2337                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
2338                 
2339                 begin_reversible_command (_("insert region"));
2340                 XMLNode &before = playlist->get_state();
2341                 playlist->add_region ((RegionFactory::create (region)), get_preferred_edit_position(), times);
2342                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
2343                 commit_reversible_command ();
2344         } 
2345 }
2346
2347 /* BUILT-IN EFFECTS */
2348
2349 void
2350 Editor::reverse_selection ()
2351 {
2352
2353 }
2354
2355 /* GAIN ENVELOPE EDITING */
2356
2357 void
2358 Editor::edit_envelope ()
2359 {
2360 }
2361
2362 /* PLAYBACK */
2363
2364 void
2365 Editor::transition_to_rolling (bool fwd)
2366 {
2367         if (!session) {
2368                 return;
2369         }
2370
2371         switch (Config->get_slave_source()) {
2372         case None:
2373         case JACK:
2374                 break;
2375         default:
2376                 /* transport controlled by the master */
2377                 return;
2378         }
2379
2380         if (session->is_auditioning()) {
2381                 session->cancel_audition ();
2382                 return;
2383         }
2384         
2385         session->request_transport_speed (fwd ? 1.0f : -1.0f);
2386 }
2387
2388 void
2389 Editor::toggle_playback (bool with_abort)
2390 {
2391         if (!session) {
2392                 return;
2393         }
2394
2395         switch (Config->get_slave_source()) {
2396         case None:
2397         case JACK:
2398                 break;
2399         default:
2400                 /* transport controlled by the master */
2401                 return;
2402         }
2403
2404         if (session->is_auditioning()) {
2405                 session->cancel_audition ();
2406                 return;
2407         }
2408         
2409         if (session->transport_rolling()) {
2410                 session->request_stop (with_abort);
2411         } else {
2412                 session->request_transport_speed (1.0f);
2413         }
2414 }
2415
2416 void
2417 Editor::play_from_start ()
2418 {
2419         session->request_locate (session->current_start_frame(), true);
2420 }
2421
2422 void
2423 Editor::play_from_edit_point ()
2424 {
2425         session->request_locate (get_preferred_edit_position(), true);
2426 }
2427
2428 void
2429 Editor::play_from_edit_point_and_return ()
2430 {
2431         nframes64_t start_frame;
2432         nframes64_t return_frame;
2433
2434         start_frame = get_preferred_edit_position (true);
2435
2436         if (session->transport_rolling()) {
2437                 session->request_locate (start_frame, false);
2438                 return;
2439         }
2440
2441         /* don't reset the return frame if its already set */
2442
2443         if ((return_frame = session->requested_return_frame()) < 0) {
2444                 return_frame = session->audible_frame();
2445         }
2446
2447         if (start_frame >= 0) {
2448                 session->request_roll_at_and_return (start_frame, return_frame);
2449         }
2450 }
2451
2452 void
2453 Editor::play_selection ()
2454 {
2455         if (selection->time.empty()) {
2456                 return;
2457         }
2458
2459         session->request_play_range (true);
2460 }
2461
2462 void
2463 Editor::loop_selected_region ()
2464 {
2465         RegionSelection rs; 
2466
2467         get_regions_for_action (rs);
2468
2469         if (!rs.empty()) {
2470                 RegionView *rv = *(rs.begin());
2471                 Location* tll;
2472
2473                 if ((tll = transport_loop_location()) != 0)  {
2474
2475                         tll->set (rv->region()->position(), rv->region()->last_frame());
2476                         
2477                         // enable looping, reposition and start rolling
2478
2479                         session->request_play_loop (true);
2480                         session->request_locate (tll->start(), false);
2481                         session->request_transport_speed (1.0f);
2482                 }
2483         }
2484 }
2485
2486 void
2487 Editor::play_location (Location& location)
2488 {
2489         if (location.start() <= location.end()) {
2490                 return;
2491         }
2492
2493         session->request_bounded_roll (location.start(), location.end());
2494 }
2495
2496 void
2497 Editor::loop_location (Location& location)
2498 {
2499         if (location.start() <= location.end()) {
2500                 return;
2501         }
2502
2503         Location* tll;
2504
2505         if ((tll = transport_loop_location()) != 0) {
2506                 tll->set (location.start(), location.end());
2507
2508                 // enable looping, reposition and start rolling
2509                 session->request_play_loop (true);
2510                 session->request_locate (tll->start(), true);
2511         }
2512 }
2513
2514 void
2515 Editor::raise_region ()
2516 {
2517         selection->foreach_region (&Region::raise);
2518 }
2519
2520 void
2521 Editor::raise_region_to_top ()
2522 {
2523         selection->foreach_region (&Region::raise_to_top);
2524 }
2525
2526 void
2527 Editor::lower_region ()
2528 {
2529         selection->foreach_region (&Region::lower);
2530 }
2531
2532 void
2533 Editor::lower_region_to_bottom ()
2534 {
2535         selection->foreach_region (&Region::lower_to_bottom);
2536 }
2537
2538 void
2539 Editor::edit_region ()
2540 {
2541         if (clicked_regionview == 0) {
2542                 return;
2543         }
2544         
2545         clicked_regionview->show_region_editor ();
2546 }
2547
2548 void
2549 Editor::rename_region()
2550 {
2551         RegionSelection rs; 
2552
2553         get_regions_for_action (rs);
2554
2555         if (rs.empty()) {
2556                 return;
2557         }
2558
2559         ArdourDialog d (*this, _("Rename Region"), true, false);
2560         Entry entry;
2561         Label label (_("New name:"));
2562         HBox hbox;
2563
2564         hbox.set_spacing (6);
2565         hbox.pack_start (label, false, false);
2566         hbox.pack_start (entry, true, true);
2567
2568         d.get_vbox()->set_border_width (12);
2569         d.get_vbox()->pack_start (hbox, false, false);
2570
2571         d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2572         d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2573
2574         d.set_size_request (300, -1);
2575         d.set_position (Gtk::WIN_POS_MOUSE);
2576
2577         entry.set_text (rs.front()->region()->name());
2578         entry.select_region (0, -1);
2579
2580         entry.signal_activate().connect (bind (mem_fun (d, &Dialog::response), RESPONSE_OK));
2581         
2582         d.show_all ();
2583         
2584         entry.grab_focus();
2585
2586         int ret = d.run();
2587
2588         d.hide ();
2589
2590         if (ret == RESPONSE_OK) {
2591                 std::string str = entry.get_text();
2592                 strip_whitespace_edges (str);
2593                 if (!str.empty()) {
2594                         rs.front()->region()->set_name (str);
2595                         redisplay_regions ();
2596                 }
2597         }
2598 }
2599
2600 void
2601 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2602 {
2603         if (session->is_auditioning()) {
2604                 session->cancel_audition ();
2605         } 
2606
2607         // note: some potential for creativity here, because region doesn't
2608         // have to belong to the playlist that Route is handling
2609
2610         // bool was_soloed = route.soloed();
2611
2612         route.set_solo (true, this);
2613         
2614         session->request_bounded_roll (region->position(), region->position() + region->length());
2615         
2616         /* XXX how to unset the solo state ? */
2617 }
2618
2619 void
2620 Editor::play_edit_range ()
2621 {
2622         nframes64_t start, end;
2623
2624         if (get_edit_op_range (start, end)) {
2625                 session->request_bounded_roll (start, end);
2626         }
2627 }
2628
2629 void
2630 Editor::play_selected_region ()
2631 {
2632         nframes64_t start = max_frames;
2633         nframes64_t end = 0;
2634         RegionSelection rs; 
2635
2636         get_regions_for_action (rs);
2637          
2638         if (rs.empty()) {
2639                 return;
2640         }
2641
2642         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2643                 if ((*i)->region()->position() < start) {
2644                         start = (*i)->region()->position();
2645                 }
2646                 if ((*i)->region()->last_frame() + 1 > end) {
2647                         end = (*i)->region()->last_frame() + 1;
2648                 }
2649         }
2650
2651         session->request_stop ();
2652         session->request_bounded_roll (start, end);
2653 }
2654
2655 void
2656 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2657 {
2658         session->audition_region (region);
2659 }
2660
2661 void
2662 Editor::build_interthread_progress_window ()
2663 {
2664         interthread_progress_window = new ArdourDialog (X_("interthread progress"), true);
2665
2666         interthread_progress_bar.set_orientation (Gtk::PROGRESS_LEFT_TO_RIGHT);
2667         
2668         interthread_progress_window->set_border_width (12);
2669         interthread_progress_window->get_vbox()->set_spacing (6);
2670
2671         interthread_progress_label.set_alignment (0.5, 0.5);
2672
2673         interthread_progress_window->get_vbox()->pack_start (interthread_progress_label, false, false);
2674         interthread_progress_window->get_vbox()->pack_start (interthread_progress_bar,false, false);
2675
2676         // GTK2FIX: this button needs a modifiable label
2677
2678         Button* b = interthread_progress_window->add_button (Stock::CANCEL, RESPONSE_CANCEL);
2679         b->signal_clicked().connect (mem_fun(*this, &Editor::interthread_cancel_clicked));
2680
2681         interthread_cancel_button.add (interthread_cancel_label);
2682
2683         interthread_progress_window->set_default_size (200, 100);
2684 }
2685
2686 void
2687 Editor::interthread_cancel_clicked ()
2688 {
2689         if (current_interthread_info) {
2690                 current_interthread_info->cancel = true;
2691         }
2692 }
2693
2694 void
2695 Editor::region_from_selection ()
2696 {
2697         if (clicked_trackview == 0) {
2698                 return;
2699         }
2700
2701         if (selection->time.empty()) {
2702                 return;
2703         }
2704
2705         nframes64_t start = selection->time[clicked_selection].start;
2706         nframes64_t end = selection->time[clicked_selection].end;
2707
2708         nframes64_t selection_cnt = end - start + 1;
2709         
2710         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2711                 boost::shared_ptr<AudioRegion> current;
2712                 boost::shared_ptr<Region> current_r;
2713                 boost::shared_ptr<Playlist> pl;
2714
2715                 nframes64_t internal_start;
2716                 string new_name;
2717
2718                 if ((pl = (*i)->playlist()) == 0) {
2719                         continue;
2720                 }
2721
2722                 if ((current_r = pl->top_region_at (start)) == 0) {
2723                         continue;
2724                 }
2725
2726                 current = boost::dynamic_pointer_cast<AudioRegion> (current_r);
2727                 // FIXME: audio only
2728                 if (current != 0) {
2729                         internal_start = start - current->position();
2730                         session->region_name (new_name, current->name(), true);
2731                         boost::shared_ptr<Region> region (RegionFactory::create (current, internal_start, selection_cnt, new_name));
2732                 }
2733         }
2734 }       
2735
2736 void
2737 Editor::create_region_from_selection (vector<boost::shared_ptr<AudioRegion> >& new_regions)
2738 {
2739         if (selection->time.empty() || selection->tracks.empty()) {
2740                 return;
2741         }
2742
2743         nframes64_t start = selection->time[clicked_selection].start;
2744         nframes64_t end = selection->time[clicked_selection].end;
2745         
2746         sort_track_selection ();
2747
2748         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
2749
2750                 boost::shared_ptr<AudioRegion> current;
2751                 boost::shared_ptr<Region> current_r;
2752                 boost::shared_ptr<Playlist> playlist;
2753                 nframes64_t internal_start;
2754                 string new_name;
2755
2756                 if ((playlist = (*i)->playlist()) == 0) {
2757                         continue;
2758                 }
2759
2760                 if ((current_r = playlist->top_region_at(start)) == 0) {
2761                         continue;
2762                 }
2763
2764                 if ((current = boost::dynamic_pointer_cast<AudioRegion>(current_r)) == 0) {
2765                         continue;
2766                 }
2767         
2768                 internal_start = start - current->position();
2769                 session->region_name (new_name, current->name(), true);
2770                 
2771                 new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (current, internal_start, end - start + 1, new_name)));
2772         }
2773 }
2774
2775 void
2776 Editor::split_multichannel_region ()
2777 {
2778         RegionSelection rs; 
2779
2780         get_regions_for_action (rs);
2781
2782         if (rs.empty()) {
2783                 return;
2784         }
2785
2786         vector<boost::shared_ptr<AudioRegion> > v;
2787
2788         for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2789
2790                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*x);
2791                 
2792                 if (!arv || arv->audio_region()->n_channels() < 2) {
2793                         continue;
2794                 }
2795
2796                 (arv)->audio_region()->separate_by_channel (*session, v);
2797         }
2798 }
2799
2800 void
2801 Editor::new_region_from_selection ()
2802 {
2803         region_from_selection ();
2804         cancel_selection ();
2805 }
2806
2807 static void
2808 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2809 {
2810         switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2811         case OverlapNone:
2812                 break;
2813         default:
2814                 rs->push_back (rv);
2815         }
2816 }
2817
2818 void
2819 Editor::separate_regions_between (const TimeSelection& ts)
2820 {
2821         bool in_command = false;
2822         boost::shared_ptr<Playlist> playlist;
2823         RegionSelection new_selection;
2824         TrackSelection tmptracks;
2825
2826         if (selection->tracks.empty()) {
2827                 
2828                 /* use tracks with selected regions */
2829
2830                 RegionSelection rs; 
2831
2832                 get_regions_for_action (rs);
2833
2834                 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2835                         TimeAxisView* tv = &(*i)->get_time_axis_view();
2836
2837                         if (find (tmptracks.begin(), tmptracks.end(), tv) == tmptracks.end()) {
2838                                 tmptracks.push_back (tv);
2839                         }
2840                 }
2841
2842                 if (tmptracks.empty()) {
2843                         /* no regions selected: do nothing */
2844                         return;
2845                 }
2846
2847         } else {
2848
2849                 tmptracks = selection->tracks;
2850
2851         }
2852
2853         sort_track_selection (&tmptracks);
2854
2855         for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2856
2857                 AudioTimeAxisView* atv;
2858
2859                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
2860
2861                         if (atv->is_audio_track()) {
2862
2863                                 /* no edits to destructive tracks */
2864
2865                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
2866                                         continue;
2867                                 }
2868                                         
2869                                 if ((playlist = atv->playlist()) != 0) {
2870
2871
2872                                         XMLNode *before;
2873                                         bool got_some;
2874
2875                                         before = &(playlist->get_state());
2876                                         got_some = false;
2877
2878                                         /* XXX need to consider musical time selections here at some point */
2879
2880                                         double speed = atv->get_diskstream()->speed();
2881
2882
2883                                         for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2884
2885                                                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2886                                                 latest_regionviews.clear ();
2887
2888                                                 playlist->partition ((nframes64_t)((*t).start * speed), (nframes64_t)((*t).end * speed), true);
2889
2890                                                 c.disconnect ();
2891
2892                                                 if (!latest_regionviews.empty()) {
2893                                                         
2894                                                         got_some = true;
2895
2896                                                         atv->view()->foreach_regionview (bind (sigc::ptr_fun (add_if_covered), &(*t), &new_selection));
2897                                                         
2898                                                         if (!in_command) {
2899                                                                 begin_reversible_command (_("separate"));
2900                                                                 in_command = true;
2901                                                         }
2902                                                         
2903                                                         session->add_command(new MementoCommand<Playlist>(*playlist, before, &playlist->get_state()));
2904                                                         
2905                                                 } 
2906                                         }
2907
2908                                         if (!got_some) {
2909                                                 delete before;
2910                                         }
2911                                 }
2912                         }
2913                 }
2914         }
2915
2916         if (in_command) {
2917                 selection->set (new_selection);
2918                 set_mouse_mode (MouseObject);
2919
2920                 commit_reversible_command ();
2921         }
2922 }
2923
2924 void
2925 Editor::separate_region_from_selection ()
2926 {
2927         /* preferentially use *all* ranges in the time selection if we're in range mode
2928            to allow discontiguous operation, since get_edit_op_range() currently
2929            returns a single range.
2930         */
2931
2932         if (mouse_mode == MouseRange && !selection->time.empty()) {
2933
2934                 separate_regions_between (selection->time);
2935
2936         } else {
2937
2938                 nframes64_t start;
2939                 nframes64_t end;
2940                 
2941                 if (get_edit_op_range (start, end)) {
2942                         
2943                         AudioRange ar (start, end, 1);
2944                         TimeSelection ts;
2945                         ts.push_back (ar);
2946
2947                         separate_regions_between (ts);
2948                 }
2949         }
2950 }
2951
2952 void
2953 Editor::separate_region_from_punch ()
2954 {
2955         Location* loc  = session->locations()->auto_punch_location();
2956         if (loc) {
2957                 separate_regions_using_location (*loc);
2958         }
2959 }
2960
2961 void
2962 Editor::separate_region_from_loop ()
2963 {
2964         Location* loc  = session->locations()->auto_loop_location();
2965         if (loc) {
2966                 separate_regions_using_location (*loc);
2967         }
2968 }
2969
2970 void
2971 Editor::separate_regions_using_location (Location& loc)
2972 {
2973         if (loc.is_mark()) {
2974                 return;
2975         }
2976
2977         AudioRange ar (loc.start(), loc.end(), 1);
2978         TimeSelection ts;
2979
2980         ts.push_back (ar);
2981
2982         separate_regions_between (ts);
2983 }
2984
2985 void
2986 Editor::crop_region_to_selection ()
2987 {
2988         if (!selection->time.empty()) {
2989
2990                 crop_region_to (selection->time.start(), selection->time.end_frame());
2991
2992         } else {
2993
2994                 nframes64_t start;
2995                 nframes64_t end;
2996
2997                 if (get_edit_op_range (start, end)) {
2998                         crop_region_to (start, end);
2999                 }
3000         }
3001                 
3002 }               
3003
3004 void
3005 Editor::crop_region_to (nframes64_t start, nframes64_t end)
3006 {
3007         vector<boost::shared_ptr<Playlist> > playlists;
3008         boost::shared_ptr<Playlist> playlist;
3009         TrackSelection* ts;
3010
3011         if (selection->tracks.empty()) {
3012                 ts = &track_views;
3013         } else {
3014                 sort_track_selection ();
3015                 ts = &selection->tracks;
3016         }
3017         
3018         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
3019                 
3020                 AudioTimeAxisView* atv;
3021                 
3022                 if ((atv = dynamic_cast<AudioTimeAxisView*> ((*i))) != 0) {
3023                         
3024                         if (atv->is_audio_track()) {
3025                                 
3026                                 /* no edits to destructive tracks */
3027
3028                                 if (atv->audio_track()->audio_diskstream()->destructive()) {
3029                                         continue;
3030                                 }
3031
3032                                 if ((playlist = atv->playlist()) != 0) {
3033                                         playlists.push_back (playlist);
3034                                 }
3035                         }
3036                 }
3037         }
3038
3039         if (playlists.empty()) {
3040                 return;
3041         }
3042                 
3043         nframes64_t the_start;
3044         nframes64_t the_end;
3045         nframes64_t cnt;
3046         
3047         begin_reversible_command (_("trim to selection"));
3048         
3049         for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3050                 
3051                 boost::shared_ptr<Region> region;
3052         
3053                 the_start = start;
3054         
3055                 if ((region = (*i)->top_region_at(the_start)) == 0) {
3056                         continue;
3057                 }
3058                 
3059                 /* now adjust lengths to that we do the right thing
3060                    if the selection extends beyond the region
3061                 */
3062                 
3063                 the_start = max (the_start, (nframes64_t) region->position());
3064                 if (max_frames - the_start < region->length()) {
3065                         the_end = the_start + region->length() - 1;
3066                 } else {
3067                         the_end = max_frames;
3068                 }
3069                 the_end = min (end, the_end);
3070                 cnt = the_end - the_start + 1;
3071                 
3072                 XMLNode &before = (*i)->get_state();
3073                 region->trim_to (the_start, cnt, this);
3074                 XMLNode &after = (*i)->get_state();
3075                 session->add_command (new MementoCommand<Playlist>(*(*i), &before, &after));
3076         }
3077         
3078         commit_reversible_command ();
3079 }               
3080
3081 void
3082 Editor::region_fill_track ()
3083 {
3084         nframes64_t end;
3085         RegionSelection rs; 
3086
3087         get_regions_for_action (rs);
3088
3089         if (!session || rs.empty()) {
3090                 return;
3091         }
3092
3093         end = session->current_end_frame ();
3094
3095         begin_reversible_command (_("region fill"));
3096
3097         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3098
3099                 boost::shared_ptr<Region> region ((*i)->region());
3100                 
3101                 // FIXME
3102                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
3103                 if (!ar)
3104                         continue;
3105
3106                 boost::shared_ptr<Playlist> pl = region->playlist();
3107
3108                 if (end <= region->last_frame()) {
3109                         return;
3110                 }
3111
3112                 double times = (double) (end - region->last_frame()) / (double) region->length();
3113
3114                 if (times == 0) {
3115                         return;
3116                 }
3117
3118                 XMLNode &before = pl->get_state();
3119                 pl->add_region (RegionFactory::create (ar), ar->last_frame(), times);
3120                 session->add_command (new MementoCommand<Playlist>(*pl, &before, &pl->get_state()));
3121         }
3122
3123         commit_reversible_command ();
3124 }
3125
3126 void
3127 Editor::region_fill_selection ()
3128 {
3129         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3130                 return;
3131         }
3132
3133         if (selection->time.empty()) {
3134                 return;
3135         }
3136
3137
3138         Glib::RefPtr<TreeSelection> selected = region_list_display.get_selection();
3139
3140         if (selected->count_selected_rows() != 1) {
3141                 return;
3142         }
3143
3144         TreeModel::iterator i = region_list_display.get_selection()->get_selected();
3145         boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
3146
3147         nframes64_t start = selection->time[clicked_selection].start;
3148         nframes64_t end = selection->time[clicked_selection].end;
3149
3150         boost::shared_ptr<Playlist> playlist; 
3151
3152         if (selection->tracks.empty()) {
3153                 return;
3154         }
3155
3156         nframes64_t selection_length = end - start;
3157         float times = (float)selection_length / region->length();
3158         
3159         begin_reversible_command (_("fill selection"));
3160         
3161         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3162
3163                 if ((playlist = (*i)->playlist()) == 0) {
3164                         continue;
3165                 }               
3166                 
3167                 XMLNode &before = playlist->get_state();
3168                 playlist->add_region (RegionFactory::create (region), start, times);
3169                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
3170         }
3171         
3172         commit_reversible_command ();                   
3173 }
3174
3175 void
3176 Editor::set_region_sync_from_edit_point ()
3177 {
3178         nframes64_t where = get_preferred_edit_position ();
3179         RegionSelection rs;
3180         get_regions_for_action (rs);
3181         set_sync_point (where, rs);
3182 }
3183
3184 void
3185 Editor::set_sync_point (nframes64_t where, const RegionSelection& rs)
3186 {
3187         bool in_command = false;
3188
3189         for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3190                 
3191                 if (!(*r)->region()->covers (where)) {
3192                         continue;
3193                 }
3194
3195                 boost::shared_ptr<Region> region ((*r)->region());
3196
3197                 if (!in_command) {
3198                         begin_reversible_command (_("set sync point"));
3199                         in_command = true;
3200                 }
3201
3202                 XMLNode &before = region->playlist()->get_state();
3203                 region->set_sync_position (where);
3204                 XMLNode &after = region->playlist()->get_state();
3205                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3206         }
3207
3208         if (in_command) {
3209                 commit_reversible_command ();
3210         }
3211 }
3212
3213 void
3214 Editor::remove_region_sync ()
3215 {
3216         RegionSelection rs; 
3217
3218         get_regions_for_action (rs);
3219
3220         if (rs.empty()) {
3221                 return;
3222         }
3223
3224         begin_reversible_command (_("remove sync"));
3225         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3226
3227                 XMLNode &before = (*i)->region()->playlist()->get_state();
3228                 (*i)->region()->clear_sync_position ();
3229                 XMLNode &after = (*i)->region()->playlist()->get_state();
3230                 session->add_command(new MementoCommand<Playlist>(*((*i)->region()->playlist()), &before, &after));
3231         }
3232         commit_reversible_command ();
3233 }
3234
3235 void
3236 Editor::naturalize ()
3237 {
3238         RegionSelection rs; 
3239
3240         get_regions_for_action (rs);
3241
3242         if (rs.empty()) {
3243                 return;
3244         }
3245
3246         begin_reversible_command (_("naturalize"));
3247         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3248                 XMLNode &before = (*i)->region()->get_state();
3249                 (*i)->region()->move_to_natural_position (this);
3250                 XMLNode &after = (*i)->region()->get_state();
3251                 session->add_command (new MementoCommand<Region>(*((*i)->region().get()), &before, &after));
3252         }
3253         commit_reversible_command ();
3254 }
3255
3256 void
3257 Editor::align (RegionPoint what)
3258 {
3259         RegionSelection rs; 
3260
3261         get_regions_for_action (rs);
3262         nframes64_t where = get_preferred_edit_position();
3263
3264         if (!rs.empty()) {
3265                 align_selection (what, where, rs);
3266         } else {
3267
3268                 RegionSelection rs;
3269                 get_regions_at (rs, where, selection->tracks);
3270                 align_selection (what, where, rs);
3271         }
3272 }
3273
3274 void
3275 Editor::align_relative (RegionPoint what)
3276 {
3277         nframes64_t where = get_preferred_edit_position();
3278         RegionSelection rs; 
3279
3280         get_regions_for_action (rs);
3281
3282         if (!rs.empty()) {
3283                 align_selection_relative (what, where, rs);
3284         } 
3285 }
3286
3287 struct RegionSortByTime {
3288     bool operator() (const AudioRegionView* a, const AudioRegionView* b) {
3289             return a->region()->position() < b->region()->position();
3290     }
3291 };
3292
3293 void
3294 Editor::align_selection_relative (RegionPoint point, nframes64_t position, const RegionSelection& rs)
3295 {
3296         if (rs.empty()) {
3297                 return;
3298         }
3299
3300         nframes64_t distance = 0;
3301         nframes64_t pos = 0;
3302         int dir = 1;
3303
3304         list<RegionView*> sorted;
3305         rs.by_position (sorted);
3306
3307         boost::shared_ptr<Region> r ((*sorted.begin())->region());
3308
3309         switch (point) {
3310         case Start:
3311                 pos = position;
3312                 if (position > r->position()) {
3313                         distance = position - r->position();
3314                 } else {
3315                         distance = r->position() - position;
3316                         dir = -1;
3317                 }
3318                 break;
3319                 
3320         case End:
3321                 if (position > r->last_frame()) {
3322                         distance = position - r->last_frame();
3323                         pos = r->position() + distance;
3324                 } else {
3325                         distance = r->last_frame() - position;
3326                         pos = r->position() - distance;
3327                         dir = -1;
3328                 }
3329                 break;
3330
3331         case SyncPoint:
3332                 pos = r->adjust_to_sync (position);
3333                 if (pos > r->position()) {
3334                         distance = pos - r->position();
3335                 } else {
3336                         distance = r->position() - pos;
3337                         dir = -1;
3338                 }
3339                 break;  
3340         }
3341
3342         if (pos == r->position()) {
3343                 return;
3344         }
3345
3346         begin_reversible_command (_("align selection (relative)"));
3347
3348         /* move first one specially */
3349
3350         XMLNode &before = r->playlist()->get_state();
3351         r->set_position (pos, this);
3352         XMLNode &after = r->playlist()->get_state();
3353         session->add_command(new MementoCommand<Playlist>(*(r->playlist()), &before, &after));
3354
3355         /* move rest by the same amount */
3356         
3357         sorted.pop_front();
3358         
3359         for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3360
3361                 boost::shared_ptr<Region> region ((*i)->region());
3362
3363                 XMLNode &before = region->playlist()->get_state();
3364                 
3365                 if (dir > 0) {
3366                         region->set_position (region->position() + distance, this);
3367                 } else {
3368                         region->set_position (region->position() - distance, this);
3369                 }
3370
3371                 XMLNode &after = region->playlist()->get_state();
3372                 session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3373
3374         }
3375
3376         commit_reversible_command ();
3377 }
3378
3379 void
3380 Editor::align_selection (RegionPoint point, nframes64_t position, const RegionSelection& rs)
3381 {
3382         if (rs.empty()) {
3383                 return;
3384         }
3385
3386         begin_reversible_command (_("align selection"));
3387
3388         for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3389                 align_region_internal ((*i)->region(), point, position);
3390         }
3391
3392         commit_reversible_command ();
3393 }
3394
3395 void
3396 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, nframes64_t position)
3397 {
3398         begin_reversible_command (_("align region"));
3399         align_region_internal (region, point, position);
3400         commit_reversible_command ();
3401 }
3402
3403 void
3404 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, nframes64_t position)
3405 {
3406         XMLNode &before = region->playlist()->get_state();
3407
3408         switch (point) {
3409         case SyncPoint:
3410                 region->set_position (region->adjust_to_sync (position), this);
3411                 break;
3412
3413         case End:
3414                 if (position > region->length()) {
3415                         region->set_position (position - region->length(), this);
3416                 }
3417                 break;
3418
3419         case Start:
3420                 region->set_position (position, this);
3421                 break;
3422         }
3423
3424         XMLNode &after = region->playlist()->get_state();
3425         session->add_command(new MementoCommand<Playlist>(*(region->playlist()), &before, &after));
3426 }       
3427
3428 void
3429 Editor::trim_region_to_loop ()
3430 {
3431         Location* loc = session->locations()->auto_loop_location();
3432         if (!loc) {
3433                 return;
3434         }
3435         trim_region_to_location (*loc, _("trim to loop"));
3436 }
3437
3438 void
3439 Editor::trim_region_to_punch ()
3440 {
3441         Location* loc = session->locations()->auto_punch_location();
3442         if (!loc) {
3443                 return;
3444         }
3445         trim_region_to_location (*loc, _("trim to punch"));
3446 }
3447
3448 void
3449 Editor::trim_region_to_location (const Location& loc, const char* str)
3450 {
3451         RegionSelection rs;
3452
3453         get_regions_for_action (rs);
3454
3455         begin_reversible_command (str);
3456
3457         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3458                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3459
3460                 if (!arv) {
3461                         continue;
3462                 }
3463
3464                 /* require region to span proposed trim */
3465
3466                 switch (arv->region()->coverage (loc.start(), loc.end())) {
3467                 case OverlapInternal:
3468                         break;
3469                 default:
3470                         continue;
3471                 }
3472                                 
3473                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3474
3475                 if (!atav) {
3476                         return;
3477                 }
3478
3479                 float speed = 1.0;
3480                 nframes64_t start;
3481                 nframes64_t end;
3482
3483                 if (atav->get_diskstream() != 0) {
3484                         speed = atav->get_diskstream()->speed();
3485                 }
3486
3487                 start = session_frame_to_track_frame (loc.start(), speed);
3488                 end = session_frame_to_track_frame (loc.end(), speed);
3489
3490                 XMLNode &before = arv->region()->playlist()->get_state();
3491                 arv->region()->trim_to (start, (end - start), this);
3492                 XMLNode &after = arv->region()->playlist()->get_state();
3493                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3494         }
3495                 
3496         commit_reversible_command ();
3497 }
3498
3499 void
3500 Editor::trim_region_to_edit_point ()
3501 {
3502         RegionSelection rs;
3503         
3504         get_regions_for_action (rs);
3505
3506         nframes64_t where = get_preferred_edit_position();
3507
3508         begin_reversible_command (_("trim region start to edit point"));
3509
3510         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3511                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3512
3513                 if (!arv) {
3514                         continue;
3515                 }
3516
3517                 /* require region to cover trim */
3518
3519                 if (!arv->region()->covers (where)) {
3520                         continue;
3521                 }
3522
3523                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3524
3525                 if (!atav) {
3526                         return;
3527                 }
3528
3529                 float speed = 1.0;
3530
3531                 if (atav->get_diskstream() != 0) {
3532                         speed = atav->get_diskstream()->speed();
3533                 }
3534
3535                 XMLNode &before = arv->region()->playlist()->get_state();
3536                 arv->region()->trim_end( session_frame_to_track_frame(where, speed), this);
3537                 XMLNode &after = arv->region()->playlist()->get_state();
3538                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3539         }
3540                 
3541         commit_reversible_command ();
3542 }
3543
3544 void
3545 Editor::trim_region_from_edit_point ()
3546 {
3547         RegionSelection rs;
3548
3549         get_regions_for_action (rs);
3550
3551         nframes64_t where = get_preferred_edit_position();
3552
3553         begin_reversible_command (_("trim region end to edit point"));
3554
3555         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3556                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3557
3558                 if (!arv) {
3559                         continue;
3560                 }
3561
3562                 /* require region to cover trim */
3563
3564                 if (!arv->region()->covers (where)) {
3565                         continue;
3566                 }
3567
3568                 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3569
3570                 if (!atav) {
3571                         return;
3572                 }
3573
3574                 float speed = 1.0;
3575
3576                 if (atav->get_diskstream() != 0) {
3577                         speed = atav->get_diskstream()->speed();
3578                 }
3579
3580                 XMLNode &before = arv->region()->playlist()->get_state();
3581                 arv->region()->trim_front ( session_frame_to_track_frame(where, speed), this);
3582                 XMLNode &after = arv->region()->playlist()->get_state();
3583                 session->add_command(new MementoCommand<Playlist>(*(arv->region()->playlist()), &before, &after));
3584         }
3585                 
3586         commit_reversible_command ();
3587 }
3588
3589 void
3590 Editor::unfreeze_route ()
3591 {
3592         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3593                 return;
3594         }
3595         
3596         clicked_audio_trackview->audio_track()->unfreeze ();
3597 }
3598
3599 void*
3600 Editor::_freeze_thread (void* arg)
3601 {
3602         PBD::notify_gui_about_thread_creation (pthread_self(), X_("Freeze"));
3603         return static_cast<Editor*>(arg)->freeze_thread ();
3604 }
3605
3606 void*
3607 Editor::freeze_thread ()
3608 {
3609         clicked_audio_trackview->audio_track()->freeze (*current_interthread_info);
3610         return 0;
3611 }
3612
3613 gint
3614 Editor::freeze_progress_timeout (void *arg)
3615 {
3616         interthread_progress_bar.set_fraction (current_interthread_info->progress);
3617         return !(current_interthread_info->done || current_interthread_info->cancel);
3618 }
3619
3620 void
3621 Editor::freeze_route ()
3622 {
3623         if (clicked_audio_trackview == 0 || !clicked_audio_trackview->is_audio_track()) {
3624                 return;
3625         }
3626         
3627         InterThreadInfo itt;
3628
3629         if (interthread_progress_window == 0) {
3630                 build_interthread_progress_window ();
3631         }
3632
3633         interthread_progress_window->set_title (_("Freeze"));
3634         interthread_progress_window->set_position (Gtk::WIN_POS_MOUSE);
3635         interthread_progress_window->show_all ();
3636         interthread_progress_bar.set_fraction (0.0f);
3637         interthread_progress_label.set_text ("");
3638         interthread_cancel_label.set_text (_("Cancel Freeze"));
3639         current_interthread_info = &itt;
3640
3641         interthread_progress_connection = 
3642           Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::freeze_progress_timeout), (gpointer) 0), 100);
3643
3644         itt.done = false;
3645         itt.cancel = false;
3646         itt.progress = 0.0f;
3647         
3648         pthread_attr_t attr;
3649         pthread_attr_init(&attr);
3650         pthread_attr_setstacksize(&attr, 500000);
3651
3652         pthread_create_and_store (X_("freezer"), &itt.thread, &attr, _freeze_thread, this);
3653
3654         pthread_attr_destroy(&attr);
3655
3656         track_canvas->get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
3657
3658         while (!itt.done && !itt.cancel) {
3659                 gtk_main_iteration ();
3660         }
3661
3662         interthread_progress_connection.disconnect ();
3663         interthread_progress_window->hide_all ();
3664         current_interthread_info = 0;
3665         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
3666 }
3667
3668 void
3669 Editor::bounce_range_selection (bool replace, bool enable_processing)
3670 {
3671         if (selection->time.empty()) {
3672                 return;
3673         }
3674
3675         TrackSelection views = selection->tracks;
3676
3677         nframes64_t start = selection->time[clicked_selection].start;
3678         nframes64_t end = selection->time[clicked_selection].end;
3679         nframes64_t cnt = end - start + 1;
3680
3681         begin_reversible_command (_("bounce range"));
3682
3683         for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3684
3685                 AudioTimeAxisView* atv;
3686
3687                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*i)) == 0) {
3688                         continue;
3689                 }
3690                 
3691                 boost::shared_ptr<Playlist> playlist;
3692                 
3693                 if ((playlist = atv->playlist()) == 0) {
3694                         return;
3695                 }
3696
3697                 InterThreadInfo itt;
3698                 
3699                 itt.done = false;
3700                 itt.cancel = false;
3701                 itt.progress = false;
3702
3703                 XMLNode &before = playlist->get_state();
3704                 boost::shared_ptr<Region> r = atv->audio_track()->bounce_range (start, start+cnt, itt, enable_processing);
3705                 
3706                 if (replace) {
3707                         list<AudioRange> ranges;
3708                         ranges.push_back (AudioRange (start, start+cnt, 0));
3709                         playlist->cut (ranges); // discard result
3710                         playlist->add_region (r, start);
3711                 }
3712
3713                 XMLNode &after = playlist->get_state();
3714                 session->add_command (new MementoCommand<Playlist> (*playlist, &before, &after));
3715         }
3716         
3717         commit_reversible_command ();
3718 }
3719
3720 void
3721 Editor::cut ()
3722 {
3723         cut_copy (Cut);
3724 }
3725
3726 void
3727 Editor::copy ()
3728 {
3729         cut_copy (Copy);
3730 }
3731
3732 void 
3733 Editor::cut_copy (CutCopyOp op)
3734 {
3735         /* only cancel selection if cut/copy is successful.*/
3736
3737         string opname;
3738
3739         switch (op) {
3740         case Cut:
3741                 opname = _("cut");
3742                 break;
3743         case Copy:
3744                 opname = _("copy");
3745                 break;
3746         case Clear:
3747                 opname = _("clear");
3748                 break;
3749         }
3750
3751         /* if we're deleting something, and the mouse is still pressed,
3752            the thing we started a drag for will be gone when we release
3753            the mouse button(s). avoid this. see part 2 at the end of
3754            this function.
3755         */
3756
3757         if (op == Cut || op == Clear) {
3758                 if (drag_info.item) {
3759                         drag_info.item->ungrab (0);
3760                         drag_info.item = 0;
3761                 }
3762         }
3763         
3764         cut_buffer->clear ();
3765
3766         if (entered_marker) {
3767
3768                 /* cut/delete op while pointing at a marker */
3769
3770                 bool ignored;
3771                 Location* loc = find_location_from_marker (entered_marker, ignored);
3772
3773                 if (session && loc) {
3774                         Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::really_remove_marker), loc));
3775                 }
3776
3777                 break_drag ();
3778
3779                 return;
3780         }
3781
3782         RegionSelection rs; 
3783
3784         /* we only want to cut regions if some are selected */
3785
3786         if (!selection->regions.empty()) {
3787                 get_regions_for_action (rs);
3788         }
3789
3790         switch (current_mouse_mode()) {
3791         case MouseObject: 
3792                 if (!rs.empty() || !selection->points.empty()) {
3793
3794                         begin_reversible_command (opname + _(" objects"));
3795
3796                         if (!rs.empty()) {
3797                                 cut_copy_regions (op, rs);
3798                                 
3799                                 if (op == Cut) {
3800                                         selection->clear_regions ();
3801                                 }
3802                         }
3803
3804                         if (!selection->points.empty()) {
3805                                 cut_copy_points (op);
3806
3807                                 if (op == Cut) {
3808                                         selection->clear_points ();
3809                                 }
3810                         }
3811
3812                         commit_reversible_command ();   
3813                         break; // terminate case statement here
3814                 } 
3815                 if (!selection->time.empty()) {
3816                         /* don't cause suprises */
3817                         break;
3818                 }
3819                 // fall thru if there was nothing selected
3820                 
3821         case MouseRange:
3822                 if (selection->time.empty()) {
3823                         nframes64_t start, end;
3824                         if (!get_edit_op_range (start, end)) {
3825                                 return;
3826                         }
3827                         selection->set ((TimeAxisView*) 0, start, end);
3828                 }
3829                         
3830                 begin_reversible_command (opname + _(" range"));
3831                 cut_copy_ranges (op);
3832                 commit_reversible_command ();
3833                 
3834                 if (op == Cut) {
3835                         selection->clear_time ();
3836                 }
3837
3838                 break;
3839                 
3840         default:
3841                 break;
3842         }
3843
3844
3845         if (op == Cut || op == Clear) {
3846                 break_drag ();
3847         }
3848 }
3849
3850 void
3851 Editor::cut_copy_points (CutCopyOp op)
3852 {
3853         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3854
3855                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
3856
3857                 if (atv) {
3858                         atv->cut_copy_clear_objects (selection->points, op);
3859                 } 
3860         }
3861 }
3862
3863 struct PlaylistState {
3864     boost::shared_ptr<Playlist> playlist;
3865     XMLNode*  before;
3866 };
3867
3868 struct lt_playlist {
3869     bool operator () (const PlaylistState& a, const PlaylistState& b) {
3870             return a.playlist < b.playlist;
3871     }
3872 };
3873         
3874 struct PlaylistMapping { 
3875     TimeAxisView* tv;
3876     boost::shared_ptr<AudioPlaylist> pl;
3877
3878     PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3879 };
3880
3881 void
3882 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3883 {
3884         /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3885            a map when we want ordered access to both elements. i think.
3886         */
3887
3888         vector<PlaylistMapping> pmap;
3889
3890         nframes64_t first_position = max_frames;
3891         
3892         set<PlaylistState, lt_playlist> freezelist;
3893         pair<set<PlaylistState, lt_playlist>::iterator,bool> insert_result;
3894         
3895         /* get ordering correct before we cut/copy */
3896         
3897         rs.sort_by_position_and_track ();
3898
3899         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3900
3901                 first_position = min ((nframes64_t) (*x)->region()->position(), first_position);
3902
3903                 if (op == Cut || op == Clear) {
3904                         boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3905
3906                         if (pl) {
3907
3908                                 PlaylistState before;
3909                                 before.playlist = pl;
3910                                 before.before = &pl->get_state();
3911                                 
3912                                 insert_result = freezelist.insert (before);
3913                                 
3914                                 if (insert_result.second) {
3915                                         pl->freeze ();
3916                                 }
3917                         }
3918                 }
3919
3920                 TimeAxisView* tv = &(*x)->get_trackview();
3921                 vector<PlaylistMapping>::iterator z;
3922
3923                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3924                         if ((*z).tv == tv) {
3925                                 break;
3926                         }
3927                 }
3928                 
3929                 if (z == pmap.end()) {
3930                         pmap.push_back (PlaylistMapping (tv));
3931                 }
3932         }
3933
3934         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3935
3936                 boost::shared_ptr<AudioPlaylist> pl = boost::dynamic_pointer_cast<AudioPlaylist>((*x)->region()->playlist());
3937                 
3938                 if (!pl) {
3939                         /* impossible, but this handles it for the future */
3940                         continue;
3941                 }
3942
3943                 TimeAxisView& tv = (*x)->get_trackview();
3944                 boost::shared_ptr<AudioPlaylist> npl;
3945                 RegionSelection::iterator tmp;
3946                 
3947                 tmp = x;
3948                 ++tmp;
3949
3950                 vector<PlaylistMapping>::iterator z;
3951                 
3952                 for (z = pmap.begin(); z != pmap.end(); ++z) {
3953                         if ((*z).tv == &tv) {
3954                                 break;
3955                         }
3956                 }
3957                 
3958                 assert (z != pmap.end());
3959                 
3960                 if (!(*z).pl) {
3961                         npl = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (*session, "cutlist", true));
3962                         npl->freeze();
3963                         (*z).pl = npl;
3964                 } else {
3965                         npl = (*z).pl;
3966                 }
3967                 
3968                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>((*x)->region());
3969                 boost::shared_ptr<Region> _xx;
3970                 
3971                 switch (op) {
3972                 case Cut:
3973                         if (!ar) break;
3974                         
3975                         _xx = RegionFactory::create ((*x)->region());
3976                         npl->add_region (_xx, (*x)->region()->position() - first_position);
3977                         pl->remove_region (((*x)->region()));
3978                         break;
3979                         
3980                 case Copy:
3981                         if (!ar) break;
3982
3983                         /* copy region before adding, so we're not putting same object into two different playlists */
3984                         npl->add_region (RegionFactory::create ((*x)->region()), (*x)->region()->position() - first_position);
3985                         break;
3986                         
3987                 case Clear:
3988                         pl->remove_region (((*x)->region()));
3989                         break;
3990                 }
3991
3992                 x = tmp;
3993         }
3994         
3995         list<boost::shared_ptr<Playlist> > foo;
3996         
3997         /* the pmap is in the same order as the tracks in which selected regions occured */
3998         
3999         for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4000                 (*i).pl->thaw();
4001                 foo.push_back ((*i).pl);
4002         }
4003         
4004
4005         if (!foo.empty()) {
4006                 cut_buffer->set (foo);
4007         }
4008
4009         for (set<PlaylistState, lt_playlist>::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4010                 (*pl).playlist->thaw ();
4011                 session->add_command (new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
4012         }
4013 }
4014
4015 void
4016 Editor::cut_copy_ranges (CutCopyOp op)
4017 {
4018         TrackSelection* ts;
4019         TrackSelection entered;
4020
4021         if (selection->tracks.empty()) {
4022                 if (!entered_track) {
4023                         return;
4024                 }
4025                 entered.push_back (entered_track);
4026                 ts = &entered;
4027         } else {
4028                 ts = &selection->tracks;
4029         }
4030
4031         for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
4032                 (*i)->cut_copy_clear (*selection, op);
4033         }
4034 }
4035
4036 void
4037 Editor::paste (float times)
4038 {
4039         paste_internal (get_preferred_edit_position(), times);
4040 }
4041
4042 void
4043 Editor::mouse_paste ()
4044 {
4045         nframes64_t where;
4046         bool ignored;
4047
4048         if (!mouse_frame (where, ignored)) {
4049                 return;
4050         }
4051
4052         snap_to (where);
4053         paste_internal (where, 1);
4054 }
4055
4056 void
4057 Editor::paste_internal (nframes64_t position, float times)
4058 {
4059         bool commit = false;
4060
4061         if (cut_buffer->empty()) {
4062                 return;
4063         }
4064
4065         if (position == max_frames) {
4066                 position = get_preferred_edit_position();
4067         }
4068
4069         begin_reversible_command (_("paste"));
4070
4071         TrackSelection ts;
4072         TrackSelection::iterator i;
4073         size_t nth;
4074
4075         /* get everything in the correct order */
4076
4077
4078         if (!selection->tracks.empty()) {
4079                 sort_track_selection ();
4080                 ts = selection->tracks;
4081         } else if (entered_track) {
4082                 ts.push_back (entered_track);
4083         }
4084
4085         for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4086
4087                 /* undo/redo is handled by individual tracks */
4088
4089                 if ((*i)->paste (position, times, *cut_buffer, nth)) {
4090                         commit = true;
4091                 }
4092         }
4093         
4094         if (commit) {
4095                 commit_reversible_command ();
4096         }
4097 }
4098
4099 void
4100 Editor::paste_named_selection (float times)
4101 {
4102         TrackSelection::iterator t;
4103
4104         Glib::RefPtr<TreeSelection> selected = named_selection_display.get_selection();
4105
4106         if (selected->count_selected_rows() != 1 || selection->tracks.empty()) {
4107                 return;
4108         }
4109
4110         TreeModel::iterator i = selected->get_selected();
4111         NamedSelection* ns = (*i)[named_selection_columns.selection];
4112
4113         list<boost::shared_ptr<Playlist> >::iterator chunk;
4114         list<boost::shared_ptr<Playlist> >::iterator tmp;
4115
4116         chunk = ns->playlists.begin();
4117                 
4118         begin_reversible_command (_("paste chunk"));
4119         
4120         sort_track_selection ();
4121
4122         for (t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
4123                 
4124                 AudioTimeAxisView* atv;
4125                 boost::shared_ptr<Playlist> pl;
4126                 boost::shared_ptr<AudioPlaylist> apl;
4127
4128                 if ((atv = dynamic_cast<AudioTimeAxisView*> (*t)) == 0) {
4129                         continue;
4130                 }
4131
4132                 if ((pl = atv->playlist()) == 0) {
4133                         continue;
4134                 }
4135                 
4136                 if ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) == 0) {
4137                         continue;
4138                 }
4139
4140                 tmp = chunk;
4141                 ++tmp;
4142
4143                 XMLNode &before = apl->get_state();
4144                 apl->paste (*chunk, get_preferred_edit_position(), times);
4145                 session->add_command(new MementoCommand<AudioPlaylist>(*apl, &before, &apl->get_state()));
4146
4147                 if (tmp != ns->playlists.end()) {
4148                         chunk = tmp;
4149                 }
4150         }
4151
4152         commit_reversible_command();
4153 }
4154
4155 void
4156 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4157 {
4158         boost::shared_ptr<Playlist> playlist; 
4159         RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
4160         RegionSelection foo;
4161
4162         begin_reversible_command (_("duplicate region"));
4163
4164         selection->clear_regions ();
4165
4166         for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4167
4168                 boost::shared_ptr<Region> r ((*i)->region());
4169
4170                 TimeAxisView& tv = (*i)->get_time_axis_view();
4171                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&tv);
4172
4173                 latest_regionviews.clear ();
4174                 sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
4175                 
4176                 playlist = (*i)->region()->playlist();
4177                 XMLNode &before = playlist->get_state();
4178                 playlist->duplicate (r, r->last_frame() + 1, times);
4179                 session->add_command(new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
4180
4181                 c.disconnect ();
4182                 
4183                 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4184         }
4185
4186         commit_reversible_command ();
4187
4188         if (!foo.empty()) {
4189                 selection->set (foo);
4190         }
4191 }
4192
4193 void
4194 Editor::duplicate_selection (float times)
4195 {
4196         if (selection->time.empty() || selection->tracks.empty()) {
4197                 return;
4198         }
4199
4200         boost::shared_ptr<Playlist> playlist; 
4201         vector<boost::shared_ptr<AudioRegion> > new_regions;
4202         vector<boost::shared_ptr<AudioRegion> >::iterator ri;
4203                 
4204         create_region_from_selection (new_regions);
4205
4206         if (new_regions.empty()) {
4207                 return;
4208         }
4209         
4210         begin_reversible_command (_("duplicate selection"));
4211
4212         ri = new_regions.begin();
4213
4214         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4215                 if ((playlist = (*i)->playlist()) == 0) {
4216                         continue;
4217                 }
4218                 XMLNode &before = playlist->get_state();
4219                 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4220                 XMLNode &after = playlist->get_state();
4221                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4222
4223                 ++ri;
4224                 if (ri == new_regions.end()) {
4225                         --ri;
4226                 }
4227         }
4228
4229         commit_reversible_command ();
4230 }
4231
4232 void
4233 Editor::reset_point_selection ()
4234 {
4235         /* reset all selected points to the relevant default value */
4236
4237         for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4238                 
4239                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>(&(*i).track);
4240                 
4241                 if (atv) {
4242                         atv->reset_objects (selection->points);
4243                 } 
4244         }
4245 }
4246
4247 void
4248 Editor::center_playhead ()
4249 {
4250         float page = canvas_width * frames_per_unit;
4251         center_screen_internal (playhead_cursor->current_frame, page);
4252 }
4253
4254 void
4255 Editor::center_edit_point ()
4256 {
4257         float page = canvas_width * frames_per_unit;
4258         center_screen_internal (get_preferred_edit_position(), page);
4259 }
4260
4261 void
4262 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4263 {
4264         begin_reversible_command (_("clear playlist"));
4265         XMLNode &before = playlist->get_state();
4266         playlist->clear ();
4267         XMLNode &after = playlist->get_state();
4268         session->add_command (new MementoCommand<Playlist>(*playlist.get(), &before, &after));
4269         commit_reversible_command ();
4270 }
4271
4272 void
4273 Editor::nudge_track (bool use_edit, bool forwards)
4274 {
4275         boost::shared_ptr<Playlist> playlist; 
4276         nframes64_t distance;
4277         nframes64_t next_distance;
4278         nframes64_t start;
4279
4280         if (use_edit) {
4281                 start = get_preferred_edit_position();
4282         } else {
4283                 start = 0;
4284         }
4285
4286         if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4287                 return;
4288         }
4289         
4290         if (selection->tracks.empty()) {
4291                 return;
4292         }
4293         
4294         begin_reversible_command (_("nudge track"));
4295         
4296         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4297
4298                 if ((playlist = (*i)->playlist()) == 0) {
4299                         continue;
4300                 }               
4301                 
4302                 XMLNode &before = playlist->get_state();
4303                 playlist->nudge_after (start, distance, forwards);
4304                 XMLNode &after = playlist->get_state();
4305                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
4306         }
4307         
4308         commit_reversible_command ();                   
4309 }
4310
4311 void
4312 Editor::remove_last_capture ()
4313 {
4314         vector<string> choices;
4315         string prompt;
4316         
4317         if (!session) {
4318                 return;
4319         }
4320
4321         if (Config->get_verify_remove_last_capture()) {
4322                 prompt  = _("Do you really want to destroy the last capture?"
4323                             "\n(This is destructive and cannot be undone)");
4324
4325                 choices.push_back (_("No, do nothing."));
4326                 choices.push_back (_("Yes, destroy it."));
4327                 
4328                 Gtkmm2ext::Choice prompter (prompt, choices);
4329                 
4330                 if (prompter.run () == 1) {
4331                         session->remove_last_capture ();
4332                 }
4333
4334         } else {
4335                 session->remove_last_capture();
4336         }
4337 }
4338
4339 void
4340 Editor::normalize_region ()
4341 {
4342         RegionSelection rs; 
4343
4344         get_regions_for_action (rs);
4345         
4346         if (!session) {
4347                 return;
4348         }
4349
4350         if (rs.empty()) {
4351                 return;
4352         }
4353
4354         begin_reversible_command (_("normalize"));
4355
4356         track_canvas->get_window()->set_cursor (*wait_cursor);
4357         gdk_flush ();
4358
4359         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4360                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4361                 if (!arv)
4362                         continue;
4363                 XMLNode &before = arv->region()->get_state();
4364                 arv->audio_region()->normalize_to (0.0f);
4365                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4366         }
4367
4368         commit_reversible_command ();
4369         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
4370 }
4371
4372
4373 void
4374 Editor::denormalize_region ()
4375 {
4376         if (!session) {
4377                 return;
4378         }
4379
4380         RegionSelection rs; 
4381
4382         get_regions_for_action (rs);
4383
4384         if (rs.empty()) {
4385                 return;
4386         }
4387
4388         begin_reversible_command ("denormalize");
4389
4390         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4391                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4392                 if (!arv)
4393                         continue;
4394                 XMLNode &before = arv->region()->get_state();
4395                 arv->audio_region()->set_scale_amplitude (1.0f);
4396                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4397         }
4398
4399         commit_reversible_command ();
4400 }
4401
4402 void
4403 Editor::adjust_region_scale_amplitude (bool up)
4404 {
4405         if (!session) {
4406                 return;
4407         }
4408
4409         RegionSelection rs; 
4410
4411         get_regions_for_action (rs);
4412
4413         if (rs.empty()) {
4414                 return;
4415         }
4416
4417         begin_reversible_command ("denormalize");
4418
4419         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4420                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4421                 if (!arv)
4422                         continue;
4423                 XMLNode &before = arv->region()->get_state();
4424                 
4425                 double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
4426                 
4427                 if (up) {
4428                         fraction += 0.05;
4429                         fraction = min (fraction, 1.0);
4430                 } else {
4431                         fraction -= 0.05;
4432                         fraction = max (fraction, 0.0);
4433                 }
4434
4435                 if (!up && fraction <= 0) {
4436                         continue;
4437                 }
4438
4439                 fraction = slider_position_to_gain (fraction);
4440
4441                 if (up && fraction >= 2.0) {
4442                         continue;
4443                 }
4444                 
4445                 arv->audio_region()->set_scale_amplitude (fraction);
4446                 session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
4447         }
4448
4449         commit_reversible_command ();
4450 }
4451
4452
4453 void
4454 Editor::reverse_region ()
4455 {
4456         if (!session) {
4457                 return;
4458         }
4459
4460         Reverse rev (*session);
4461         apply_filter (rev, _("reverse regions"));
4462 }
4463
4464 void
4465 Editor::apply_filter (AudioFilter& filter, string command)
4466 {
4467         RegionSelection rs; 
4468
4469         get_regions_for_action (rs);
4470
4471         if (rs.empty()) {
4472                 return;
4473         }
4474
4475         begin_reversible_command (command);
4476
4477         track_canvas->get_window()->set_cursor (*wait_cursor);
4478         gdk_flush ();
4479
4480         for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4481                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4482                 if (!arv)
4483                         continue;
4484
4485                 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4486
4487                 RegionSelection::iterator tmp;
4488
4489                 tmp = r;
4490                 ++tmp;
4491
4492                 if (arv->audio_region()->apply (filter) == 0) {
4493
4494                         XMLNode &before = playlist->get_state();
4495                         playlist->replace_region (arv->region(), filter.results.front(), arv->region()->position());
4496                         XMLNode &after = playlist->get_state();
4497                         session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after));
4498                 } else {
4499                         goto out;
4500                 }
4501
4502                 r = tmp;
4503         }
4504
4505         commit_reversible_command ();
4506         rs.clear ();
4507
4508   out:
4509         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
4510 }
4511
4512 void
4513 Editor::region_selection_op (void (Region::*pmf)(void))
4514 {
4515         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4516                 Region* region = (*i)->region().get();
4517                 (region->*pmf)();
4518         }
4519 }
4520
4521
4522 void
4523 Editor::region_selection_op (void (Region::*pmf)(void*), void *arg)
4524 {
4525         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4526                 Region* region = (*i)->region().get();
4527                 (region->*pmf)(arg);
4528         }
4529 }
4530
4531 void
4532 Editor::region_selection_op (void (Region::*pmf)(bool), bool yn)
4533 {
4534         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
4535                 Region* region = (*i)->region().get();
4536                 (region->*pmf)(yn);
4537         }
4538 }
4539
4540 void
4541 Editor::external_edit_region ()
4542 {
4543         // XXX shouldn't this use get_regions_for_action(rs) too?       
4544         if (!clicked_regionview) {
4545                 return;
4546         }
4547
4548         /* more to come */
4549 }
4550
4551 void
4552 Editor::brush (nframes64_t pos)
4553 {
4554         RegionSelection sel;
4555         RegionSelection rs; 
4556
4557         get_regions_for_action (rs);
4558
4559         snap_to (pos);
4560
4561         if (rs.empty()) {
4562                 return;
4563         }
4564
4565         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4566                 mouse_brush_insert_region ((*i), pos);
4567         }
4568 }
4569
4570 void
4571 Editor::reset_region_gain_envelopes ()
4572 {
4573         RegionSelection rs; 
4574
4575         get_regions_for_action (rs);
4576
4577         if (!session || rs.empty()) {
4578                 return;
4579         }
4580
4581         session->begin_reversible_command (_("reset region gain"));
4582
4583         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4584                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4585                 if (arv) {
4586                         AutomationList& alist (arv->audio_region()->envelope());
4587                         XMLNode& before (alist.get_state());
4588
4589                         arv->audio_region()->set_default_envelope ();
4590                         session->add_command (new MementoCommand<AutomationList>(arv->audio_region()->envelope(), &before, &alist.get_state()));
4591                 }
4592         }
4593
4594         session->commit_reversible_command ();
4595 }
4596
4597 void
4598 Editor::toggle_gain_envelope_visibility ()
4599 {
4600         RegionSelection rs; 
4601
4602         get_regions_for_action (rs);
4603
4604         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4605                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4606                 if (arv) {
4607                         arv->set_envelope_visible (!arv->envelope_visible());
4608                 }
4609         }
4610 }
4611
4612 void
4613 Editor::toggle_gain_envelope_active ()
4614 {
4615         RegionSelection rs; 
4616
4617         get_regions_for_action (rs);
4618
4619         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4620                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4621                 if (arv) {
4622                         arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4623                 }
4624         }
4625 }
4626
4627 void
4628 Editor::toggle_region_lock ()
4629 {
4630         RegionSelection rs; 
4631
4632         get_regions_for_action (rs);
4633
4634         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4635                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4636                 if (arv) {
4637                         arv->audio_region()->set_locked (!arv->audio_region()->locked());
4638                 }
4639         }
4640 }
4641
4642 void
4643 Editor::set_region_lock_style (Region::PositionLockStyle ps)
4644 {
4645         RegionSelection rs; 
4646
4647         get_regions_for_action (rs);
4648
4649         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4650                 (*i)->region()->set_position_lock_style (ps);
4651         }
4652 }
4653
4654
4655 void
4656 Editor::toggle_region_mute ()
4657 {
4658         RegionSelection rs; 
4659
4660         get_regions_for_action (rs);
4661
4662         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4663                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4664                 if (arv) {
4665                         arv->audio_region()->set_muted (!arv->audio_region()->muted());
4666                 }
4667         }
4668 }
4669
4670 void
4671 Editor::toggle_region_opaque ()
4672 {
4673         RegionSelection rs; 
4674
4675         get_regions_for_action (rs);
4676
4677         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4678                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4679                 if (arv) {
4680                         arv->audio_region()->set_opaque (!arv->audio_region()->opaque());
4681                 }
4682         }
4683 }
4684
4685 void
4686 Editor::toggle_record_enable ()
4687 {
4688         bool new_state = false;
4689         bool first = true;
4690         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4691                 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4692                 if (!rtav)
4693                         continue;
4694                 if (!rtav->is_track())
4695                         continue;
4696
4697                 if (first) {
4698                         new_state = !rtav->track()->record_enabled();
4699                         first = false;
4700                 }
4701
4702                 rtav->track()->set_record_enable(new_state, this);
4703         }
4704 }
4705
4706
4707 void
4708 Editor::set_fade_length (bool in)
4709 {
4710         RegionSelection rs; 
4711
4712         get_regions_for_action (rs);
4713
4714         /* we need a region to measure the offset from the start */
4715
4716         RegionView* rv;
4717
4718         if (!rs.empty()) {
4719                 rv = rs.front();
4720         } else if (entered_regionview) {
4721                 rv = entered_regionview;
4722         } else {
4723                 return;
4724         }
4725
4726         nframes64_t pos = get_preferred_edit_position();
4727         nframes64_t len;
4728         char* cmd;
4729         
4730         if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
4731                 /* edit point is outside the relevant region */
4732                 return;
4733         }
4734
4735         if (in) {
4736                 if (pos <= rv->region()->position()) {
4737                         /* can't do it */
4738                         return;
4739                 }
4740                 len = pos - rv->region()->position();
4741                 cmd = _("set fade in length");
4742         } else {
4743                 if (pos >= rv->region()->last_frame()) {
4744                         /* can't do it */
4745                         return;
4746                 }
4747                 len = rv->region()->last_frame() - pos;
4748                 cmd = _("set fade out length");
4749         }
4750
4751         begin_reversible_command (cmd);
4752
4753         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4754                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4755
4756                 if (!tmp) {
4757                         return;
4758                 }
4759
4760                 AutomationList* alist;
4761                 if (in) {
4762                         alist = &tmp->audio_region()->fade_in();
4763                 } else {
4764                         alist = &tmp->audio_region()->fade_out();
4765                 }
4766
4767                 XMLNode &before = alist->get_state();
4768
4769                 if (in) {
4770                         tmp->audio_region()->set_fade_in_length (len);
4771                         tmp->audio_region()->set_fade_in_active (true);
4772                 } else {
4773                         tmp->audio_region()->set_fade_out_length (len);
4774                         tmp->audio_region()->set_fade_out_active (true);
4775                 }
4776                 
4777                 XMLNode &after = alist->get_state();
4778                 session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
4779         }
4780
4781         commit_reversible_command ();
4782 }
4783
4784
4785 void
4786 Editor::toggle_fade_active (bool in)
4787 {
4788         RegionSelection rs; 
4789
4790         get_regions_for_action (rs);
4791
4792         if (rs.empty()) {
4793                 return;
4794         }
4795
4796         const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
4797         bool have_switch = false;
4798         bool yn = false;
4799
4800         begin_reversible_command (cmd);
4801
4802         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4803                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4804                 
4805                 if (!tmp) {
4806                         return;
4807                 }
4808
4809                 boost::shared_ptr<AudioRegion> region (tmp->audio_region());
4810
4811                 /* make the behaviour consistent across all regions */
4812                 
4813                 if (!have_switch) {
4814                         if (in) {
4815                                 yn = region->fade_in_active();
4816                         } else {
4817                                 yn = region->fade_out_active();
4818                         }
4819                         have_switch = true;
4820                 }
4821
4822                 XMLNode &before = region->get_state();
4823                 if (in) {
4824                         region->set_fade_in_active (!yn);
4825                 } else {
4826                         region->set_fade_out_active (!yn);
4827                 }
4828                 XMLNode &after = region->get_state();
4829                 session->add_command(new MementoCommand<AudioRegion>(*region.get(), &before, &after));
4830         }
4831
4832         commit_reversible_command ();
4833 }
4834
4835 void
4836 Editor::set_fade_in_shape (AudioRegion::FadeShape shape)
4837 {
4838         RegionSelection rs; 
4839
4840         get_regions_for_action (rs);
4841
4842         if (rs.empty()) {
4843                 return;
4844         }
4845
4846         begin_reversible_command (_("set fade in shape"));
4847
4848         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4849                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4850
4851                 if (!tmp) {
4852                         return;
4853                 }
4854
4855                 AutomationList& alist = tmp->audio_region()->fade_in();
4856                 XMLNode &before = alist.get_state();
4857
4858                 tmp->audio_region()->set_fade_in_shape (shape);
4859                 
4860                 XMLNode &after = alist.get_state();
4861                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4862         }
4863
4864         commit_reversible_command ();
4865                 
4866 }
4867
4868 void
4869 Editor::set_fade_out_shape (AudioRegion::FadeShape shape)
4870 {
4871         RegionSelection rs; 
4872
4873         get_regions_for_action (rs);
4874
4875         if (rs.empty()) {
4876                 return;
4877         }
4878
4879         begin_reversible_command (_("set fade out shape"));
4880
4881         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4882                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4883
4884                 if (!tmp) {
4885                         return;
4886                 }
4887
4888                 AutomationList& alist = tmp->audio_region()->fade_out();
4889                 XMLNode &before = alist.get_state();
4890
4891                 tmp->audio_region()->set_fade_out_shape (shape);
4892                 
4893                 XMLNode &after = alist.get_state();
4894                 session->add_command(new MementoCommand<AutomationList>(alist, &before, &after));
4895         }
4896
4897         commit_reversible_command ();
4898 }
4899
4900 void
4901 Editor::set_fade_in_active (bool yn)
4902 {
4903         RegionSelection rs; 
4904
4905         get_regions_for_action (rs);
4906
4907         if (rs.empty()) {
4908                 return;
4909         }
4910
4911         begin_reversible_command (_("set fade in active"));
4912
4913         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4914                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4915
4916                 if (!tmp) {
4917                         return;
4918                 }
4919
4920
4921                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4922
4923                 XMLNode &before = ar->get_state();
4924
4925                 ar->set_fade_in_active (yn);
4926                 
4927                 XMLNode &after = ar->get_state();
4928                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4929         }
4930
4931         commit_reversible_command ();
4932 }
4933
4934 void
4935 Editor::set_fade_out_active (bool yn)
4936 {
4937         RegionSelection rs; 
4938
4939         get_regions_for_action (rs);
4940
4941         if (rs.empty()) {
4942                 return;
4943         }
4944
4945         begin_reversible_command (_("set fade out active"));
4946
4947         for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4948                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
4949
4950                 if (!tmp) {
4951                         return;
4952                 }
4953
4954                 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
4955
4956                 XMLNode &before = ar->get_state();
4957
4958                 ar->set_fade_out_active (yn);
4959                 
4960                 XMLNode &after = ar->get_state();
4961                 session->add_command(new MementoCommand<AudioRegion>(*ar, &before, &after));
4962         }
4963
4964         commit_reversible_command ();
4965 }
4966
4967 void
4968 Editor::toggle_selected_region_fades (int dir)
4969 {
4970         RegionSelection rs;
4971         RegionSelection::iterator i;
4972         boost::shared_ptr<AudioRegion> ar;
4973         bool yn;
4974
4975         get_regions_for_action (rs);
4976         
4977         if (rs.empty()) {
4978                 return;
4979         }
4980
4981         for (i = rs.begin(); i != rs.end(); ++i) {
4982                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
4983                         if (dir == -1) {
4984                                 yn = ar->fade_out_active ();
4985                         } else {
4986                                 yn = ar->fade_in_active ();
4987                         }
4988                         break;
4989                 }
4990         }
4991
4992         if (i == rs.end()) {
4993                 return;
4994         }
4995
4996         /* XXX should this undo-able? */
4997
4998         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4999                 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5000                         continue;
5001                 }
5002                 if (dir == 1 || dir == 0) {
5003                         ar->set_fade_in_active (!yn);
5004                 }
5005
5006                 if (dir == -1 || dir == 0) {
5007                         ar->set_fade_out_active (!yn);
5008                 }
5009         }
5010 }
5011
5012
5013 /** Update region fade visibility after its configuration has been changed */
5014 void
5015 Editor::update_region_fade_visibility ()
5016 {
5017         bool _fade_visibility = Config->get_show_region_fades ();
5018
5019         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5020                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5021                 if (v) {
5022                         if (_fade_visibility) {
5023                                 v->audio_view()->show_all_fades ();
5024                         } else {
5025                                 v->audio_view()->hide_all_fades ();
5026                         }
5027                 }
5028         }
5029 }
5030
5031 /** Update crossfade visibility after its configuration has been changed */
5032 void
5033 Editor::update_xfade_visibility ()
5034 {
5035         _xfade_visibility = Config->get_xfades_visible ();
5036         
5037         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5038                 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5039                 if (v) {
5040                         if (_xfade_visibility) {
5041                                 v->show_all_xfades ();
5042                         } else {
5043                                 v->hide_all_xfades ();
5044                         }
5045                 }
5046         }
5047 }
5048
5049 void
5050 Editor::set_edit_point ()
5051 {
5052         nframes64_t where;
5053         bool ignored;
5054
5055         if (!mouse_frame (where, ignored)) {
5056                 return;
5057         }
5058         
5059         snap_to (where);
5060
5061         if (selection->markers.empty()) {
5062                 
5063                 mouse_add_new_marker (where);
5064
5065         } else {
5066                 bool ignored;
5067
5068                 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5069
5070                 if (loc) {
5071                         loc->move_to (where);
5072                 }
5073         }
5074 }
5075
5076 void
5077 Editor::set_playhead_cursor ()
5078 {
5079         if (entered_marker) {
5080                 session->request_locate (entered_marker->position(), session->transport_rolling());
5081         } else {
5082                 nframes64_t where;
5083                 bool ignored;
5084
5085                 if (!mouse_frame (where, ignored)) {
5086                         return;
5087                 }
5088                         
5089                 snap_to (where);
5090                 
5091                 if (session) {
5092                         session->request_locate (where, session->transport_rolling());
5093                 }
5094         }
5095 }
5096
5097 void
5098 Editor::split ()
5099 {
5100         RegionSelection rs; 
5101
5102         get_regions_for_action (rs);
5103
5104         nframes64_t where = get_preferred_edit_position();
5105
5106         if (rs.empty()) {
5107                 return;
5108         }
5109
5110         split_regions_at (where, rs);
5111 }
5112
5113 void
5114 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
5115 {
5116         if (entered_track && mouse_mode == MouseObject) {
5117                 if (!selection->tracks.empty()) {
5118                         if (!selection->selected (entered_track)) {
5119                                 selection->add (entered_track);
5120                         }
5121                 } else {
5122                         /* there is no selection, but this operation requires/prefers selected objects */
5123
5124                         if (op_really_wants_one_track_if_none_are_selected) {
5125                                 selection->set (entered_track);
5126                         }
5127                 }
5128         }
5129 }
5130
5131 void
5132 Editor::trim_region_front ()
5133 {
5134         trim_region (true);
5135 }
5136
5137 void
5138 Editor::trim_region_back ()
5139 {
5140         trim_region (false);
5141 }
5142
5143 void
5144 Editor::trim_region (bool front)
5145 {
5146         nframes64_t where = get_preferred_edit_position();
5147         RegionSelection rs;
5148
5149         get_regions_for_action (rs);
5150
5151         if (rs.empty()) {
5152                 return;
5153         }
5154
5155         begin_reversible_command (front ? _("trim front") : _("trim back"));
5156
5157         for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
5158                 if (!(*i)->region()->locked()) {
5159                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
5160                         XMLNode &before = pl->get_state();
5161                         if (front) {
5162                                 (*i)->region()->trim_front (where, this);       
5163                         } else {
5164                                 (*i)->region()->trim_end (where, this); 
5165                         }
5166                         XMLNode &after = pl->get_state();
5167                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
5168                 }
5169         }
5170
5171         commit_reversible_command ();
5172 }
5173
5174 struct EditorOrderRouteSorter {
5175     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5176             /* use of ">" forces the correct sort order */
5177             return a->order_key ("editor") < b->order_key ("editor");
5178     }
5179 };
5180
5181 void
5182 Editor::select_next_route()
5183 {
5184         if (selection->tracks.empty()) {
5185                 selection->set (track_views.front());
5186                 return;
5187         }
5188
5189         TimeAxisView* current = selection->tracks.front();
5190
5191         RouteUI *rui;
5192         do {
5193                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5194                         if (*i == current) {
5195                                 ++i;
5196                                 if (i != track_views.end()) {
5197                                         current = (*i);
5198                                 } else {
5199                                         current = (*(track_views.begin()));
5200                                         //selection->set (*(track_views.begin()));
5201                                 }
5202                                 break;
5203                         }
5204                 }
5205                 rui = dynamic_cast<RouteUI *>(current);
5206         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5207
5208         selection->set(current);
5209
5210         ensure_track_visible(current);
5211 }
5212
5213 void
5214 Editor::select_prev_route()
5215 {
5216         if (selection->tracks.empty()) {
5217                 selection->set (track_views.front());
5218                 return;
5219         }
5220
5221         TimeAxisView* current = selection->tracks.front();
5222
5223         RouteUI *rui;
5224         do {
5225                 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5226                         if (*i == current) {
5227                                 ++i;
5228                                 if (i != track_views.rend()) {
5229                                         current = (*i);
5230                                 } else {
5231                                         current = *(track_views.rbegin());
5232                                 }
5233                                 break;
5234                         }
5235                 }
5236                 rui = dynamic_cast<RouteUI *>(current);
5237         } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5238
5239         selection->set (current);
5240
5241         ensure_track_visible(current);
5242 }
5243
5244 void
5245 Editor::ensure_track_visible(TimeAxisView *track)
5246 {
5247         if (track->hidden())
5248                 return;
5249
5250         double current_view_min_y = vertical_adjustment.get_value();
5251         double current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5252
5253         double track_min_y = track->y_position;
5254         double track_max_y = track->y_position + (double)track->effective_height;
5255
5256         if (track_min_y >= current_view_min_y &&
5257             track_max_y <= current_view_max_y) {
5258                 return;
5259         }
5260
5261         double new_value;
5262
5263         if (track_min_y < current_view_min_y) {
5264                 // Track is above the current view
5265                 new_value = track_min_y;
5266         } else {
5267                 // Track is below the current view
5268                 new_value = track->y_position + (double)track->effective_height + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5269         }
5270
5271         vertical_adjustment.set_value(new_value);
5272 }
5273
5274 void
5275 Editor::set_loop_from_selection (bool play)
5276 {
5277         if (session == 0 || selection->time.empty()) {
5278                 return;
5279         }
5280
5281         nframes64_t start = selection->time[clicked_selection].start;
5282         nframes64_t end = selection->time[clicked_selection].end;
5283         
5284         set_loop_range (start, end,  _("set loop range from selection"));
5285
5286         if (play) {
5287                 session->request_play_loop (true);
5288                 session->request_locate (start, true);
5289         }
5290 }
5291
5292 void
5293 Editor::set_loop_from_edit_range (bool play)
5294 {
5295         if (session == 0) {
5296                 return;
5297         }
5298
5299         nframes64_t start;
5300         nframes64_t end;
5301         
5302         if (!get_edit_op_range (start, end)) {
5303                 return;
5304         }
5305
5306         set_loop_range (start, end,  _("set loop range from edit range"));
5307
5308         if (play) {
5309                 session->request_play_loop (true);
5310                 session->request_locate (start, true);
5311         }
5312 }
5313
5314 void
5315 Editor::set_loop_from_region (bool play)
5316 {
5317         nframes64_t start = max_frames;
5318         nframes64_t end = 0;
5319
5320         RegionSelection rs; 
5321
5322         get_regions_for_action (rs);
5323
5324         if (rs.empty()) {
5325                 return;
5326         }
5327
5328         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5329                 if ((*i)->region()->position() < start) {
5330                         start = (*i)->region()->position();
5331                 }
5332                 if ((*i)->region()->last_frame() + 1 > end) {
5333                         end = (*i)->region()->last_frame() + 1;
5334                 }
5335         }
5336
5337         set_loop_range (start, end, _("set loop range from region"));
5338
5339         if (play) {
5340                 session->request_play_loop (true);
5341                 session->request_locate (start, true);
5342         }
5343 }
5344
5345 void
5346 Editor::set_punch_from_selection ()
5347 {
5348         if (session == 0 || selection->time.empty()) {
5349                 return;
5350         }
5351
5352         nframes64_t start = selection->time[clicked_selection].start;
5353         nframes64_t end = selection->time[clicked_selection].end;
5354         
5355         set_punch_range (start, end,  _("set punch range from selection"));
5356 }
5357
5358 void
5359 Editor::set_punch_from_edit_range ()
5360 {
5361         if (session == 0) {
5362                 return;
5363         }
5364
5365         nframes64_t start;
5366         nframes64_t end;
5367         
5368         if (!get_edit_op_range (start, end)) {
5369                 return;
5370         }
5371
5372         set_punch_range (start, end,  _("set punch range from edit range"));
5373 }
5374
5375 void
5376 Editor::set_punch_from_region ()
5377 {
5378         nframes64_t start = max_frames;
5379         nframes64_t end = 0;
5380
5381         RegionSelection rs; 
5382
5383         get_regions_for_action (rs);
5384
5385         if (rs.empty()) {
5386                 return;
5387         }
5388
5389         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5390                 if ((*i)->region()->position() < start) {
5391                         start = (*i)->region()->position();
5392                 }
5393                 if ((*i)->region()->last_frame() + 1 > end) {
5394                         end = (*i)->region()->last_frame() + 1;
5395                 }
5396         }
5397
5398         set_punch_range (start, end, _("set punch range from region"));
5399 }
5400
5401 void
5402 Editor::pitch_shift_regions ()
5403 {
5404         RegionSelection rs; 
5405
5406         get_regions_for_action (rs);
5407         
5408         if (rs.empty()) {
5409                 return;
5410         }
5411
5412         pitch_shift (rs, 1.2);
5413 }
5414         
5415 void
5416 Editor::use_region_as_bar ()
5417 {
5418         if (!session) {
5419                 return;
5420         }
5421
5422         RegionSelection rs; 
5423
5424         get_regions_for_action (rs);
5425
5426         if (rs.empty()) {
5427                 return;
5428         }
5429
5430         RegionView* rv = rs.front();
5431
5432         define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5433 }
5434
5435 void
5436 Editor::use_range_as_bar ()
5437 {
5438         nframes64_t start, end;
5439         if (get_edit_op_range (start, end)) {
5440                 define_one_bar (start, end);
5441         }
5442 }
5443
5444 void
5445 Editor::define_one_bar (nframes64_t start, nframes64_t end)
5446 {
5447         nframes64_t length = end - start;
5448         
5449         const Meter& m (session->tempo_map().meter_at (start));
5450
5451         /* length = 1 bar */
5452
5453         /* now we want frames per beat.
5454            we have frames per bar, and beats per bar, so ...
5455         */
5456
5457         double frames_per_beat = length / m.beats_per_bar();
5458         
5459         /* beats per minute = */
5460
5461         double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
5462
5463         /* now decide whether to:
5464
5465             (a) set global tempo 
5466             (b) add a new tempo marker
5467
5468         */
5469
5470         const TempoSection& t (session->tempo_map().tempo_section_at (start));
5471
5472         bool do_global = false;
5473
5474         if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) {
5475                 
5476                 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5477                    at the start, or create a new marker
5478                 */
5479
5480                 vector<string> options;
5481                 options.push_back (_("Cancel"));
5482                 options.push_back (_("Add new marker"));
5483                 options.push_back (_("Set global tempo"));
5484                 Choice c (_("Do you want to set the global tempo or add new tempo marker?"),
5485                           options);
5486                 c.set_default_response (2);
5487
5488                 switch (c.run()) {
5489                 case 0:
5490                         return;
5491
5492                 case 2:
5493                         do_global = true;
5494                         break;
5495
5496                 default:
5497                         do_global = false;
5498                 }
5499
5500         } else {
5501
5502                 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5503                    if the marker is at the region starter, change it, otherwise add
5504                    a new tempo marker 
5505                 */
5506         }
5507
5508         begin_reversible_command (_("set tempo from region"));
5509         XMLNode& before (session->tempo_map().get_state());
5510
5511         if (do_global) {
5512                 session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5513         } else if (t.frame() == start) {
5514                 session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5515         } else {
5516                 session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5517         }
5518
5519         XMLNode& after (session->tempo_map().get_state());
5520
5521         session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
5522         commit_reversible_command ();
5523 }
5524
5525 void
5526 Editor::split_region_at_transients ()
5527 {
5528         AnalysisFeatureList positions;
5529
5530         if (!session) {
5531                 return;
5532         }
5533
5534         RegionSelection rs; 
5535
5536         get_regions_for_action (rs);
5537
5538         if (rs.empty()) {
5539                 return;
5540         }
5541
5542         session->begin_reversible_command (_("split regions"));
5543
5544         for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5545
5546                 RegionSelection::iterator tmp;
5547
5548                 tmp = i;
5549                 ++tmp;
5550
5551                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5552                 
5553                 if (ar && (ar->get_transients (positions) == 0)) {
5554                         split_region_at_points ((*i)->region(), positions, true);
5555                         positions.clear ();
5556                 }
5557                 
5558                 i = tmp;
5559         }
5560
5561         session->commit_reversible_command ();
5562
5563 }
5564
5565 void
5566 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret)
5567 {
5568         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
5569         bool use_rhythmic_rodent = false;
5570
5571         if (!ar) {
5572                 return;
5573         }
5574         
5575         boost::shared_ptr<Playlist> pl = ar->playlist();
5576         
5577         if (!pl) {
5578                 return;
5579         }
5580         
5581         if (positions.empty()) {
5582                 return;
5583         }
5584
5585
5586         if (positions.size() > 20) {
5587                 Glib::ustring msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), ar->name(), positions.size() + 1);
5588                 MessageDialog msg (msgstr,
5589                                    false,
5590                                    Gtk::MESSAGE_INFO,
5591                                    Gtk::BUTTONS_OK_CANCEL);
5592
5593                 if (can_ferret) {
5594                         msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5595                         msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5596                 } else {
5597                         msg.set_secondary_text (_("Press OK to continue with this split operation"));
5598                 }
5599
5600                 msg.set_title (_("Excessive split?"));
5601                 msg.present ();
5602
5603                 int response = msg.run();
5604                 msg.hide ();
5605                 switch (response) {
5606                 case RESPONSE_OK:
5607                         break;
5608                 case RESPONSE_APPLY:
5609                         use_rhythmic_rodent = true;
5610                         break;
5611                 default:
5612                         return;
5613                 }
5614         }
5615         
5616         if (use_rhythmic_rodent) {
5617                 show_rhythm_ferret ();
5618                 return;
5619         }
5620
5621         AnalysisFeatureList::const_iterator x;  
5622         
5623         nframes64_t pos = ar->position();
5624
5625         XMLNode& before (pl->get_state());
5626         
5627         x = positions.begin();
5628         
5629         while (x != positions.end()) {
5630                 if ((*x) > pos) {
5631                         break;
5632                 }
5633                 ++x;
5634         }
5635         
5636         if (x == positions.end()) {
5637                 return;
5638         }
5639         
5640         pl->freeze ();
5641         pl->remove_region (ar);
5642         
5643         while (x != positions.end()) {
5644                 
5645                 /* file start = original start + how far we from the initial position ? 
5646                  */
5647                 
5648                 nframes64_t file_start = ar->start() + (pos - ar->position());
5649
5650                 /* length = next position - current position
5651                  */
5652                 
5653                 nframes64_t len = (*x) - pos;
5654
5655                 /* XXX we do we really want to allow even single-sample regions?
5656                    shouldn't we have some kind of lower limit on region size?
5657                 */
5658
5659                 if (len <= 0) {
5660                         break;
5661                 }
5662                 
5663                 string new_name;
5664                 
5665                 if (session->region_name (new_name, ar->name())) {
5666                         break;
5667                 }
5668                 
5669                 /* do NOT announce new regions 1 by one, just wait till they are all done */
5670
5671                 boost::shared_ptr<Region> r = RegionFactory::create (ar->get_sources(), file_start, len, new_name, 0, Region::DefaultFlags, false);
5672                 pl->add_region (r, pos);
5673
5674                 pos += len;
5675                 ++x;
5676
5677                 if (*x > ar->last_frame()) {
5678
5679                         /* add final fragment */
5680                         
5681                         file_start = ar->start() + (pos - ar->position());
5682                         len = ar->last_frame() - pos;
5683
5684                         boost::shared_ptr<Region> r = RegionFactory::create (ar->get_sources(), file_start, len, new_name, 0, Region::DefaultFlags);
5685                         pl->add_region (r, pos);
5686
5687                         break;
5688                 }
5689         } 
5690
5691         pl->thaw ();
5692
5693         XMLNode& after (pl->get_state());
5694         
5695         session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
5696 }
5697
5698 void
5699 Editor::tab_to_transient (bool forward)
5700 {
5701         AnalysisFeatureList positions;
5702
5703         if (!session) {
5704                 return;
5705         }
5706
5707         nframes64_t pos = session->audible_frame ();
5708
5709         if (!selection->tracks.empty()) {
5710
5711                 for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
5712
5713                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
5714
5715                         if (rtv) {
5716                                 boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
5717                                 if (ds) {
5718                                         boost::shared_ptr<Playlist> pl = rtv->get_diskstream()->playlist ();
5719                                         if (pl) {
5720                                                 nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1);
5721                                                 
5722                                                 if (result >= 0) {
5723                                                         positions.push_back (result);
5724                                                 }
5725                                         }
5726                                 }
5727                         }
5728                 }
5729
5730         } else {
5731                 
5732                 RegionSelection rs; 
5733
5734                 get_regions_for_action (rs);
5735         
5736                 if (rs.empty()) {
5737                         return;
5738                 }
5739                 
5740                 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5741                         (*r)->region()->get_transients (positions);
5742                 }
5743         }
5744
5745         TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0);
5746
5747         if (forward) {
5748                 AnalysisFeatureList::iterator x;
5749
5750                 for (x = positions.begin(); x != positions.end(); ++x) {
5751                         if ((*x) > pos) {
5752                                 break;
5753                         }
5754                 }
5755
5756                 if (x != positions.end ()) {
5757                         session->request_locate (*x);
5758                 }
5759
5760         } else {
5761                 AnalysisFeatureList::reverse_iterator x;
5762
5763                 for (x = positions.rbegin(); x != positions.rend(); ++x) {
5764                         if ((*x) < pos) {
5765                                 break;
5766                         }
5767                 }
5768
5769                 if (x != positions.rend ()) {
5770                         session->request_locate (*x);
5771                 }
5772         }
5773 }
5774
5775 void
5776 Editor::playhead_forward_to_grid ()
5777 {
5778         if (!session) return;
5779         nframes64_t pos = playhead_cursor->current_frame;
5780         if (pos < max_frames - 1) {
5781                 pos += 2;
5782                 snap_to_internal (pos, 1, false);
5783                 session->request_locate (pos);
5784         }
5785 }
5786
5787
5788 void
5789 Editor::playhead_backward_to_grid ()
5790 {
5791         if (!session) return;
5792         nframes64_t pos = playhead_cursor->current_frame;
5793         if (pos > 2) {
5794                 pos -= 2;
5795                 snap_to_internal (pos, -1, false);
5796                 session->request_locate (pos);
5797         }
5798 }
5799
5800 void
5801 Editor::set_track_height (uint32_t h)
5802 {
5803         TrackSelection& ts (selection->tracks);
5804
5805         if (ts.empty()) {
5806                 return;
5807         }
5808
5809         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5810                 (*x)->set_height (h);
5811         }
5812 }
5813
5814 void
5815 Editor::set_track_height_largest ()
5816 {
5817         set_track_height (TimeAxisView::hLargest);
5818 }
5819 void
5820 Editor::set_track_height_large ()
5821 {
5822         set_track_height (TimeAxisView::hLarge);
5823 }
5824 void
5825 Editor::set_track_height_larger ()
5826 {
5827         set_track_height (TimeAxisView::hLarger);
5828 }
5829 void
5830 Editor::set_track_height_normal ()
5831 {
5832         set_track_height (TimeAxisView::hNormal);
5833 }
5834 void
5835 Editor::set_track_height_smaller ()
5836 {
5837         set_track_height (TimeAxisView::hSmaller);
5838 }
5839 void
5840 Editor::set_track_height_small ()
5841 {
5842         set_track_height (TimeAxisView::hSmall);
5843 }
5844
5845 void
5846 Editor::toggle_tracks_active ()
5847 {
5848         TrackSelection& ts (selection->tracks);
5849         bool first = true;
5850         bool target = false;
5851
5852         if (ts.empty()) {
5853                 return;
5854         }
5855
5856         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5857                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
5858
5859                 if (rtv) {
5860                         if (first) {
5861                                 target = !rtv->_route->active();
5862                                 first = false;
5863                         }
5864                         rtv->_route->set_active (target);
5865                 }
5866         }
5867 }
5868
5869 void
5870 Editor::remove_tracks ()
5871 {
5872         TrackSelection& ts (selection->tracks);
5873
5874         if (ts.empty()) {
5875                 return;
5876         }
5877
5878         vector<string> choices;
5879         string prompt;
5880         int ntracks = 0;
5881         int nbusses = 0;
5882         const char* trackstr;
5883         const char* busstr;
5884         vector<boost::shared_ptr<Route> > routes;
5885
5886         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5887                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
5888                 if (rtv) {
5889                         if (rtv->is_track()) {
5890                                 ntracks++;
5891                         } else {
5892                                 nbusses++;
5893                         }
5894                 }
5895                 routes.push_back (rtv->_route);
5896         }
5897         
5898         if (ntracks + nbusses == 0) {
5899                 return;
5900         }
5901
5902         if (ntracks > 1) {
5903                 trackstr = _("tracks");
5904         } else {
5905                 trackstr = _("track");
5906         }
5907
5908         if (nbusses > 1) {
5909                 busstr = _("busses");
5910         } else {
5911                 busstr = _("bus");
5912         }
5913
5914         if (ntracks) {
5915                 if (nbusses) {
5916                         prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
5917                                                     "(You may also lose the playlists associated with the %2)\n\n"
5918                                                     "This action cannot be undone!"),
5919                                                   ntracks, trackstr, nbusses, busstr);
5920                 } else {
5921                         prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
5922                                                     "(You may also lose the playlists associated with the %2)\n\n"
5923                                                     "This action cannot be undone!"),
5924                                                   ntracks, trackstr);
5925                 }
5926         } else if (nbusses) {
5927                 prompt  = string_compose (_("Do you really want to remove %1 %2?"),
5928                                           nbusses, busstr);
5929         }
5930
5931         choices.push_back (_("No, do nothing."));
5932         if (ntracks + nbusses > 1) {
5933                 choices.push_back (_("Yes, remove them."));
5934         } else {
5935                 choices.push_back (_("Yes, remove it."));
5936         }
5937
5938         Choice prompter (prompt, choices);
5939
5940         if (prompter.run () != 1) {
5941                 return;
5942         }
5943
5944         for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
5945                 session->remove_route (*x);
5946         }
5947 }
5948
5949 void
5950 Editor::set_waveform_scale (WaveformScale ws)
5951 {
5952         TrackSelection& ts (selection->tracks);
5953
5954         if (ts.empty()) {
5955                 return;
5956         }
5957
5958         for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
5959                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (*x);
5960                 if (atv) {
5961                         atv->set_waveform_scale (ws);
5962                 }
5963         }
5964 }       
5965
5966 void
5967 Editor::do_insert_time ()
5968 {
5969         if (selection->tracks.empty()) {
5970                 return;
5971         }
5972
5973         nframes64_t pos = get_preferred_edit_position ();
5974         ArdourDialog d (*this, _("Insert Time"));
5975         VButtonBox button_box;
5976         VBox option_box;
5977         RadioButtonGroup group;
5978         RadioButton leave_button (group, _("Stay in position"));
5979         RadioButton move_button (group, _("Move"));
5980         RadioButton split_button (group, _("Split & Later Section Moves"));
5981         Label intersect_option_label (_("Intersected regions should:"));
5982         CheckButton glue_button (_("Move Glued Regions"));
5983         CheckButton marker_button (_("Move Markers"));
5984         CheckButton tempo_button (_("Move Tempo & Meters"));
5985         AudioClock clock ("insertTimeClock", true, X_("InsertTimeClock"), true, true, true);
5986         HBox clock_box;
5987
5988         clock.set (0);
5989         clock.set_session (session);
5990         clock.set_bbt_reference (pos);
5991
5992         clock_box.pack_start (clock, false, true);
5993
5994         option_box.set_spacing (6);
5995         option_box.pack_start (intersect_option_label, false, false);
5996         option_box.pack_start (button_box, false, false);
5997         option_box.pack_start (glue_button, false, false);
5998         option_box.pack_start (marker_button, false, false);
5999         option_box.pack_start (tempo_button, false, false);
6000
6001         button_box.pack_start (leave_button, false, false);
6002         button_box.pack_start (move_button, false, false);
6003         button_box.pack_start (split_button, false, false);
6004                                       
6005         d.get_vbox()->set_border_width (12);
6006         d.get_vbox()->pack_start (clock_box, false, false);
6007         d.get_vbox()->pack_start (option_box, false, false);
6008         
6009         leave_button.show ();
6010         move_button.show ();
6011         split_button.show ();
6012         intersect_option_label.show ();
6013         option_box.show ();
6014         button_box.show ();
6015         glue_button.show ();
6016         clock.show_all();
6017         clock_box.show ();
6018         marker_button.show ();
6019         tempo_button.show ();
6020
6021         d.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
6022         d.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
6023         d.show ();
6024
6025         int response = d.run ();
6026
6027         if (response != RESPONSE_OK) {
6028                 return;
6029         }
6030         
6031         nframes64_t distance = clock.current_duration (pos);
6032
6033         if (distance == 0) {
6034                 return;
6035         }
6036
6037         InsertTimeOption opt;
6038
6039         if (leave_button.get_active()) {
6040                 opt = LeaveIntersected;
6041         } else if (move_button.get_active()) {
6042                 opt = MoveIntersected;
6043         } else {
6044                 opt = SplitIntersected;
6045         }
6046
6047         insert_time (pos, distance, opt, glue_button.get_active(), marker_button.get_active(), tempo_button.get_active());
6048 }
6049
6050 void
6051 Editor::insert_time (nframes64_t pos, nframes64_t frames, InsertTimeOption opt, 
6052                      bool ignore_music_glue, bool markers_too, bool tempo_too)
6053 {
6054         bool commit = false;
6055
6056         if (Config->get_edit_mode() == Lock) {
6057                 return;
6058         }
6059
6060         begin_reversible_command (_("insert time"));
6061
6062         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
6063                 /* regions */
6064                 boost::shared_ptr<Playlist> pl = (*x)->playlist();
6065                 
6066                 if (pl) {
6067
6068                         XMLNode &before = pl->get_state();
6069                         
6070                         if (opt == SplitIntersected) {
6071                                 pl->split (pos);
6072                         }
6073                         
6074                         pl->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6075                         
6076                         XMLNode &after = pl->get_state();
6077                         
6078                         session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
6079                         commit = true;
6080                 }
6081                         
6082                 /* automation */
6083                 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6084                 if (rtav) {
6085                         rtav->route ()->shift (pos, frames);
6086                         commit = true;
6087                 }
6088         }
6089
6090         /* markers */
6091         if (markers_too) {
6092                 bool moved = false;
6093                 XMLNode& before (session->locations()->get_state());
6094                 Locations::LocationList copy (session->locations()->list());
6095
6096                 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6097                         
6098                         Locations::LocationList::const_iterator tmp;
6099
6100                         if ((*i)->start() >= pos) {
6101                                 (*i)->set_start ((*i)->start() + frames);
6102                                 if (!(*i)->is_mark()) {
6103                                         (*i)->set_end ((*i)->end() + frames);
6104                                 }
6105                                 moved = true;
6106                         }
6107                 }
6108
6109                 if (moved) {
6110                         XMLNode& after (session->locations()->get_state());
6111                         session->add_command (new MementoCommand<Locations>(*session->locations(), &before, &after));
6112                 }
6113         }
6114         
6115         if (tempo_too)
6116                 session->tempo_map().insert_time (pos, frames);
6117
6118         if (commit) {
6119                 commit_reversible_command ();
6120         }
6121 }
6122
6123 void
6124 Editor::fit_tracks ()
6125 {
6126         if (selection->tracks.empty()) {
6127                 return;
6128         }
6129
6130         uint32_t child_heights = 0;
6131
6132         for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
6133
6134                 if (!(*t)->marked_for_display()) {
6135                         continue;
6136                 }
6137
6138                 child_heights += ((*t)->effective_height - (*t)->current_height());
6139         }
6140
6141         uint32_t h = (uint32_t) floor ((canvas_height - child_heights - canvas_timebars_vsize)/selection->tracks.size());
6142         double first_y_pos = DBL_MAX;
6143
6144         if (h < TimeAxisView::hSmall) {
6145                 MessageDialog msg (*this, _("There are too many selected tracks to fit in the current window"));
6146                 /* too small to be displayed */
6147                 return;
6148         }
6149
6150         undo_visual_stack.push_back (current_visual_state());
6151         
6152         /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6153         
6154         bool prev_was_selected = false;
6155         bool is_selected = selection->selected (track_views.front());
6156         bool next_is_selected;
6157
6158         for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
6159
6160                 TrackViewList::iterator next;
6161                 
6162                 next = t;
6163                 ++next;
6164                 
6165                 if (next != track_views.end()) {
6166                         next_is_selected = selection->selected (*next);
6167                 } else {
6168                         next_is_selected = false;
6169                 }
6170
6171                 if (is_selected) {
6172                         (*t)->set_height (h);
6173                         first_y_pos = std::min ((*t)->y_position, first_y_pos);
6174                 } else {
6175                         if (prev_was_selected && next_is_selected) {
6176                                 hide_track_in_display (**t);
6177                         }
6178                 }
6179
6180                 prev_was_selected = is_selected;
6181                 is_selected = next_is_selected;
6182         }
6183
6184         /* 
6185            set the controls_layout height now, because waiting for its size 
6186            request signal handler will cause the vertical adjustment setting to fail 
6187         */ 
6188
6189         controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6190         vertical_adjustment.set_value (first_y_pos);
6191
6192         redo_visual_stack.push_back (current_visual_state());
6193 }
6194
6195 void
6196 Editor::save_visual_state (uint32_t n)
6197 {
6198         while (visual_states.size() <= n) {
6199                 visual_states.push_back (0);
6200         }
6201
6202         if (visual_states[n] != 0) {
6203                 delete visual_states[n];
6204         }
6205
6206         visual_states[n] = current_visual_state (true);
6207         gdk_beep ();
6208 }
6209
6210 void
6211 Editor::goto_visual_state (uint32_t n)
6212 {
6213         if (visual_states.size() <= n) {
6214                 return;
6215         }
6216
6217         if (visual_states[n] == 0) {
6218                 return;
6219         }
6220
6221         use_visual_state (*visual_states[n]);
6222 }
6223
6224 void
6225 Editor::start_visual_state_op (uint32_t n)
6226 {
6227         if (visual_state_op_connection.empty()) {
6228                 visual_state_op_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::end_visual_state_op), n), 1000);
6229         }
6230 }
6231
6232 void
6233 Editor::cancel_visual_state_op (uint32_t n)
6234 {
6235         if (!visual_state_op_connection.empty()) {
6236                 visual_state_op_connection.disconnect();
6237                 goto_visual_state (n);
6238         } else {
6239                 //we land here if called from the menu OR if end_visual_state_op has been called
6240                 //so check if we are already in visual state n
6241                 // XXX not yet checking it at all, but redoing does not hurt
6242                 goto_visual_state (n);
6243         }
6244 }
6245
6246 bool
6247 Editor::end_visual_state_op (uint32_t n)
6248 {
6249         visual_state_op_connection.disconnect();
6250         save_visual_state (n);
6251         
6252         PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6253         char buf[32];
6254         snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6255         pup->set_text (buf);
6256         pup->touch();
6257
6258         return false; // do not call again
6259 }
6260