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