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