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