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