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