1 /*
2  * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2005-2009 Sampo Savolainen <v2@iki.fi>
4  * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
7  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
8  * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
9  * Copyright (C) 2013-2016 Colin Fletcher <colin.m.fletcher@googlemail.com>
10  * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
11  * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
12  * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
13  * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
14  * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with this program; if not, write to the Free Software Foundation, Inc.,
28  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29  */
30 
31 /* Note: public Editor methods are documented in public_editor.h */
32 
33 #include <unistd.h>
34 
35 #include <cstdlib>
36 #include <cmath>
37 #include <string>
38 #include <limits>
39 #include <map>
40 #include <set>
41 
42 #include <gtkmm/messagedialog.h>
43 
44 #include "pbd/error.h"
45 #include "pbd/basename.h"
46 #include "pbd/pthread_utils.h"
47 #include "pbd/memento_command.h"
48 #include "pbd/unwind.h"
49 #include "pbd/whitespace.h"
50 #include "pbd/stateful_diff_command.h"
51 
52 #include "gtkmm2ext/utils.h"
53 
54 #include "widgets/choice.h"
55 #include "widgets/popup.h"
56 #include "widgets/prompter.h"
57 
58 #include "ardour/audioengine.h"
59 #include "ardour/audio_track.h"
60 #include "ardour/audioregion.h"
61 #include "ardour/boost_debug.h"
62 #include "ardour/dB.h"
63 #include "ardour/location.h"
64 #include "ardour/midi_region.h"
65 #include "ardour/midi_track.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist_factory.h"
68 #include "ardour/profile.h"
69 #include "ardour/quantize.h"
70 #include "ardour/legatize.h"
71 #include "ardour/region_factory.h"
72 #include "ardour/reverse.h"
73 #include "ardour/selection.h"
74 #include "ardour/session.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/source.h"
77 #include "ardour/strip_silence.h"
78 #include "ardour/transient_detector.h"
79 #include "ardour/transport_master_manager.h"
80 #include "ardour/transpose.h"
81 #include "ardour/vca_manager.h"
82 
83 #include "canvas/canvas.h"
84 
85 #include "actions.h"
86 #include "ardour_message.h"
87 #include "ardour_ui.h"
88 #include "audio_region_view.h"
89 #include "audio_streamview.h"
90 #include "audio_time_axis.h"
91 #include "automation_region_view.h"
92 #include "automation_time_axis.h"
93 #include "control_point.h"
94 #include "debug.h"
95 #include "editing.h"
96 #include "editor.h"
97 #include "editor_cursors.h"
98 #include "editor_drag.h"
99 #include "editor_regions.h"
100 #include "editor_sources.h"
101 #include "editor_routes.h"
102 #include "gui_thread.h"
103 #include "insert_remove_time_dialog.h"
104 #include "interthread_progress_window.h"
105 #include "item_counts.h"
106 #include "keyboard.h"
107 #include "midi_region_view.h"
108 #include "mixer_ui.h"
109 #include "mixer_strip.h"
110 #include "mouse_cursors.h"
111 #include "normalize_dialog.h"
112 #include "note.h"
113 #include "paste_context.h"
114 #include "patch_change_dialog.h"
115 #include "quantize_dialog.h"
116 #include "region_gain_line.h"
117 #include "rgb_macros.h"
118 #include "route_time_axis.h"
119 #include "selection.h"
120 #include "selection_templates.h"
121 #include "streamview.h"
122 #include "strip_silence_dialog.h"
123 #include "time_axis_view.h"
124 #include "timers.h"
125 #include "transpose_dialog.h"
126 #include "transform_dialog.h"
127 #include "ui_config.h"
128 #include "utils.h"
129 #include "vca_time_axis.h"
130 
131 #include "pbd/i18n.h"
132 
133 using namespace std;
134 using namespace ARDOUR;
135 using namespace PBD;
136 using namespace Gtk;
137 using namespace Gtkmm2ext;
138 using namespace ArdourWidgets;
139 using namespace Editing;
140 using Gtkmm2ext::Keyboard;
141 
142 /***********************************************************************
143   Editor operations
144  ***********************************************************************/
145 
146 void
undo(uint32_t n)147 Editor::undo (uint32_t n)
148 {
149 	if (_session && _session->actively_recording()) {
150 		/* no undo allowed while recording. Session will check also,
151 		   but we don't even want to get to that.
152 		*/
153 		return;
154 	}
155 
156 	if (_drags->active ()) {
157 		_drags->abort ();
158 	}
159 	paste_count = 0;
160 
161 	if (_session) {
162 		_session->undo (n);
163 		if (_session->undo_depth() == 0) {
164 			undo_action->set_sensitive(false);
165 		}
166 		redo_action->set_sensitive(true);
167 		begin_selection_op_history ();
168 	}
169 }
170 
171 void
redo(uint32_t n)172 Editor::redo (uint32_t n)
173 {
174 	if (_session && _session->actively_recording()) {
175 		/* no redo allowed while recording. Session will check also,
176 		   but we don't even want to get to that.
177 		*/
178 		return;
179 	}
180 
181 	if (_drags->active ()) {
182 		_drags->abort ();
183 	}
184 	paste_count = 0;
185 
186 	if (_session) {
187 	_session->redo (n);
188 		if (_session->redo_depth() == 0) {
189 			redo_action->set_sensitive(false);
190 		}
191 		undo_action->set_sensitive(true);
192 		begin_selection_op_history ();
193 	}
194 }
195 
196 void
split_regions_at(MusicSample where,RegionSelection & regions)197 Editor::split_regions_at (MusicSample where, RegionSelection& regions)
198 {
199 	bool frozen = false;
200 
201 	list<boost::shared_ptr<Playlist> > used_playlists;
202 	list<RouteTimeAxisView*> used_trackviews;
203 
204 	if (regions.empty()) {
205 		return;
206 	}
207 
208 	begin_reversible_command (_("split"));
209 
210 
211 	if (regions.size() == 1) {
212 		/* TODO:  if splitting a single region, and snap-to is using
213 		 region boundaries, mabye we shouldn't pay attention to them? */
214 	} else {
215 		frozen = true;
216 		EditorFreeze(); /* Emit Signal */
217 	}
218 
219 	for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
220 
221 		RegionSelection::iterator tmp;
222 
223 		/* XXX this test needs to be more complicated, to make sure we really
224 		   have something to split.
225 		*/
226 
227 		if (!(*a)->region()->covers (where.sample)) {
228 			++a;
229 			continue;
230 		}
231 
232 		tmp = a;
233 		++tmp;
234 
235 		boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
236 
237 		if (!pl) {
238 			a = tmp;
239 			continue;
240 		}
241 
242 		if (!pl->frozen()) {
243 			/* we haven't seen this playlist before */
244 
245 			/* remember used playlists so we can thaw them later */
246 			used_playlists.push_back(pl);
247 
248 			TimeAxisView& tv = (*a)->get_time_axis_view();
249 			RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
250 			if (rtv) {
251 				used_trackviews.push_back (rtv);
252 			}
253 			pl->freeze();
254 		}
255 
256 
257 		if (pl) {
258 			pl->clear_changes ();
259 			pl->split_region ((*a)->region(), where);
260 			_session->add_command (new StatefulDiffCommand (pl));
261 		}
262 
263 		a = tmp;
264 	}
265 
266 	latest_regionviews.clear ();
267 
268 	vector<sigc::connection> region_added_connections;
269 
270 	for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
271 		region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
272 	}
273 
274 	while (used_playlists.size() > 0) {
275 		list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
276 		(*i)->thaw();
277 		used_playlists.pop_front();
278 	}
279 
280 	for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 		(*c).disconnect ();
282 	}
283 
284 	if (frozen){
285 		EditorThaw(); /* Emit Signal */
286 	}
287 
288 	if (_session->abort_empty_reversible_command ()) {
289 		/* no change was made */
290 		return;
291 	}
292 
293 	RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
294 
295 	//if the user has "Clear Selection" as their post-split behavior, then clear the selection
296 	if (!latest_regionviews.empty() && (rsas == None)) {
297 		selection->clear_objects();
298 		selection->clear_time();
299 		//but leave track selection intact
300 	}
301 
302 	//if the user doesn't want to preserve the "Existing" selection, then clear the selection
303 	if (!(rsas & Existing)) {
304 		selection->clear_objects();
305 		selection->clear_time();
306 	}
307 
308 	//if the user wants newly-created regions to be selected, then select them:
309 	if (mouse_mode == MouseObject) {
310 		for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
311 			if ((*ri)->region()->position() < where.sample) {
312 				// new regions created before the split
313 				if (rsas & NewlyCreatedLeft) {
314 					selection->add (*ri);
315 				}
316 			} else {
317 				// new regions created after the split
318 				if (rsas & NewlyCreatedRight) {
319 					selection->add (*ri);
320 				}
321 			}
322 		}
323 	}
324 
325 	commit_reversible_command ();
326 }
327 
328 /** Move one extreme of the current range selection.  If more than one range is selected,
329  *  the start of the earliest range or the end of the latest range is moved.
330  *
331  *  @param move_end true to move the end of the current range selection, false to move
332  *  the start.
333  *  @param next true to move the extreme to the next region boundary, false to move to
334  *  the previous.
335  */
336 void
move_range_selection_start_or_end_to_region_boundary(bool move_end,bool next)337 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
338 {
339 	if (selection->time.start() == selection->time.end_sample()) {
340 		return;
341 	}
342 
343 	samplepos_t start = selection->time.start ();
344 	samplepos_t end = selection->time.end_sample ();
345 
346 	/* the position of the thing we may move */
347 	samplepos_t pos = move_end ? end : start;
348 	int dir = next ? 1 : -1;
349 
350 	/* so we don't find the current region again */
351 	if (dir > 0 || pos > 0) {
352 		pos += dir;
353 	}
354 
355 	samplepos_t const target = get_region_boundary (pos, dir, true, false);
356 	if (target < 0) {
357 		return;
358 	}
359 
360 	if (move_end) {
361 		end = target;
362 	} else {
363 		start = target;
364 	}
365 
366 	if (end < start) {
367 		return;
368 	}
369 
370 	begin_reversible_selection_op (_("alter selection"));
371 	selection->set_preserving_all_ranges (start, end);
372 	commit_reversible_selection_op ();
373 }
374 
375 bool
nudge_forward_release(GdkEventButton * ev)376 Editor::nudge_forward_release (GdkEventButton* ev)
377 {
378 	if (ev->state & Keyboard::PrimaryModifier) {
379 		nudge_forward (false, true);
380 	} else {
381 		nudge_forward (false, false);
382 	}
383 	return false;
384 }
385 
386 bool
nudge_backward_release(GdkEventButton * ev)387 Editor::nudge_backward_release (GdkEventButton* ev)
388 {
389 	if (ev->state & Keyboard::PrimaryModifier) {
390 		nudge_backward (false, true);
391 	} else {
392 		nudge_backward (false, false);
393 	}
394 	return false;
395 }
396 
397 
398 void
nudge_forward(bool next,bool force_playhead)399 Editor::nudge_forward (bool next, bool force_playhead)
400 {
401 	samplepos_t distance;
402 	samplepos_t next_distance;
403 
404 	if (!_session) {
405 		return;
406 	}
407 
408 	RegionSelection rs = get_regions_from_selection_and_entered ();
409 
410 	if (!force_playhead && !rs.empty()) {
411 
412 		begin_reversible_command (_("nudge regions forward"));
413 
414 		for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
415 			boost::shared_ptr<Region> r ((*i)->region());
416 
417 			distance = get_nudge_distance (r->position(), next_distance);
418 
419 			if (next) {
420 				distance = next_distance;
421 			}
422 
423 			r->clear_changes ();
424 			r->set_position (r->position() + distance);
425 			_session->add_command (new StatefulDiffCommand (r));
426 		}
427 
428 		commit_reversible_command ();
429 
430 
431 	} else if (!force_playhead && !selection->markers.empty()) {
432 
433 		bool is_start;
434 		bool in_command = false;
435 		const int32_t divisions = get_grid_music_divisions (0);
436 
437 		for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
438 
439 			Location* loc = find_location_from_marker ((*i), is_start);
440 
441 			if (loc) {
442 
443 				XMLNode& before (loc->get_state());
444 
445 				if (is_start) {
446 					distance = get_nudge_distance (loc->start(), next_distance);
447 					if (next) {
448 						distance = next_distance;
449 					}
450 					if (max_samplepos - distance > loc->start() + loc->length()) {
451 						loc->set_start (loc->start() + distance, false, true, divisions);
452 					} else {
453 						loc->set_start (max_samplepos - loc->length(), false, true, divisions);
454 					}
455 				} else {
456 					distance = get_nudge_distance (loc->end(), next_distance);
457 					if (next) {
458 						distance = next_distance;
459 					}
460 					if (max_samplepos - distance > loc->end()) {
461 						loc->set_end (loc->end() + distance, false, true, divisions);
462 					} else {
463 						loc->set_end (max_samplepos, false, true, divisions);
464 					}
465 					if (loc->is_session_range()) {
466 						_session->set_session_range_is_free (false);
467 					}
468 				}
469 				if (!in_command) {
470 					begin_reversible_command (_("nudge location forward"));
471 					in_command = true;
472 				}
473 				XMLNode& after (loc->get_state());
474 				_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
475 			}
476 		}
477 
478 		if (in_command) {
479 			commit_reversible_command ();
480 		}
481 	} else {
482 		distance = get_nudge_distance (_playhead_cursor->current_sample (), next_distance);
483 		_session->request_locate (_playhead_cursor->current_sample () + distance);
484 	}
485 }
486 
487 void
nudge_backward(bool next,bool force_playhead)488 Editor::nudge_backward (bool next, bool force_playhead)
489 {
490 	samplepos_t distance;
491 	samplepos_t next_distance;
492 
493 	if (!_session) {
494 		return;
495 	}
496 
497 	RegionSelection rs = get_regions_from_selection_and_entered ();
498 
499 	if (!force_playhead && !rs.empty()) {
500 
501 		begin_reversible_command (_("nudge regions backward"));
502 
503 		for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
504 			boost::shared_ptr<Region> r ((*i)->region());
505 
506 			distance = get_nudge_distance (r->position(), next_distance);
507 
508 			if (next) {
509 				distance = next_distance;
510 			}
511 
512 			r->clear_changes ();
513 
514 			if (r->position() > distance) {
515 				r->set_position (r->position() - distance);
516 			} else {
517 				r->set_position (0);
518 			}
519 			_session->add_command (new StatefulDiffCommand (r));
520 		}
521 
522 		commit_reversible_command ();
523 
524 	} else if (!force_playhead && !selection->markers.empty()) {
525 
526 		bool is_start;
527 		bool in_command = false;
528 
529 		for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
530 
531 			Location* loc = find_location_from_marker ((*i), is_start);
532 
533 			if (loc) {
534 
535 				XMLNode& before (loc->get_state());
536 
537 				if (is_start) {
538 					distance = get_nudge_distance (loc->start(), next_distance);
539 					if (next) {
540 						distance = next_distance;
541 					}
542 					if (distance < loc->start()) {
543 						loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
544 					} else {
545 						loc->set_start (0, false, true, get_grid_music_divisions(0));
546 					}
547 				} else {
548 					distance = get_nudge_distance (loc->end(), next_distance);
549 
550 					if (next) {
551 						distance = next_distance;
552 					}
553 
554 					if (distance < loc->end() - loc->length()) {
555 						loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
556 					} else {
557 						loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
558 					}
559 					if (loc->is_session_range()) {
560 						_session->set_session_range_is_free (false);
561 					}
562 				}
563 				if (!in_command) {
564 					begin_reversible_command (_("nudge location forward"));
565 					in_command = true;
566 				}
567 				XMLNode& after (loc->get_state());
568 				_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
569 			}
570 		}
571 		if (in_command) {
572 			commit_reversible_command ();
573 		}
574 
575 	} else {
576 
577 		distance = get_nudge_distance (_playhead_cursor->current_sample (), next_distance);
578 
579 		if (_playhead_cursor->current_sample () > distance) {
580 			_session->request_locate (_playhead_cursor->current_sample () - distance);
581 		} else {
582 			_session->goto_start();
583 		}
584 	}
585 }
586 
587 void
nudge_forward_capture_offset()588 Editor::nudge_forward_capture_offset ()
589 {
590 	RegionSelection rs = get_regions_from_selection_and_entered ();
591 
592 	if (!_session || rs.empty()) {
593 		return;
594 	}
595 
596 	begin_reversible_command (_("nudge forward"));
597 
598 	samplepos_t const distance = _session->worst_output_latency();
599 
600 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
601 		boost::shared_ptr<Region> r ((*i)->region());
602 
603 		r->clear_changes ();
604 		r->set_position (r->position() + distance);
605 		_session->add_command(new StatefulDiffCommand (r));
606 	}
607 
608 	commit_reversible_command ();
609 }
610 
611 void
nudge_backward_capture_offset()612 Editor::nudge_backward_capture_offset ()
613 {
614 	RegionSelection rs = get_regions_from_selection_and_entered ();
615 
616 	if (!_session || rs.empty()) {
617 		return;
618 	}
619 
620 	begin_reversible_command (_("nudge backward"));
621 
622 	samplepos_t const distance = _session->worst_output_latency();
623 
624 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
625 		boost::shared_ptr<Region> r ((*i)->region());
626 
627 		r->clear_changes ();
628 
629 		if (r->position() > distance) {
630 			r->set_position (r->position() - distance);
631 		} else {
632 			r->set_position (0);
633 		}
634 		_session->add_command(new StatefulDiffCommand (r));
635 	}
636 
637 	commit_reversible_command ();
638 }
639 
640 struct RegionSelectionPositionSorter {
operator ()RegionSelectionPositionSorter641 	bool operator() (RegionView* a, RegionView* b) {
642 		return a->region()->position() < b->region()->position();
643 	}
644 };
645 
646 void
sequence_regions()647 Editor::sequence_regions ()
648 {
649 	samplepos_t r_end;
650 	samplepos_t r_end_prev;
651 
652 	int iCount=0;
653 
654 	if (!_session) {
655 		return;
656 	}
657 
658 	RegionSelection rs = get_regions_from_selection_and_entered ();
659 	rs.sort(RegionSelectionPositionSorter());
660 
661 	if (!rs.empty()) {
662 
663 		bool in_command = false;
664 
665 		for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
666 			boost::shared_ptr<Region> r ((*i)->region());
667 
668 			r->clear_changes();
669 
670 			if(r->locked())
671 			{
672 				continue;
673 			}
674 			if(r->position_locked())
675 			{
676 				continue;
677 			}
678 			if(iCount>0)
679 			{
680 				r_end_prev=r_end;
681 				r->set_position(r_end_prev);
682 			}
683 
684 			if (!in_command) {
685 				begin_reversible_command (_("sequence regions"));
686 				in_command = true;
687 			}
688 			_session->add_command (new StatefulDiffCommand (r));
689 
690 			r_end=r->position() + r->length();
691 
692 			iCount++;
693 		}
694 
695 		if (in_command) {
696 			commit_reversible_command ();
697 		}
698 	}
699 }
700 
701 
702 /* DISPLAY MOTION */
703 
704 void
move_to_start()705 Editor::move_to_start ()
706 {
707 	_session->goto_start ();
708 }
709 
710 void
move_to_end()711 Editor::move_to_end ()
712 {
713 
714 	_session->request_locate (_session->current_end_sample());
715 }
716 
717 void
build_region_boundary_cache()718 Editor::build_region_boundary_cache ()
719 {
720 
721 	/* TODO:  maybe set a timer so we don't recalutate when lots of changes are coming in */
722 	/* TODO:  maybe somehow defer this until session is fully loaded.  */
723 
724 	if (!_region_boundary_cache_dirty)
725 		return;
726 
727 	samplepos_t pos = 0;
728 	vector<RegionPoint> interesting_points;
729 	boost::shared_ptr<Region> r;
730 	TrackViewList tracks;
731 	bool at_end = false;
732 
733 	region_boundary_cache.clear ();
734 
735 	if (_session == 0) {
736 		return;
737 	}
738 
739 	bool maybe_first_sample = false;
740 
741 	if (UIConfiguration::instance().get_snap_to_region_start()) {
742 		interesting_points.push_back (Start);
743 		maybe_first_sample = true;
744 	}
745 
746 	if (UIConfiguration::instance().get_snap_to_region_end()) {
747 		interesting_points.push_back (End);
748 	}
749 
750 	if (UIConfiguration::instance().get_snap_to_region_sync()) {
751 		interesting_points.push_back (SyncPoint);
752 	}
753 
754 	/* if no snap selections are set, boundary cache should be left empty */
755 	if ( interesting_points.empty() ) {
756 		_region_boundary_cache_dirty = false;
757 		return;
758 	}
759 
760 	TimeAxisView *ontrack = 0;
761 	TrackViewList tlist;
762 
763 	tlist = track_views.filter_to_unique_playlists ();
764 
765 	if (maybe_first_sample) {
766 		TrackViewList::const_iterator i;
767 		for (i = tlist.begin(); i != tlist.end(); ++i) {
768 			boost::shared_ptr<Playlist> pl = (*i)->playlist();
769 			if (pl && pl->count_regions_at (0)) {
770 				region_boundary_cache.push_back (0);
771 				break;
772 			}
773 		}
774 	}
775 
776 	/* allow regions to snap to the video start (if any) as if it were a "region" */
777 	if (ARDOUR_UI::instance()->video_timeline) {
778 		ARDOUR::samplepos_t vo = ARDOUR_UI::instance()->video_timeline->get_video_start_offset();
779 		if (std::find (region_boundary_cache.begin(), region_boundary_cache.end(), vo) == region_boundary_cache.end()) {
780 			region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
781 		}
782 	}
783 
784 	std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
785 	samplepos_t session_end = ext.second;
786 
787 	while (pos < session_end && !at_end) {
788 
789 		samplepos_t rpos;
790 		samplepos_t lpos = session_end;
791 
792 		for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
793 
794 			if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
795 				if (*p == interesting_points.back()) {
796 					at_end = true;
797 				}
798 				/* move to next point type */
799 				continue;
800 			}
801 
802 			switch (*p) {
803 			case Start:
804 				rpos = r->first_sample();
805 				break;
806 
807 			case End:
808 				rpos = r->last_sample() + 1;
809 				break;
810 
811 			case SyncPoint:
812 				rpos = r->sync_position ();
813 				break;
814 
815 			default:
816 				break;
817 			}
818 
819 			if (rpos < lpos) {
820 				lpos = rpos;
821 			}
822 
823 			/* prevent duplicates, but we don't use set<> because we want to be able
824 			   to sort later.
825 			*/
826 
827 			vector<samplepos_t>::iterator ri;
828 
829 			for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
830 				if (*ri == rpos) {
831 					break;
832 				}
833 			}
834 
835 			if (ri == region_boundary_cache.end()) {
836 				region_boundary_cache.push_back (rpos);
837 			}
838 		}
839 
840 		pos = lpos + 1;
841 	}
842 
843 	/* finally sort to be sure that the order is correct */
844 
845 	sort (region_boundary_cache.begin(), region_boundary_cache.end());
846 
847 	_region_boundary_cache_dirty = false;
848 }
849 
850 boost::shared_ptr<Region>
find_next_region(samplepos_t sample,RegionPoint point,int32_t dir,TrackViewList & tracks,TimeAxisView ** ontrack)851 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
852 {
853 	TrackViewList::iterator i;
854 	samplepos_t closest = max_samplepos;
855 	boost::shared_ptr<Region> ret;
856 	samplepos_t rpos = 0;
857 
858 	samplepos_t track_sample;
859 
860 	for (i = tracks.begin(); i != tracks.end(); ++i) {
861 
862 		samplecnt_t distance;
863 		boost::shared_ptr<Region> r;
864 
865 		track_sample = sample;
866 
867 		if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
868 			continue;
869 		}
870 
871 		switch (point) {
872 		case Start:
873 			rpos = r->first_sample ();
874 			break;
875 
876 		case End:
877 			rpos = r->last_sample ();
878 			break;
879 
880 		case SyncPoint:
881 			rpos = r->sync_position ();
882 			break;
883 		}
884 
885 		if (rpos > sample) {
886 			distance = rpos - sample;
887 		} else {
888 			distance = sample - rpos;
889 		}
890 
891 		if (distance < closest) {
892 			closest = distance;
893 			if (ontrack != 0)
894 				*ontrack = (*i);
895 			ret = r;
896 		}
897 	}
898 
899 	return ret;
900 }
901 
902 samplepos_t
find_next_region_boundary(samplepos_t pos,int32_t dir,const TrackViewList & tracks)903 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
904 {
905 	samplecnt_t distance = max_samplepos;
906 	samplepos_t current_nearest = -1;
907 
908 	for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
909 		samplepos_t contender;
910 		samplecnt_t d;
911 
912 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
913 
914 		if (!rtv) {
915 			continue;
916 		}
917 
918 		if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
919 			continue;
920 		}
921 
922 		d = ::llabs (pos - contender);
923 
924 		if (d < distance) {
925 			current_nearest = contender;
926 			distance = d;
927 		}
928 	}
929 
930 	return current_nearest;
931 }
932 
933 samplepos_t
get_region_boundary(samplepos_t pos,int32_t dir,bool with_selection,bool only_onscreen)934 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
935 {
936 	samplepos_t target;
937 	TrackViewList tvl;
938 
939 	if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
940 
941 		if (!selection->tracks.empty()) {
942 
943 			target = find_next_region_boundary (pos, dir, selection->tracks);
944 
945 		} else {
946 
947 			if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
948 				get_onscreen_tracks (tvl);
949 				target = find_next_region_boundary (pos, dir, tvl);
950 			} else {
951 				target = find_next_region_boundary (pos, dir, track_views);
952 			}
953 		}
954 
955 	} else {
956 
957 		if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
958 			get_onscreen_tracks (tvl);
959 			target = find_next_region_boundary (pos, dir, tvl);
960 		} else {
961 			target = find_next_region_boundary (pos, dir, track_views);
962 		}
963 	}
964 
965 	return target;
966 }
967 
968 void
cursor_to_region_boundary(bool with_selection,int32_t dir)969 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
970 {
971 	samplepos_t pos = _playhead_cursor->current_sample ();
972 	samplepos_t target;
973 
974 	if (!_session) {
975 		return;
976 	}
977 
978 	// so we don't find the current region again..
979 	if (dir > 0 || pos > 0) {
980 		pos += dir;
981 	}
982 
983 	if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
984 		return;
985 	}
986 
987 	_session->request_locate (target);
988 }
989 
990 void
cursor_to_next_region_boundary(bool with_selection)991 Editor::cursor_to_next_region_boundary (bool with_selection)
992 {
993 	cursor_to_region_boundary (with_selection, 1);
994 }
995 
996 void
cursor_to_previous_region_boundary(bool with_selection)997 Editor::cursor_to_previous_region_boundary (bool with_selection)
998 {
999 	cursor_to_region_boundary (with_selection, -1);
1000 }
1001 
1002 void
cursor_to_region_point(EditorCursor * cursor,RegionPoint point,int32_t dir)1003 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
1004 {
1005 	boost::shared_ptr<Region> r;
1006 	samplepos_t pos = cursor->current_sample ();
1007 
1008 	if (!_session) {
1009 		return;
1010 	}
1011 
1012 	TimeAxisView *ontrack = 0;
1013 
1014 	// so we don't find the current region again..
1015 	if (dir>0 || pos>0)
1016 		pos+=dir;
1017 
1018 	if (!selection->tracks.empty()) {
1019 
1020 		r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1021 
1022 	} else if (clicked_axisview) {
1023 
1024 		TrackViewList t;
1025 		t.push_back (clicked_axisview);
1026 
1027 		r = find_next_region (pos, point, dir, t, &ontrack);
1028 
1029 	} else {
1030 
1031 		r = find_next_region (pos, point, dir, track_views, &ontrack);
1032 	}
1033 
1034 	if (r == 0) {
1035 		return;
1036 	}
1037 
1038 	switch (point) {
1039 	case Start:
1040 		pos = r->first_sample ();
1041 		break;
1042 
1043 	case End:
1044 		pos = r->last_sample ();
1045 		break;
1046 
1047 	case SyncPoint:
1048 		pos = r->sync_position ();
1049 		break;
1050 	}
1051 
1052 	if (cursor == _playhead_cursor) {
1053 		_session->request_locate (pos);
1054 	} else {
1055 		cursor->set_position (pos);
1056 	}
1057 }
1058 
1059 void
cursor_to_next_region_point(EditorCursor * cursor,RegionPoint point)1060 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1061 {
1062 	cursor_to_region_point (cursor, point, 1);
1063 }
1064 
1065 void
cursor_to_previous_region_point(EditorCursor * cursor,RegionPoint point)1066 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1067 {
1068 	cursor_to_region_point (cursor, point, -1);
1069 }
1070 
1071 void
cursor_to_selection_start(EditorCursor * cursor)1072 Editor::cursor_to_selection_start (EditorCursor *cursor)
1073 {
1074 	samplepos_t pos = 0;
1075 
1076 	switch (mouse_mode) {
1077 	case MouseObject:
1078 		if (!selection->regions.empty()) {
1079 			pos = selection->regions.start();
1080 		}
1081 		break;
1082 
1083 	case MouseRange:
1084 		if (!selection->time.empty()) {
1085 			pos = selection->time.start ();
1086 		}
1087 		break;
1088 
1089 	default:
1090 		return;
1091 	}
1092 
1093 	if (cursor == _playhead_cursor) {
1094 		_session->request_locate (pos);
1095 	} else {
1096 		cursor->set_position (pos);
1097 	}
1098 }
1099 
1100 void
cursor_to_selection_end(EditorCursor * cursor)1101 Editor::cursor_to_selection_end (EditorCursor *cursor)
1102 {
1103 	samplepos_t pos = 0;
1104 
1105 	switch (mouse_mode) {
1106 	case MouseObject:
1107 		if (!selection->regions.empty()) {
1108 			pos = selection->regions.end_sample();
1109 		}
1110 		break;
1111 
1112 	case MouseRange:
1113 		if (!selection->time.empty()) {
1114 			pos = selection->time.end_sample ();
1115 		}
1116 		break;
1117 
1118 	default:
1119 		return;
1120 	}
1121 
1122 	if (cursor == _playhead_cursor) {
1123 		_session->request_locate (pos);
1124 	} else {
1125 		cursor->set_position (pos);
1126 	}
1127 }
1128 
1129 void
selected_marker_to_region_boundary(bool with_selection,int32_t dir)1130 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1131 {
1132 	samplepos_t target;
1133 	Location* loc;
1134 	bool ignored;
1135 
1136 	if (!_session) {
1137 		return;
1138 	}
1139 
1140 	if (selection->markers.empty()) {
1141 		samplepos_t mouse;
1142 		bool ignored;
1143 
1144 		if (!mouse_sample (mouse, ignored)) {
1145 			return;
1146 		}
1147 
1148 		add_location_mark (mouse);
1149 	}
1150 
1151 	if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1152 		return;
1153 	}
1154 
1155 	samplepos_t pos = loc->start();
1156 
1157 	// so we don't find the current region again..
1158 	if (dir > 0 || pos > 0) {
1159 		pos += dir;
1160 	}
1161 
1162 	if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1163 		return;
1164 	}
1165 
1166 	loc->move_to (target, 0);
1167 }
1168 
1169 void
selected_marker_to_next_region_boundary(bool with_selection)1170 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1171 {
1172 	selected_marker_to_region_boundary (with_selection, 1);
1173 }
1174 
1175 void
selected_marker_to_previous_region_boundary(bool with_selection)1176 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1177 {
1178 	selected_marker_to_region_boundary (with_selection, -1);
1179 }
1180 
1181 void
selected_marker_to_region_point(RegionPoint point,int32_t dir)1182 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1183 {
1184 	boost::shared_ptr<Region> r;
1185 	samplepos_t pos;
1186 	Location* loc;
1187 	bool ignored;
1188 
1189 	if (!_session || selection->markers.empty()) {
1190 		return;
1191 	}
1192 
1193 	if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1194 		return;
1195 	}
1196 
1197 	TimeAxisView *ontrack = 0;
1198 
1199 	pos = loc->start();
1200 
1201 	// so we don't find the current region again..
1202 	if (dir>0 || pos>0)
1203 		pos+=dir;
1204 
1205 	if (!selection->tracks.empty()) {
1206 
1207 		r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1208 
1209 	} else {
1210 
1211 		r = find_next_region (pos, point, dir, track_views, &ontrack);
1212 	}
1213 
1214 	if (r == 0) {
1215 		return;
1216 	}
1217 
1218 	switch (point) {
1219 	case Start:
1220 		pos = r->first_sample ();
1221 		break;
1222 
1223 	case End:
1224 		pos = r->last_sample ();
1225 		break;
1226 
1227 	case SyncPoint:
1228 		pos = r->adjust_to_sync (r->first_sample());
1229 		break;
1230 	}
1231 
1232 	loc->move_to (pos, 0);
1233 }
1234 
1235 void
selected_marker_to_next_region_point(RegionPoint point)1236 Editor::selected_marker_to_next_region_point (RegionPoint point)
1237 {
1238 	selected_marker_to_region_point (point, 1);
1239 }
1240 
1241 void
selected_marker_to_previous_region_point(RegionPoint point)1242 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1243 {
1244 	selected_marker_to_region_point (point, -1);
1245 }
1246 
1247 void
selected_marker_to_selection_start()1248 Editor::selected_marker_to_selection_start ()
1249 {
1250 	samplepos_t pos = 0;
1251 	Location* loc;
1252 	bool ignored;
1253 
1254 	if (!_session || selection->markers.empty()) {
1255 		return;
1256 	}
1257 
1258 	if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1259 		return;
1260 	}
1261 
1262 	switch (mouse_mode) {
1263 	case MouseObject:
1264 		if (!selection->regions.empty()) {
1265 			pos = selection->regions.start();
1266 		}
1267 		break;
1268 
1269 	case MouseRange:
1270 		if (!selection->time.empty()) {
1271 			pos = selection->time.start ();
1272 		}
1273 		break;
1274 
1275 	default:
1276 		return;
1277 	}
1278 
1279 	loc->move_to (pos, 0);
1280 }
1281 
1282 void
selected_marker_to_selection_end()1283 Editor::selected_marker_to_selection_end ()
1284 {
1285 	samplepos_t pos = 0;
1286 	Location* loc;
1287 	bool ignored;
1288 
1289 	if (!_session || selection->markers.empty()) {
1290 		return;
1291 	}
1292 
1293 	if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1294 		return;
1295 	}
1296 
1297 	switch (mouse_mode) {
1298 	case MouseObject:
1299 		if (!selection->regions.empty()) {
1300 			pos = selection->regions.end_sample();
1301 		}
1302 		break;
1303 
1304 	case MouseRange:
1305 		if (!selection->time.empty()) {
1306 			pos = selection->time.end_sample ();
1307 		}
1308 		break;
1309 
1310 	default:
1311 		return;
1312 	}
1313 
1314 	loc->move_to (pos, 0);
1315 }
1316 
1317 void
scroll_playhead(bool forward)1318 Editor::scroll_playhead (bool forward)
1319 {
1320 	samplepos_t pos = _playhead_cursor->current_sample ();
1321 	samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1322 
1323 	if (forward) {
1324 		if (pos == max_samplepos) {
1325 			return;
1326 		}
1327 
1328 		if (pos < max_samplepos - delta) {
1329 			pos += delta ;
1330 		} else {
1331 			pos = max_samplepos;
1332 		}
1333 
1334 	} else {
1335 
1336 		if (pos == 0) {
1337 			return;
1338 		}
1339 
1340 		if (pos > delta) {
1341 			pos -= delta;
1342 		} else {
1343 			pos = 0;
1344 		}
1345 	}
1346 
1347 	_session->request_locate (pos);
1348 }
1349 
1350 void
cursor_align(bool playhead_to_edit)1351 Editor::cursor_align (bool playhead_to_edit)
1352 {
1353 	if (!_session) {
1354 		return;
1355 	}
1356 
1357 	if (playhead_to_edit) {
1358 
1359 		if (selection->markers.empty()) {
1360 			return;
1361 		}
1362 
1363 		_session->request_locate (selection->markers.front()->position());
1364 
1365 	} else {
1366 		const int32_t divisions = get_grid_music_divisions (0);
1367 		/* move selected markers to playhead */
1368 
1369 		for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1370 			bool ignored;
1371 
1372 			Location* loc = find_location_from_marker (*i, ignored);
1373 
1374 			if (loc->is_mark()) {
1375 				loc->set_start (_playhead_cursor->current_sample (), false, true, divisions);
1376 			} else {
1377 				loc->set (_playhead_cursor->current_sample (),
1378 					  _playhead_cursor->current_sample () + loc->length(), true, divisions);
1379 			}
1380 		}
1381 	}
1382 }
1383 
1384 void
scroll_backward(float pages)1385 Editor::scroll_backward (float pages)
1386 {
1387 	samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1388 	samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1389 
1390 	samplepos_t sample;
1391 	if (_leftmost_sample < cnt) {
1392 		sample = 0;
1393 	} else {
1394 		sample = _leftmost_sample - cnt;
1395 	}
1396 
1397 	reset_x_origin (sample);
1398 }
1399 
1400 void
scroll_forward(float pages)1401 Editor::scroll_forward (float pages)
1402 {
1403 	samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1404 	samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1405 
1406 	samplepos_t sample;
1407 	if (max_samplepos - cnt < _leftmost_sample) {
1408 		sample = max_samplepos - cnt;
1409 	} else {
1410 		sample = _leftmost_sample + cnt;
1411 	}
1412 
1413 	reset_x_origin (sample);
1414 }
1415 
1416 void
scroll_tracks_down()1417 Editor::scroll_tracks_down ()
1418 {
1419 	double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1420 	if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1421 		vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1422 	}
1423 
1424 	vertical_adjustment.set_value (vert_value);
1425 }
1426 
1427 void
scroll_tracks_up()1428 Editor::scroll_tracks_up ()
1429 {
1430 	vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1431 }
1432 
1433 void
scroll_tracks_down_line()1434 Editor::scroll_tracks_down_line ()
1435 {
1436 	double vert_value = vertical_adjustment.get_value() + 60;
1437 
1438 	if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1439 		vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1440 	}
1441 
1442 	vertical_adjustment.set_value (vert_value);
1443 }
1444 
1445 void
scroll_tracks_up_line()1446 Editor::scroll_tracks_up_line ()
1447 {
1448 	reset_y_origin (vertical_adjustment.get_value() - 60);
1449 }
1450 
1451 void
select_topmost_track()1452 Editor::select_topmost_track ()
1453 {
1454 	const double top_of_trackviews = vertical_adjustment.get_value();
1455 	for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1456 		if ((*t)->hidden()) {
1457 			continue;
1458 		}
1459 		std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1460 		if (res.first) {
1461 			selection->set (*t);
1462 			break;
1463 		}
1464 	}
1465 }
1466 
1467 bool
scroll_down_one_track(bool skip_child_views)1468 Editor::scroll_down_one_track (bool skip_child_views)
1469 {
1470 	TrackViewList::reverse_iterator next = track_views.rend();
1471 	const double top_of_trackviews = vertical_adjustment.get_value();
1472 
1473 	for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1474 		if ((*t)->hidden()) {
1475 			continue;
1476 		}
1477 
1478 		/* If this is the upper-most visible trackview, we want to display
1479 		 * the one above it (next)
1480 		 *
1481 		 * Note that covers_y_position() is recursive and includes child views
1482 		 */
1483 		std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1484 
1485 		if (res.first) {
1486 			if (skip_child_views) {
1487 				break;
1488 			}
1489 			/* automation lane (one level, non-recursive)
1490 			 *
1491 			 * - if no automation lane exists -> move to next tack
1492 			 * - if the first (here: bottom-most) matches -> move to next tack
1493 			 * - if no y-axis match is found -> the current track is at the top
1494 			 *     -> move to last (here: top-most) automation lane
1495 			 */
1496 			TimeAxisView::Children kids = (*t)->get_child_list();
1497 			TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1498 
1499 			for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1500 				if ((*ci)->hidden()) {
1501 					continue;
1502 				}
1503 
1504 				std::pair<TimeAxisView*,double> dev;
1505 				dev = (*ci)->covers_y_position (top_of_trackviews);
1506 				if (dev.first) {
1507 					/* some automation lane is currently at the top */
1508 					if (ci == kids.rbegin()) {
1509 						/* first (bottom-most) autmation lane is at the top.
1510 						 * -> move to next track
1511 						 */
1512 						nkid = kids.rend();
1513 					}
1514 					break;
1515 				}
1516 				nkid = ci;
1517 			}
1518 
1519 			if (nkid != kids.rend()) {
1520 				ensure_time_axis_view_is_visible (**nkid, true);
1521 				return true;
1522 			}
1523 			break;
1524 		}
1525 		next = t;
1526 	}
1527 
1528 	/* move to the track below the first one that covers the */
1529 
1530 	if (next != track_views.rend()) {
1531 		ensure_time_axis_view_is_visible (**next, true);
1532 		return true;
1533 	}
1534 
1535 	return false;
1536 }
1537 
1538 bool
scroll_up_one_track(bool skip_child_views)1539 Editor::scroll_up_one_track (bool skip_child_views)
1540 {
1541 	TrackViewList::iterator prev = track_views.end();
1542 	double top_of_trackviews = vertical_adjustment.get_value ();
1543 
1544 	for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1545 
1546 		if ((*t)->hidden()) {
1547 			continue;
1548 		}
1549 
1550 		/* find the trackview at the top of the trackview group
1551 		 *
1552 		 * Note that covers_y_position() is recursive and includes child views
1553 		 */
1554 		std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1555 
1556 		if (res.first) {
1557 			if (skip_child_views) {
1558 				break;
1559 			}
1560 			/* automation lane (one level, non-recursive)
1561 			 *
1562 			 * - if no automation lane exists -> move to prev tack
1563 			 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1564 			 *     (actually last automation lane of previous track, see below)
1565 			 * - if first (top-most) lane is at the top -> move to this track
1566 			 * - else move up one lane
1567 			 */
1568 			TimeAxisView::Children kids = (*t)->get_child_list();
1569 			TimeAxisView::Children::iterator pkid = kids.end();
1570 
1571 			for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1572 				if ((*ci)->hidden()) {
1573 					continue;
1574 				}
1575 
1576 				std::pair<TimeAxisView*,double> dev;
1577 				dev = (*ci)->covers_y_position (top_of_trackviews);
1578 				if (dev.first) {
1579 					/* some automation lane is currently at the top */
1580 					if (ci == kids.begin()) {
1581 						/* first (top-most) autmation lane is at the top.
1582 						 * jump directly to this track's top
1583 						 */
1584 						ensure_time_axis_view_is_visible (**t, true);
1585 						return true;
1586 					}
1587 					else if (pkid != kids.end()) {
1588 						/* some other automation lane is at the top.
1589 						 * move up to prev automation lane.
1590 						 */
1591 						ensure_time_axis_view_is_visible (**pkid, true);
1592 						return true;
1593 					}
1594 					assert(0); // not reached
1595 					break;
1596 				}
1597 				pkid = ci;
1598 			}
1599 			break;
1600 		}
1601 
1602 		prev = t;
1603 	}
1604 
1605 	if (prev != track_views.end()) {
1606 		// move to bottom-most automation-lane of the previous track
1607 		TimeAxisView::Children kids = (*prev)->get_child_list();
1608 		TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1609 		if (!skip_child_views) {
1610 			// find the last visible lane
1611 			for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1612 				if (!(*ci)->hidden()) {
1613 					pkid = ci;
1614 					break;
1615 				}
1616 			}
1617 		}
1618 		if (pkid != kids.rend()) {
1619 			ensure_time_axis_view_is_visible (**pkid, true);
1620 		} else  {
1621 			ensure_time_axis_view_is_visible (**prev, true);
1622 		}
1623 		return true;
1624 	}
1625 
1626 	return false;
1627 }
1628 
1629 void
scroll_left_step()1630 Editor::scroll_left_step ()
1631 {
1632 	samplepos_t xdelta = (current_page_samples() / 8);
1633 
1634 	if (_leftmost_sample > xdelta) {
1635 		reset_x_origin (_leftmost_sample - xdelta);
1636 	} else {
1637 		reset_x_origin (0);
1638 	}
1639 }
1640 
1641 
1642 void
scroll_right_step()1643 Editor::scroll_right_step ()
1644 {
1645 	samplepos_t xdelta = (current_page_samples() / 8);
1646 
1647 	if (max_samplepos - xdelta > _leftmost_sample) {
1648 		reset_x_origin (_leftmost_sample + xdelta);
1649 	} else {
1650 		reset_x_origin (max_samplepos - current_page_samples());
1651 	}
1652 }
1653 
1654 void
scroll_left_half_page()1655 Editor::scroll_left_half_page ()
1656 {
1657 	samplepos_t xdelta = (current_page_samples() / 2);
1658 	if (_leftmost_sample > xdelta) {
1659 		reset_x_origin (_leftmost_sample - xdelta);
1660 	} else {
1661 		reset_x_origin (0);
1662 	}
1663 }
1664 
1665 void
scroll_right_half_page()1666 Editor::scroll_right_half_page ()
1667 {
1668 	samplepos_t xdelta = (current_page_samples() / 2);
1669 	if (max_samplepos - xdelta > _leftmost_sample) {
1670 		reset_x_origin (_leftmost_sample + xdelta);
1671 	} else {
1672 		reset_x_origin (max_samplepos - current_page_samples());
1673 	}
1674 }
1675 
1676 /* ZOOM */
1677 
1678 void
tav_zoom_step(bool coarser)1679 Editor::tav_zoom_step (bool coarser)
1680 {
1681 	DisplaySuspender ds;
1682 
1683 	TrackViewList* ts;
1684 
1685 	if (selection->tracks.empty()) {
1686 		ts = &track_views;
1687 	} else {
1688 		ts = &selection->tracks;
1689 	}
1690 
1691 	for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1692 		TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1693 			tv->step_height (coarser);
1694 	}
1695 }
1696 
1697 void
tav_zoom_smooth(bool coarser,bool force_all)1698 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1699 {
1700 	DisplaySuspender ds;
1701 
1702 	TrackViewList* ts;
1703 
1704 	if (selection->tracks.empty() || force_all) {
1705 		ts = &track_views;
1706 	} else {
1707 		ts = &selection->tracks;
1708 	}
1709 
1710 	for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1711 		TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1712 		uint32_t h = tv->current_height ();
1713 
1714 		if (coarser) {
1715 			if (h > 5) {
1716 				h -= 5; // pixels
1717 				if (h >= TimeAxisView::preset_height (HeightSmall)) {
1718 					tv->set_height (h);
1719 				}
1720 			}
1721 		} else {
1722 			tv->set_height (h + 5);
1723 		}
1724 	}
1725 }
1726 
1727 void
temporal_zoom_step_mouse_focus_scale(bool zoom_out,double scale)1728 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1729 {
1730 	PBD::Unwinder<Editing::ZoomFocus> zf (zoom_focus, Editing::ZoomFocusMouse);
1731 	temporal_zoom_step_scale (zoom_out, scale);
1732 }
1733 
1734 void
temporal_zoom_step_mouse_focus(bool zoom_out)1735 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1736 {
1737 	temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1738 }
1739 
1740 void
temporal_zoom_step(bool zoom_out)1741 Editor::temporal_zoom_step (bool zoom_out)
1742 {
1743 	temporal_zoom_step_scale (zoom_out, 2.0);
1744 }
1745 
1746 void
temporal_zoom_step_scale(bool zoom_out,double scale)1747 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1748 {
1749 	ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1750 
1751 	samplecnt_t nspp = samples_per_pixel;
1752 
1753 	if (zoom_out) {
1754 		nspp *= scale;
1755 		if (nspp == samples_per_pixel) {
1756 			nspp *= 2.0;
1757 		}
1758 	} else {
1759 		nspp /= scale;
1760 		if (nspp == samples_per_pixel) {
1761 			nspp /= 2.0;
1762 		}
1763 	}
1764 
1765 	//zoom-behavior-tweaks
1766 	//limit our maximum zoom to the session gui extents value
1767 	std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1768 	samplecnt_t session_extents_pp = (ext.second - ext.first)  / _visible_canvas_width;
1769 	if (nspp > session_extents_pp)
1770 		nspp = session_extents_pp;
1771 
1772 	temporal_zoom (nspp);
1773 }
1774 
1775 void
temporal_zoom(samplecnt_t spp)1776 Editor::temporal_zoom (samplecnt_t spp)
1777 {
1778 	if (!_session) {
1779 		return;
1780 	}
1781 
1782 	samplepos_t current_page = current_page_samples();
1783 	samplepos_t current_leftmost = _leftmost_sample;
1784 	samplepos_t current_rightmost;
1785 	samplepos_t current_center;
1786 	samplepos_t new_page_size;
1787 	samplepos_t half_page_size;
1788 	samplepos_t leftmost_after_zoom = 0;
1789 	samplepos_t where;
1790 	bool in_track_canvas;
1791 	bool use_mouse_sample = true;
1792 	samplecnt_t nspp;
1793 	double l;
1794 
1795 	if (spp == samples_per_pixel) {
1796 		return;
1797 	}
1798 
1799 	// Imposing an arbitrary limit to zoom out as too much zoom out produces
1800 	// segfaults for lack of memory. If somebody decides this is not high enough I
1801 	// believe it can be raisen to higher values but some limit must be in place.
1802 	//
1803 	// This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1804 	// all of which is used for the editor track displays. The whole day
1805 	// would be 4147200000 samples, so 2592000 samples per pixel.
1806 
1807 	nspp = min (spp, (samplecnt_t) 2592000);
1808 	nspp = max ((samplecnt_t) 1, nspp);
1809 
1810 	new_page_size = (samplepos_t) floor (_visible_canvas_width * nspp);
1811 	half_page_size = new_page_size / 2;
1812 
1813 	Editing::ZoomFocus zf = zoom_focus;
1814 
1815 	if (zf == ZoomFocusEdit && _edit_point == EditAtMouse) {
1816 		zf = ZoomFocusMouse;
1817 	}
1818 
1819 	switch (zf) {
1820 	case ZoomFocusLeft:
1821 		leftmost_after_zoom = current_leftmost;
1822 		break;
1823 
1824 	case ZoomFocusRight:
1825 		current_rightmost = _leftmost_sample + current_page;
1826 		if (current_rightmost < new_page_size) {
1827 			leftmost_after_zoom = 0;
1828 		} else {
1829 			leftmost_after_zoom = current_rightmost - new_page_size;
1830 		}
1831 		break;
1832 
1833 	case ZoomFocusCenter:
1834 		current_center = current_leftmost + (current_page/2);
1835 		if (current_center < half_page_size) {
1836 			leftmost_after_zoom = 0;
1837 		} else {
1838 			leftmost_after_zoom = current_center - half_page_size;
1839 		}
1840 		break;
1841 
1842 	case ZoomFocusPlayhead:
1843 		/* centre playhead */
1844 		l = _playhead_cursor->current_sample () - (new_page_size * 0.5);
1845 
1846 		if (l < 0) {
1847 			leftmost_after_zoom = 0;
1848 		} else if (l > max_samplepos) {
1849 			leftmost_after_zoom = max_samplepos - new_page_size;
1850 		} else {
1851 			leftmost_after_zoom = (samplepos_t) l;
1852 		}
1853 		break;
1854 
1855 	case ZoomFocusMouse:
1856 		/* try to keep the mouse over the same point in the display */
1857 
1858 		if (_drags->active()) {
1859 			where = _drags->current_pointer_sample ();
1860 		} else if (!mouse_sample (where, in_track_canvas)) {
1861 			use_mouse_sample = false;
1862 		}
1863 
1864 		if (use_mouse_sample) {
1865 			l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1866 
1867 			if (l < 0) {
1868 				leftmost_after_zoom = 0;
1869 			} else if (l > max_samplepos) {
1870 				leftmost_after_zoom = max_samplepos - new_page_size;
1871 			} else {
1872 				leftmost_after_zoom = (samplepos_t) l;
1873 			}
1874 		} else {
1875 			/* use playhead instead */
1876 			where = _playhead_cursor->current_sample ();
1877 
1878 			if (where < half_page_size) {
1879 				leftmost_after_zoom = 0;
1880 			} else {
1881 				leftmost_after_zoom = where - half_page_size;
1882 			}
1883 		}
1884 		break;
1885 
1886 	case ZoomFocusEdit:
1887 		/* try to keep the edit point in the same place */
1888 		where = get_preferred_edit_position ();
1889 		{
1890 			double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1891 
1892 			if (l < 0) {
1893 				leftmost_after_zoom = 0;
1894 			} else if (l > max_samplepos) {
1895 				leftmost_after_zoom = max_samplepos - new_page_size;
1896 			} else {
1897 				leftmost_after_zoom = (samplepos_t) l;
1898 			}
1899 		}
1900 		break;
1901 
1902 	}
1903 
1904 	// leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1905 
1906 	reposition_and_zoom (leftmost_after_zoom, nspp);
1907 }
1908 
1909 void
calc_extra_zoom_edges(samplepos_t & start,samplepos_t & end)1910 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1911 {
1912 	/* this func helps make sure we leave a little space
1913 	   at each end of the editor so that the zoom doesn't fit the region
1914 	   precisely to the screen.
1915 	*/
1916 
1917 	GdkScreen* screen = gdk_screen_get_default ();
1918 	const gint pixwidth = gdk_screen_get_width (screen);
1919 	const gint mmwidth = gdk_screen_get_width_mm (screen);
1920 	const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1921 	const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1922 
1923 	const samplepos_t range = end - start;
1924 	const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1925 	const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1926 
1927 	if (start > extra_samples) {
1928 		start -= extra_samples;
1929 	} else {
1930 		start = 0;
1931 	}
1932 
1933 	if (max_samplepos - extra_samples > end) {
1934 		end += extra_samples;
1935 	} else {
1936 		end = max_samplepos;
1937 	}
1938 }
1939 
1940 bool
get_selection_extents(samplepos_t & start,samplepos_t & end) const1941 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1942 {
1943 	start = max_samplepos;
1944 	end = 0;
1945 	bool ret = true;
1946 
1947 	//ToDo:  if notes are selected, set extents to that selection
1948 
1949 	//ToDo:  if control points are selected, set extents to that selection
1950 
1951 	if (!selection->regions.empty()) {
1952 		RegionSelection rs = get_regions_from_selection_and_entered ();
1953 
1954 		for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1955 
1956 			if ((*i)->region()->position() < start) {
1957 				start = (*i)->region()->position();
1958 			}
1959 
1960 			if ((*i)->region()->last_sample() + 1 > end) {
1961 				end = (*i)->region()->last_sample() + 1;
1962 			}
1963 		}
1964 
1965 	} else if (!selection->time.empty()) {
1966 		start = selection->time.start();
1967 		end = selection->time.end_sample();
1968 	} else
1969 		ret = false;  //no selection found
1970 
1971 	//range check
1972 	if ((start == 0 && end == 0) || end < start) {
1973 		ret = false;
1974 	}
1975 
1976 	return ret;
1977 }
1978 
1979 
1980 void
temporal_zoom_selection(Editing::ZoomAxis axes)1981 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1982 {
1983 	if (!selection) return;
1984 
1985 	if (selection->regions.empty() && selection->time.empty()) {
1986 		if (axes == Horizontal || axes == Both) {
1987 			temporal_zoom_step(true);
1988 		}
1989 		if (axes == Vertical || axes == Both) {
1990 			if (!track_views.empty()) {
1991 
1992 				TrackViewList tvl;
1993 
1994 				//implicit hack: by extending the top & bottom check outside the current view limits, we include the trackviews immediately above & below what is visible
1995 				const double top = vertical_adjustment.get_value() - 10;
1996 				const double btm = top + _visible_canvas_height + 10;
1997 
1998 				for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1999 					if ((*iter)->covered_by_y_range (top, btm)) {
2000 						tvl.push_back(*iter);
2001 					}
2002 				}
2003 
2004 				fit_tracks (tvl);
2005 			}
2006 		}
2007 		return;
2008 	}
2009 
2010 	//ToDo:  if notes are selected, zoom to that
2011 
2012 	//ToDo:  if control points are selected, zoom to that
2013 
2014 	if (axes == Horizontal || axes == Both) {
2015 
2016 		samplepos_t start, end;
2017 		if (get_selection_extents (start, end)) {
2018 			calc_extra_zoom_edges (start, end);
2019 			temporal_zoom_by_sample (start, end);
2020 		}
2021 	}
2022 
2023 	if (axes == Vertical || axes == Both) {
2024 		fit_selection ();
2025 	}
2026 
2027 	//normally, we don't do anything "automatic" to the user's selection.
2028 	//but in this case, we will clear the selection after a zoom-to-selection.
2029 	selection->clear();
2030 }
2031 
2032 void
temporal_zoom_session()2033 Editor::temporal_zoom_session ()
2034 {
2035 	ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2036 
2037 	if (_session) {
2038 		samplecnt_t start = _session->current_start_sample();
2039 		samplecnt_t end = _session->current_end_sample();
2040 
2041 		if (_session->actively_recording ()) {
2042 			samplepos_t cur = _playhead_cursor->current_sample ();
2043 			if (cur > end) {
2044 				/* recording beyond the end marker; zoom out
2045 				 * by 5 seconds more so that if 'follow
2046 				 * playhead' is active we don't immediately
2047 				 * scroll.
2048 				 */
2049 				end = cur + _session->sample_rate() * 5;
2050 			}
2051 		}
2052 
2053 		if ((start == 0 && end == 0) || end < start) {
2054 			return;
2055 		}
2056 
2057 		calc_extra_zoom_edges(start, end);
2058 
2059 		temporal_zoom_by_sample (start, end);
2060 	}
2061 }
2062 
2063 void
temporal_zoom_extents()2064 Editor::temporal_zoom_extents ()
2065 {
2066 	ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2067 
2068 	if (_session) {
2069 		std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);  //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2070 
2071 		samplecnt_t start = ext.first;
2072 		samplecnt_t end = ext.second;
2073 
2074 		if (_session->actively_recording ()) {
2075 			samplepos_t cur = _playhead_cursor->current_sample ();
2076 			if (cur > end) {
2077 				/* recording beyond the end marker; zoom out
2078 				 * by 5 seconds more so that if 'follow
2079 				 * playhead' is active we don't immediately
2080 				 * scroll.
2081 				 */
2082 				end = cur + _session->sample_rate() * 5;
2083 			}
2084 		}
2085 
2086 		if ((start == 0 && end == 0) || end < start) {
2087 			return;
2088 		}
2089 
2090 		calc_extra_zoom_edges(start, end);
2091 
2092 		temporal_zoom_by_sample (start, end);
2093 	}
2094 }
2095 
2096 void
temporal_zoom_by_sample(samplepos_t start,samplepos_t end)2097 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2098 {
2099 	if (!_session) return;
2100 
2101 	if ((start == 0 && end == 0) || end < start) {
2102 		return;
2103 	}
2104 
2105 	samplepos_t range = end - start;
2106 
2107 	const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2108 
2109 	samplepos_t new_page = range;
2110 	samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2111 	samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2112 
2113 	if (new_leftmost > middle) {
2114 		new_leftmost = 0;
2115 	}
2116 
2117 	if (new_leftmost < 0) {
2118 		new_leftmost = 0;
2119 	}
2120 
2121 	reposition_and_zoom (new_leftmost, new_fpp);
2122 }
2123 
2124 void
temporal_zoom_to_sample(bool coarser,samplepos_t sample)2125 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2126 {
2127 	if (!_session) {
2128 		return;
2129 	}
2130 
2131 	samplecnt_t range_before = sample - _leftmost_sample;
2132 	samplecnt_t new_spp;
2133 
2134 	if (coarser) {
2135 		if (samples_per_pixel <= 1) {
2136 			new_spp = 2;
2137 		} else {
2138 			new_spp = samples_per_pixel + (samples_per_pixel/2);
2139 		}
2140 		range_before += range_before/2;
2141 	} else {
2142 		if (samples_per_pixel >= 1) {
2143 			new_spp = samples_per_pixel - (samples_per_pixel/2);
2144 		} else {
2145 			/* could bail out here since we cannot zoom any finer,
2146 			   but leave that to the equality test below
2147 			*/
2148 			new_spp = samples_per_pixel;
2149 		}
2150 
2151 		range_before -= range_before/2;
2152 	}
2153 
2154 	if (new_spp == samples_per_pixel)  {
2155 		return;
2156 	}
2157 
2158 	/* zoom focus is automatically taken as @param sample when this
2159 	   method is used.
2160 	*/
2161 
2162 	samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2163 
2164 	if (new_leftmost > sample) {
2165 		new_leftmost = 0;
2166 	}
2167 
2168 	if (new_leftmost < 0) {
2169 		new_leftmost = 0;
2170 	}
2171 
2172 	reposition_and_zoom (new_leftmost, new_spp);
2173 }
2174 
2175 
2176 bool
choose_new_marker_name(string & name,bool is_range)2177 Editor::choose_new_marker_name(string &name, bool is_range) {
2178 
2179 	if (!UIConfiguration::instance().get_name_new_markers()) {
2180 		/* don't prompt user for a new name */
2181 		return true;
2182 	}
2183 
2184 	Prompter dialog (true);
2185 
2186 	dialog.set_prompt (_("New Name:"));
2187 
2188 	if (is_range) {
2189 		dialog.set_title(_("New Range"));
2190 	} else {
2191 		dialog.set_title (_("New Location Marker"));
2192 	}
2193 
2194 	dialog.set_name ("MarkNameWindow");
2195 	dialog.set_size_request (250, -1);
2196 	dialog.set_position (Gtk::WIN_POS_MOUSE);
2197 
2198 	dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2199 	dialog.set_initial_text (name);
2200 
2201 	dialog.show ();
2202 
2203 	switch (dialog.run ()) {
2204 	case RESPONSE_ACCEPT:
2205 		break;
2206 	default:
2207 		return false;
2208 	}
2209 
2210 	dialog.get_result(name);
2211 	return true;
2212 
2213 }
2214 
2215 
2216 void
add_location_from_selection()2217 Editor::add_location_from_selection ()
2218 {
2219 	string rangename;
2220 
2221 	if (selection->time.empty()) {
2222 		return;
2223 	}
2224 
2225 	if (_session == 0 || clicked_axisview == 0) {
2226 		return;
2227 	}
2228 
2229 	samplepos_t start = selection->time[clicked_selection].start;
2230 	samplepos_t end = selection->time[clicked_selection].end;
2231 
2232 	_session->locations()->next_available_name(rangename,"selection");
2233 	if (!choose_new_marker_name(rangename, true)) {
2234 		return;
2235 	}
2236 	Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2237 
2238 	begin_reversible_command (_("add marker"));
2239 
2240 	XMLNode &before = _session->locations()->get_state();
2241 	_session->locations()->add (location, true);
2242 	XMLNode &after = _session->locations()->get_state();
2243 	_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2244 
2245 	commit_reversible_command ();
2246 }
2247 
2248 void
add_location_mark(samplepos_t where)2249 Editor::add_location_mark (samplepos_t where)
2250 {
2251 	string markername;
2252 
2253 	select_new_marker = true;
2254 
2255 	_session->locations()->next_available_name(markername,"mark");
2256 	if (!choose_new_marker_name(markername)) {
2257 		return;
2258 	}
2259 	Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2260 	begin_reversible_command (_("add marker"));
2261 
2262 	XMLNode &before = _session->locations()->get_state();
2263 	_session->locations()->add (location, true);
2264 	XMLNode &after = _session->locations()->get_state();
2265 	_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2266 
2267 	commit_reversible_command ();
2268 }
2269 
2270 void
set_session_start_from_playhead()2271 Editor::set_session_start_from_playhead ()
2272 {
2273 	if (!_session)
2274 		return;
2275 
2276 	Location* loc;
2277 	if ((loc = _session->locations()->session_range_location()) == 0) {
2278 		_session->set_session_extents (_session->audible_sample(), _session->audible_sample() + 3 * 60 * _session->sample_rate());
2279 	} else {
2280 		XMLNode &before = loc->get_state();
2281 
2282 		_session->set_session_extents (_session->audible_sample(), loc->end());
2283 
2284 		XMLNode &after = loc->get_state();
2285 
2286 		begin_reversible_command (_("Set session start"));
2287 
2288 		_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2289 
2290 		commit_reversible_command ();
2291 	}
2292 
2293 	_session->set_session_range_is_free (false);
2294 }
2295 
2296 void
set_session_end_from_playhead()2297 Editor::set_session_end_from_playhead ()
2298 {
2299 	if (!_session)
2300 		return;
2301 
2302 	Location* loc;
2303 	if ((loc = _session->locations()->session_range_location()) == 0) {  //should never happen
2304 		_session->set_session_extents (0, _session->audible_sample());
2305 	} else {
2306 		XMLNode &before = loc->get_state();
2307 
2308 		_session->set_session_extents (loc->start(), _session->audible_sample());
2309 
2310 		XMLNode &after = loc->get_state();
2311 
2312 		begin_reversible_command (_("Set session start"));
2313 
2314 		_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2315 
2316 		commit_reversible_command ();
2317 	}
2318 
2319 	_session->set_session_range_is_free (false);
2320 }
2321 
2322 
2323 void
toggle_location_at_playhead_cursor()2324 Editor::toggle_location_at_playhead_cursor ()
2325 {
2326 	if (!do_remove_location_at_playhead_cursor())
2327 	{
2328 		add_location_from_playhead_cursor();
2329 	}
2330 }
2331 
2332 void
add_location_from_playhead_cursor()2333 Editor::add_location_from_playhead_cursor ()
2334 {
2335 	add_location_mark (_session->audible_sample());
2336 }
2337 
2338 bool
do_remove_location_at_playhead_cursor()2339 Editor::do_remove_location_at_playhead_cursor ()
2340 {
2341 	bool removed = false;
2342 	if (_session) {
2343 		//set up for undo
2344 		XMLNode &before = _session->locations()->get_state();
2345 
2346 		//find location(s) at this time
2347 		Locations::LocationList locs;
2348 		_session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2349 		for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2350 			if ((*i)->is_mark()) {
2351 				_session->locations()->remove (*i);
2352 				removed = true;
2353 			}
2354 		}
2355 
2356 		//store undo
2357 		if (removed) {
2358 			begin_reversible_command (_("remove marker"));
2359 			XMLNode &after = _session->locations()->get_state();
2360 			_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2361 			commit_reversible_command ();
2362 		}
2363 	}
2364 	return removed;
2365 }
2366 
2367 void
remove_location_at_playhead_cursor()2368 Editor::remove_location_at_playhead_cursor ()
2369 {
2370 	do_remove_location_at_playhead_cursor ();
2371 }
2372 
2373 /** Add a range marker around each selected region */
2374 void
add_locations_from_region()2375 Editor::add_locations_from_region ()
2376 {
2377 	RegionSelection rs = get_regions_from_selection_and_entered ();
2378 
2379 	if (rs.empty()) {
2380 		return;
2381 	}
2382 	bool commit = false;
2383 
2384 	XMLNode &before = _session->locations()->get_state();
2385 
2386 	for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2387 
2388 		boost::shared_ptr<Region> region = (*i)->region ();
2389 
2390 		Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2391 
2392 		_session->locations()->add (location, true);
2393 		commit = true;
2394 	}
2395 
2396 	if (commit) {
2397 		begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2398 		XMLNode &after = _session->locations()->get_state();
2399 		_session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2400 		commit_reversible_command ();
2401 	}
2402 }
2403 
2404 /** Add a single range marker around all selected regions */
2405 void
add_location_from_region()2406 Editor::add_location_from_region ()
2407 {
2408 	RegionSelection rs = get_regions_from_selection_and_entered ();
2409 
2410 	if (rs.empty()) {
2411 		return;
2412 	}
2413 
2414 	XMLNode &before = _session->locations()->get_state();
2415 
2416 	string markername;
2417 
2418 	if (rs.size() > 1) {
2419 		_session->locations()->next_available_name(markername, "regions");
2420 	} else {
2421 		RegionView* rv = *(rs.begin());
2422 		boost::shared_ptr<Region> region = rv->region();
2423 		markername = region->name();
2424 	}
2425 
2426 	if (!choose_new_marker_name(markername)) {
2427 		return;
2428 	}
2429 
2430 	// single range spanning all selected
2431 	Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2432 	_session->locations()->add (location, true);
2433 
2434 	begin_reversible_command (_("add marker"));
2435 	XMLNode &after = _session->locations()->get_state();
2436 	_session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2437 	commit_reversible_command ();
2438 }
2439 
2440 /* MARKS */
2441 
2442 void
jump_forward_to_mark()2443 Editor::jump_forward_to_mark ()
2444 {
2445 	if (!_session) {
2446 		return;
2447 	}
2448 
2449 	samplepos_t pos = _session->locations()->first_mark_after (_playhead_cursor->current_sample());
2450 
2451 	if (pos < 0) {
2452 		return;
2453 	}
2454 
2455 	_session->request_locate (pos);
2456 }
2457 
2458 void
jump_backward_to_mark()2459 Editor::jump_backward_to_mark ()
2460 {
2461 	if (!_session) {
2462 		return;
2463 	}
2464 
2465 	samplepos_t pos = _session->locations()->first_mark_before (_playhead_cursor->current_sample());
2466 
2467 	//handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2468 	if (_session->transport_rolling()) {
2469 		if ((_playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2470 			samplepos_t prior = _session->locations()->first_mark_before (pos);
2471 			pos = prior;
2472 		}
2473 	}
2474 
2475 	if (pos < 0) {
2476 		return;
2477 	}
2478 
2479 	_session->request_locate (pos);
2480 }
2481 
2482 void
set_mark()2483 Editor::set_mark ()
2484 {
2485 	samplepos_t const pos = _session->audible_sample ();
2486 
2487 	string markername;
2488 	_session->locations()->next_available_name (markername, "mark");
2489 
2490 	if (!choose_new_marker_name (markername)) {
2491 		return;
2492 	}
2493 
2494 	_session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2495 }
2496 
2497 void
clear_markers()2498 Editor::clear_markers ()
2499 {
2500 	if (_session) {
2501 		begin_reversible_command (_("clear markers"));
2502 
2503 		XMLNode &before = _session->locations()->get_state();
2504 		if (_session->locations()->clear_markers ()) {
2505 			XMLNode &after = _session->locations()->get_state();
2506 			_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2507 			commit_reversible_command ();
2508 		}
2509 	} else {
2510 		abort_reversible_command ();
2511 	}
2512 }
2513 
2514 void
clear_xrun_markers()2515 Editor::clear_xrun_markers ()
2516 {
2517 	if (_session) {
2518 		begin_reversible_command (_("clear xrun markers"));
2519 
2520 		XMLNode &before = _session->locations()->get_state();
2521 		if (_session->locations()->clear_xrun_markers ()) {
2522 			XMLNode &after = _session->locations()->get_state();
2523 			_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2524 
2525 			commit_reversible_command ();
2526 		}
2527 	} else {
2528 		abort_reversible_command ();
2529 	}
2530 }
2531 
2532 void
clear_ranges()2533 Editor::clear_ranges ()
2534 {
2535 	if (_session) {
2536 		begin_reversible_command (_("clear ranges"));
2537 
2538 		XMLNode &before = _session->locations()->get_state();
2539 
2540 		if (_session->locations()->clear_ranges ()) {
2541 
2542 			XMLNode &after = _session->locations()->get_state();
2543 			_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2544 
2545 			commit_reversible_command ();
2546 		}
2547 	} else {
2548 		abort_reversible_command ();
2549 	}
2550 }
2551 
2552 void
clear_locations()2553 Editor::clear_locations ()
2554 {
2555 	begin_reversible_command (_("clear locations"));
2556 
2557 	XMLNode &before = _session->locations()->get_state();
2558 	if (_session->locations()->clear ()) {
2559 		XMLNode &after = _session->locations()->get_state();
2560 		_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2561 
2562 		commit_reversible_command ();
2563 	} else {
2564 		abort_reversible_command ();
2565 	}
2566 }
2567 
2568 void
unhide_markers()2569 Editor::unhide_markers ()
2570 {
2571 	for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2572 		Location *l = (*i).first;
2573 		if (l->is_hidden() && l->is_mark()) {
2574 			l->set_hidden(false, this);
2575 		}
2576 	}
2577 }
2578 
2579 void
unhide_ranges()2580 Editor::unhide_ranges ()
2581 {
2582 	for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2583 		Location *l = (*i).first;
2584 		if (l->is_hidden() && l->is_range_marker()) {
2585 			l->set_hidden(false, this);
2586 		}
2587 	}
2588 }
2589 
2590 /* INSERT/REPLACE */
2591 
2592 void
insert_source_list_selection(float times)2593 Editor::insert_source_list_selection (float times)
2594 {
2595 	RouteTimeAxisView *tv = 0;
2596 	boost::shared_ptr<Playlist> playlist;
2597 
2598 	if (clicked_routeview != 0) {
2599 		tv = clicked_routeview;
2600 	} else if (!selection->tracks.empty()) {
2601 		if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2602 			return;
2603 		}
2604 	} else if (entered_track != 0) {
2605 		if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2606 			return;
2607 		}
2608 	} else {
2609 		return;
2610 	}
2611 
2612 	if ((playlist = tv->playlist()) == 0) {
2613 		return;
2614 	}
2615 
2616 	boost::shared_ptr<Region> region = _sources->get_single_selection ();
2617 	if (region == 0) {
2618 		return;
2619 	}
2620 
2621 	begin_reversible_command (_("insert region"));
2622 	playlist->clear_changes ();
2623 	playlist->clear_owned_changes ();
2624 	playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2625 	if (Config->get_edit_mode() == Ripple) {
2626 		playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2627 		/* recusive diff of rippled regions */
2628 		vector<Command*> cmds;
2629 		playlist->rdiff (cmds);
2630 		_session->add_commands (cmds);
2631 	}
2632 
2633 	_session->add_command(new StatefulDiffCommand (playlist));
2634 	commit_reversible_command ();
2635 }
2636 
2637 /* BUILT-IN EFFECTS */
2638 
2639 void
reverse_selection()2640 Editor::reverse_selection ()
2641 {
2642 
2643 }
2644 
2645 /* GAIN ENVELOPE EDITING */
2646 
2647 void
edit_envelope()2648 Editor::edit_envelope ()
2649 {
2650 }
2651 
2652 /* PLAYBACK */
2653 
2654 void
transition_to_rolling(bool fwd)2655 Editor::transition_to_rolling (bool fwd)
2656 {
2657 	if (!_session) {
2658 		return;
2659 	}
2660 
2661 	if (_session->config.get_external_sync()) {
2662 		switch (TransportMasterManager::instance().current()->type()) {
2663 		case Engine:
2664 			break;
2665 		default:
2666 			/* transport controlled by the master */
2667 			return;
2668 		}
2669 	}
2670 
2671 	if (_session->is_auditioning()) {
2672 		_session->cancel_audition ();
2673 		return;
2674 	}
2675 
2676 	_session->request_transport_speed (fwd ? 1.0f : -1.0f, false);
2677 	_session->request_roll ();
2678 }
2679 
2680 void
play_from_start()2681 Editor::play_from_start ()
2682 {
2683 	_session->request_locate (_session->current_start_sample(), MustRoll);
2684 }
2685 
2686 void
play_from_edit_point()2687 Editor::play_from_edit_point ()
2688 {
2689 	_session->request_locate (get_preferred_edit_position(), MustRoll);
2690 }
2691 
2692 void
play_from_edit_point_and_return()2693 Editor::play_from_edit_point_and_return ()
2694 {
2695 	samplepos_t start_sample;
2696 	samplepos_t return_sample;
2697 
2698 	start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2699 
2700 	if (_session->transport_rolling()) {
2701 		_session->request_locate (start_sample, MustStop);
2702 		return;
2703 	}
2704 
2705 	/* don't reset the return sample if its already set */
2706 
2707 	if ((return_sample = _session->requested_return_sample()) < 0) {
2708 		return_sample = _session->audible_sample();
2709 	}
2710 
2711 	if (start_sample >= 0) {
2712 		_session->request_roll_at_and_return (start_sample, return_sample);
2713 	}
2714 }
2715 
2716 void
play_selection()2717 Editor::play_selection ()
2718 {
2719 	samplepos_t start, end;
2720 	if (!get_selection_extents (start, end))
2721 		return;
2722 
2723 	AudioRange ar (start, end, 0);
2724 	list<AudioRange> lar;
2725 	lar.push_back (ar);
2726 
2727 	_session->request_play_range (&lar, true);
2728 }
2729 
2730 
2731 void
maybe_locate_with_edit_preroll(samplepos_t location)2732 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2733 {
2734 	if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2735 		return;
2736 
2737 	location -= _session->preroll_samples (location);
2738 
2739 	//don't try to locate before the beginning of time
2740 	if (location < 0) {
2741 		location = 0;
2742 	}
2743 
2744 	//if follow_playhead is on, keep the playhead on the screen
2745 	if (_follow_playhead)
2746 		if (location < _leftmost_sample)
2747 			location = _leftmost_sample;
2748 
2749 	_session->request_locate (location);
2750 }
2751 
2752 void
play_with_preroll()2753 Editor::play_with_preroll ()
2754 {
2755 	samplepos_t start, end;
2756 	if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2757 		const samplepos_t preroll = _session->preroll_samples (start);
2758 
2759 		samplepos_t ret = start;
2760 
2761 		if (start > preroll) {
2762 			start = start - preroll;
2763 		}
2764 
2765 		end = end + preroll;  //"post-roll"
2766 
2767 		AudioRange ar (start, end, 0);
2768 		list<AudioRange> lar;
2769 		lar.push_back (ar);
2770 
2771 		_session->request_play_range (&lar, true);
2772 		_session->set_requested_return_sample (ret);  //force auto-return to return to range start, without the preroll
2773 	} else {
2774 		samplepos_t ph = _playhead_cursor->current_sample ();
2775 		const samplepos_t preroll = _session->preroll_samples (ph);
2776 		samplepos_t start;
2777 		if (ph > preroll) {
2778 			start = ph - preroll;
2779 		} else {
2780 			start = 0;
2781 		}
2782 		_session->request_locate (start, MustRoll);
2783 		_session->set_requested_return_sample (ph);  //force auto-return to return to playhead location, without the preroll
2784 	}
2785 }
2786 
2787 void
rec_with_preroll()2788 Editor::rec_with_preroll ()
2789 {
2790 	samplepos_t ph = _playhead_cursor->current_sample ();
2791 	samplepos_t preroll = _session->preroll_samples (ph);
2792 	_session->request_preroll_record_trim (ph, preroll);
2793 }
2794 
2795 void
rec_with_count_in()2796 Editor::rec_with_count_in ()
2797 {
2798 	_session->request_count_in_record ();
2799 }
2800 
2801 void
play_location(Location & location)2802 Editor::play_location (Location& location)
2803 {
2804 	if (location.start() <= location.end()) {
2805 		return;
2806 	}
2807 
2808 	_session->request_bounded_roll (location.start(), location.end());
2809 }
2810 
2811 void
loop_location(Location & location)2812 Editor::loop_location (Location& location)
2813 {
2814 	if (location.start() <= location.end()) {
2815 		return;
2816 	}
2817 
2818 	Location* tll;
2819 
2820 	if ((tll = transport_loop_location()) != 0) {
2821 		tll->set (location.start(), location.end());
2822 
2823 		// enable looping, reposition and start rolling
2824 		_session->request_locate (tll->start(), MustRoll);
2825 		_session->request_play_loop (true);
2826 	}
2827 }
2828 
2829 void
do_layer_operation(LayerOperation op)2830 Editor::do_layer_operation (LayerOperation op)
2831 {
2832 	if (selection->regions.empty ()) {
2833 		return;
2834 	}
2835 
2836 	bool const multiple = selection->regions.size() > 1;
2837 	switch (op) {
2838 	case Raise:
2839 		if (multiple) {
2840 			begin_reversible_command (_("raise regions"));
2841 		} else {
2842 			begin_reversible_command (_("raise region"));
2843 		}
2844 		break;
2845 
2846 	case RaiseToTop:
2847 		if (multiple) {
2848 			begin_reversible_command (_("raise regions to top"));
2849 		} else {
2850 			begin_reversible_command (_("raise region to top"));
2851 		}
2852 		break;
2853 
2854 	case Lower:
2855 		if (multiple) {
2856 			begin_reversible_command (_("lower regions"));
2857 		} else {
2858 			begin_reversible_command (_("lower region"));
2859 		}
2860 		break;
2861 
2862 	case LowerToBottom:
2863 		if (multiple) {
2864 			begin_reversible_command (_("lower regions to bottom"));
2865 		} else {
2866 			begin_reversible_command (_("lower region"));
2867 		}
2868 		break;
2869 	}
2870 
2871 	set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2872 	for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2873 		(*i)->clear_owned_changes ();
2874 	}
2875 
2876 	for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2877 		boost::shared_ptr<Region> r = (*i)->region ();
2878 		switch (op) {
2879 		case Raise:
2880 			r->raise ();
2881 			break;
2882 		case RaiseToTop:
2883 			r->raise_to_top ();
2884 			break;
2885 		case Lower:
2886 			r->lower ();
2887 			break;
2888 		case LowerToBottom:
2889 			r->lower_to_bottom ();
2890 		}
2891 	}
2892 
2893 	for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2894 		vector<Command*> cmds;
2895 		(*i)->rdiff (cmds);
2896 		_session->add_commands (cmds);
2897 	}
2898 
2899 	commit_reversible_command ();
2900 }
2901 
2902 void
raise_region()2903 Editor::raise_region ()
2904 {
2905 	do_layer_operation (Raise);
2906 }
2907 
2908 void
raise_region_to_top()2909 Editor::raise_region_to_top ()
2910 {
2911 	do_layer_operation (RaiseToTop);
2912 }
2913 
2914 void
lower_region()2915 Editor::lower_region ()
2916 {
2917 	do_layer_operation (Lower);
2918 }
2919 
2920 void
lower_region_to_bottom()2921 Editor::lower_region_to_bottom ()
2922 {
2923 	do_layer_operation (LowerToBottom);
2924 }
2925 
2926 /** Show the region editor for the selected regions */
2927 void
show_region_properties()2928 Editor::show_region_properties ()
2929 {
2930 	selection->foreach_regionview (&RegionView::show_region_editor);
2931 }
2932 
2933 /** Show the midi list editor for the selected MIDI regions */
2934 void
show_midi_list_editor()2935 Editor::show_midi_list_editor ()
2936 {
2937 	selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2938 }
2939 
2940 void
rename_region()2941 Editor::rename_region ()
2942 {
2943 	RegionSelection rs = get_regions_from_selection_and_entered ();
2944 
2945 	if (rs.empty()) {
2946 		return;
2947 	}
2948 
2949 	ArdourDialog d (_("Rename Region"), true, false);
2950 	Entry entry;
2951 	Label label (_("New name:"));
2952 	HBox hbox;
2953 
2954 	hbox.set_spacing (6);
2955 	hbox.pack_start (label, false, false);
2956 	hbox.pack_start (entry, true, true);
2957 
2958 	d.get_vbox()->set_border_width (12);
2959 	d.get_vbox()->pack_start (hbox, false, false);
2960 
2961 	d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2962 	d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2963 
2964 	d.set_size_request (300, -1);
2965 
2966 	entry.set_text (rs.front()->region()->name());
2967 	entry.select_region (0, -1);
2968 
2969 	entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2970 
2971 	d.show_all ();
2972 
2973 	entry.grab_focus();
2974 
2975 	int const ret = d.run();
2976 
2977 	d.hide ();
2978 
2979 	if (ret != RESPONSE_OK) {
2980 		return;
2981 	}
2982 
2983 	std::string str = entry.get_text();
2984 	strip_whitespace_edges (str);
2985 	if (!str.empty()) {
2986 		if (!rs.front()->region()->set_name (str)) {
2987 			ArdourMessageDialog msg (_("Rename failed. Check for characters such as '/' or ':'"));
2988 			msg.run ();
2989 		} else {
2990 			_regions->redisplay ();
2991 		}
2992 	}
2993 }
2994 
2995 /** Start an audition of the first selected region */
2996 void
play_edit_range()2997 Editor::play_edit_range ()
2998 {
2999 	samplepos_t start, end;
3000 
3001 	if (get_edit_op_range (start, end)) {
3002 		_session->request_bounded_roll (start, end);
3003 	}
3004 }
3005 
3006 void
play_selected_region()3007 Editor::play_selected_region ()
3008 {
3009 	samplepos_t start = max_samplepos;
3010 	samplepos_t end = 0;
3011 
3012 	RegionSelection rs = get_regions_from_selection_and_entered ();
3013 
3014 	if (rs.empty()) {
3015 		return;
3016 	}
3017 
3018 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3019 		if ((*i)->region()->position() < start) {
3020 			start = (*i)->region()->position();
3021 		}
3022 		if ((*i)->region()->last_sample() + 1 > end) {
3023 			end = (*i)->region()->last_sample() + 1;
3024 		}
3025 	}
3026 
3027 	_session->request_bounded_roll (start, end);
3028 }
3029 
3030 void
audition_playlist_region_standalone(boost::shared_ptr<Region> region)3031 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
3032 {
3033 	_session->audition_region (region);
3034 }
3035 
3036 void
region_from_selection()3037 Editor::region_from_selection ()
3038 {
3039 	if (clicked_axisview == 0) {
3040 		return;
3041 	}
3042 
3043 	if (selection->time.empty()) {
3044 		return;
3045 	}
3046 
3047 	samplepos_t start = selection->time[clicked_selection].start;
3048 	samplepos_t end = selection->time[clicked_selection].end;
3049 
3050 	TrackViewList tracks = get_tracks_for_range_action ();
3051 
3052 	samplepos_t selection_cnt = end - start + 1;
3053 
3054 	for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
3055 		boost::shared_ptr<Region> current;
3056 		boost::shared_ptr<Playlist> pl;
3057 		samplepos_t internal_start;
3058 		string new_name;
3059 
3060 		if ((pl = (*i)->playlist()) == 0) {
3061 			continue;
3062 		}
3063 
3064 		if ((current = pl->top_region_at (start)) == 0) {
3065 			continue;
3066 		}
3067 
3068 		internal_start = start - current->position();
3069 		RegionFactory::region_name (new_name, current->name(), true);
3070 
3071 		PropertyList plist;
3072 
3073 		plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3074 		plist.add (ARDOUR::Properties::length, selection_cnt);
3075 		plist.add (ARDOUR::Properties::name, new_name);
3076 		plist.add (ARDOUR::Properties::layer, 0);
3077 
3078 		boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3079 	}
3080 }
3081 
3082 void
create_region_from_selection(vector<boost::shared_ptr<Region>> & new_regions)3083 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3084 {
3085 	if (selection->time.empty() || selection->tracks.empty()) {
3086 		return;
3087 	}
3088 
3089 	samplepos_t start, end;
3090 	if (clicked_selection) {
3091 		start = selection->time[clicked_selection].start;
3092 		end = selection->time[clicked_selection].end;
3093 	} else {
3094 		start = selection->time.start();
3095 		end = selection->time.end_sample();
3096 	}
3097 
3098 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3099 	sort_track_selection (ts);
3100 
3101 	for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3102 		boost::shared_ptr<Region> current;
3103 		boost::shared_ptr<Playlist> playlist;
3104 		samplepos_t internal_start;
3105 		string new_name;
3106 
3107 		if ((playlist = (*i)->playlist()) == 0) {
3108 			continue;
3109 		}
3110 
3111 		if ((current = playlist->top_region_at(start)) == 0) {
3112 			continue;
3113 		}
3114 
3115 		internal_start = start - current->position();
3116 		RegionFactory::region_name (new_name, current->name(), true);
3117 
3118 		PropertyList plist;
3119 
3120 		plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3121 		plist.add (ARDOUR::Properties::length, end - start + 1);
3122 		plist.add (ARDOUR::Properties::name, new_name);
3123 
3124 		new_regions.push_back (RegionFactory::create (current, plist));
3125 	}
3126 }
3127 
3128 void
split_multichannel_region()3129 Editor::split_multichannel_region ()
3130 {
3131 	RegionSelection rs = get_regions_from_selection_and_entered ();
3132 
3133 	if (rs.empty()) {
3134 		return;
3135 	}
3136 
3137 	vector< boost::shared_ptr<Region> > v;
3138 
3139 	for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3140 		(*x)->region()->separate_by_channel (v);
3141 	}
3142 }
3143 
3144 void
new_region_from_selection()3145 Editor::new_region_from_selection ()
3146 {
3147 	region_from_selection ();
3148 	cancel_selection ();
3149 }
3150 
3151 static void
add_if_covered(RegionView * rv,const AudioRange * ar,RegionSelection * rs)3152 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3153 {
3154 	switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3155 	// n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3156 	case Evoral::OverlapNone:
3157 		break;
3158 	default:
3159 		rs->push_back (rv);
3160 	}
3161 }
3162 
3163 /** Return either:
3164  *    - selected tracks, or if there are none...
3165  *    - tracks containing selected regions, or if there are none...
3166  *    - all tracks
3167  * @return tracks.
3168  */
3169 TrackViewList
get_tracks_for_range_action() const3170 Editor::get_tracks_for_range_action () const
3171 {
3172 	TrackViewList t;
3173 
3174 	if (selection->tracks.empty()) {
3175 
3176 		/* use tracks with selected regions */
3177 
3178 		RegionSelection rs = selection->regions;
3179 
3180 		for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3181 			TimeAxisView* tv = &(*i)->get_time_axis_view();
3182 
3183 			if (!t.contains (tv)) {
3184 				t.push_back (tv);
3185 			}
3186 		}
3187 
3188 		if (t.empty()) {
3189 			/* no regions and no tracks: use all tracks */
3190 			t = track_views;
3191 		}
3192 
3193 	} else {
3194 
3195 		t = selection->tracks;
3196 	}
3197 
3198 	return t.filter_to_unique_playlists();
3199 }
3200 
3201 void
separate_regions_between(const TimeSelection & ts)3202 Editor::separate_regions_between (const TimeSelection& ts)
3203 {
3204 	bool in_command = false;
3205 	boost::shared_ptr<Playlist> playlist;
3206 	RegionSelection new_selection;
3207 
3208 	TrackViewList tmptracks = get_tracks_for_range_action ();
3209 	sort_track_selection (tmptracks);
3210 
3211 	for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3212 
3213 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3214 
3215 		if (!rtv) {
3216 			continue;
3217 		}
3218 
3219 		if (!rtv->is_track()) {
3220 			continue;
3221 		}
3222 
3223 		if ((playlist = rtv->playlist()) != 0) {
3224 
3225 			playlist->clear_changes ();
3226 
3227 			/* XXX need to consider musical time selections here at some point */
3228 
3229 			for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3230 
3231 				if (!in_command) {
3232 					begin_reversible_command (_("separate"));
3233 					in_command = true;
3234 				}
3235 
3236 				sigc::connection c = rtv->view()->RegionViewAdded.connect (
3237 					sigc::mem_fun(*this, &Editor::collect_new_region_view));
3238 
3239 				latest_regionviews.clear ();
3240 
3241 				playlist->partition ((*t).start, (*t).end, false);
3242 
3243 				c.disconnect ();
3244 
3245 				if (!latest_regionviews.empty()) {
3246 
3247 					rtv->view()->foreach_regionview (sigc::bind (
3248 					                                             sigc::ptr_fun (add_if_covered),
3249 					                                             &(*t), &new_selection));
3250 
3251 					/* pick up changes to existing regions */
3252 
3253 					vector<Command*> cmds;
3254 					playlist->rdiff (cmds);
3255 					_session->add_commands (cmds);
3256 
3257 					/* pick up changes to the playlist itself (adds/removes) */
3258 
3259 					_session->add_command(new StatefulDiffCommand (playlist));
3260 				}
3261 			}
3262 		}
3263 	}
3264 
3265 	if (in_command) {
3266 		if (_session->abort_empty_reversible_command ()) {
3267 			return;
3268 		}
3269 
3270 		RangeSelectionAfterSplit rsas = Config->get_range_selection_after_split();
3271 
3272 		//if our config preference says to clear the selection, clear the Range selection
3273 		if (rsas == ClearSel) {
3274 			selection->clear_time();
3275 			//but leave track selection intact
3276 		} else if (rsas == ForceSel) {
3277 			//note: forcing the regions to be selected *might* force a tool-change to Object here
3278 			selection->set(new_selection);
3279 		}
3280 
3281 		commit_reversible_command ();
3282 	}
3283 }
3284 
3285 struct PlaylistState {
3286 	boost::shared_ptr<Playlist> playlist;
3287 	XMLNode*  before;
3288 };
3289 
3290 /** Take tracks from get_tracks_for_range_action and cut any regions
3291  *  on those tracks so that the tracks are empty over the time
3292  *  selection.
3293  */
3294 void
separate_region_from_selection()3295 Editor::separate_region_from_selection ()
3296 {
3297 	/* preferentially use *all* ranges in the time selection if we're in range mode
3298 	   to allow discontiguous operation, since get_edit_op_range() currently
3299 	   returns a single range.
3300 	*/
3301 
3302 	if (!selection->time.empty()) {
3303 
3304 		separate_regions_between (selection->time);
3305 
3306 	} else {
3307 
3308 		samplepos_t start;
3309 		samplepos_t end;
3310 
3311 		if (get_edit_op_range (start, end)) {
3312 
3313 			AudioRange ar (start, end, 1);
3314 			TimeSelection ts;
3315 			ts.push_back (ar);
3316 
3317 			separate_regions_between (ts);
3318 		}
3319 	}
3320 }
3321 
3322 void
separate_region_from_punch()3323 Editor::separate_region_from_punch ()
3324 {
3325 	Location* loc  = _session->locations()->auto_punch_location();
3326 	if (loc) {
3327 		separate_regions_using_location (*loc);
3328 	}
3329 }
3330 
3331 void
separate_region_from_loop()3332 Editor::separate_region_from_loop ()
3333 {
3334 	Location* loc  = _session->locations()->auto_loop_location();
3335 	if (loc) {
3336 		separate_regions_using_location (*loc);
3337 	}
3338 }
3339 
3340 void
separate_regions_using_location(Location & loc)3341 Editor::separate_regions_using_location (Location& loc)
3342 {
3343 	if (loc.is_mark()) {
3344 		return;
3345 	}
3346 
3347 	AudioRange ar (loc.start(), loc.end(), 1);
3348 	TimeSelection ts;
3349 
3350 	ts.push_back (ar);
3351 
3352 	separate_regions_between (ts);
3353 }
3354 
3355 /** Separate regions under the selected region */
3356 void
separate_under_selected_regions()3357 Editor::separate_under_selected_regions ()
3358 {
3359 	vector<PlaylistState> playlists;
3360 
3361 	RegionSelection rs;
3362 
3363 	rs = get_regions_from_selection_and_entered();
3364 
3365 	if (!_session || rs.empty()) {
3366 		return;
3367 	}
3368 
3369 	begin_reversible_command (_("separate region under"));
3370 
3371 	list<boost::shared_ptr<Region> > regions_to_remove;
3372 
3373 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3374 		// we can't just remove the region(s) in this loop because
3375 		// this removes them from the RegionSelection, and they thus
3376 		// disappear from underneath the iterator, and the ++i above
3377 		// SEGVs in a puzzling fashion.
3378 
3379 		// so, first iterate over the regions to be removed from rs and
3380 		// add them to the regions_to_remove list, and then
3381 		// iterate over the list to actually remove them.
3382 
3383 		regions_to_remove.push_back ((*i)->region());
3384 	}
3385 
3386 	for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3387 
3388 		boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3389 
3390 		if (!playlist) {
3391 			// is this check necessary?
3392 			continue;
3393 		}
3394 
3395 		vector<PlaylistState>::iterator i;
3396 
3397 		//only take state if this is a new playlist.
3398 		for (i = playlists.begin(); i != playlists.end(); ++i) {
3399 			if ((*i).playlist == playlist) {
3400 				break;
3401 			}
3402 		}
3403 
3404 		if (i == playlists.end()) {
3405 
3406 			PlaylistState before;
3407 			before.playlist = playlist;
3408 			before.before = &playlist->get_state();
3409 			playlist->clear_changes ();
3410 			playlist->freeze ();
3411 			playlists.push_back(before);
3412 		}
3413 
3414 		//Partition on the region bounds
3415 		playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3416 
3417 		//Re-add region that was just removed due to the partition operation
3418 		playlist->add_region ((*rl), (*rl)->first_sample());
3419 	}
3420 
3421 	vector<PlaylistState>::iterator pl;
3422 
3423 	for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3424 		(*pl).playlist->thaw ();
3425 		_session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3426 	}
3427 
3428 	commit_reversible_command ();
3429 }
3430 
3431 void
crop_region_to_selection()3432 Editor::crop_region_to_selection ()
3433 {
3434 	if (!selection->time.empty()) {
3435 
3436 		begin_reversible_command (_("Crop Regions to Time Selection"));
3437 		for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3438 			crop_region_to ((*i).start, (*i).end);
3439 		}
3440 		commit_reversible_command();
3441 	} else {
3442 
3443 		samplepos_t start;
3444 		samplepos_t end;
3445 
3446 		if (get_edit_op_range (start, end)) {
3447 			begin_reversible_command (_("Crop Regions to Edit Range"));
3448 
3449 			crop_region_to (start, end);
3450 
3451 			commit_reversible_command();
3452 		}
3453 	}
3454 
3455 }
3456 
3457 void
crop_region_to(samplepos_t start,samplepos_t end)3458 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3459 {
3460 	vector<boost::shared_ptr<Playlist> > playlists;
3461 	boost::shared_ptr<Playlist> playlist;
3462 	TrackViewList ts;
3463 
3464 	if (selection->tracks.empty()) {
3465 		ts = track_views.filter_to_unique_playlists();
3466 	} else {
3467 		ts = selection->tracks.filter_to_unique_playlists ();
3468 	}
3469 
3470 	sort_track_selection (ts);
3471 
3472 	for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3473 
3474 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3475 
3476 		if (!rtv) {
3477 			continue;
3478 		}
3479 
3480 		boost::shared_ptr<Track> t = rtv->track();
3481 
3482 		if (t) {
3483 			if ((playlist = rtv->playlist()) != 0) {
3484 				playlists.push_back (playlist);
3485 			}
3486 		}
3487 	}
3488 
3489 	if (playlists.empty()) {
3490 		return;
3491 	}
3492 
3493 	samplepos_t pos;
3494 	samplepos_t new_start;
3495 	samplepos_t new_end;
3496 	samplecnt_t new_length;
3497 
3498 	for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3499 
3500 		/* Only the top regions at start and end have to be cropped */
3501 		boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3502 		boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3503 
3504 		vector<boost::shared_ptr<Region> > regions;
3505 
3506 		if (region_at_start != 0) {
3507 			regions.push_back (region_at_start);
3508 		}
3509 		if (region_at_end != 0) {
3510 			regions.push_back (region_at_end);
3511 		}
3512 
3513 		/* now adjust lengths */
3514 		for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3515 
3516 			pos = (*i)->position();
3517 			new_start = max (start, pos);
3518 			if (max_samplepos - pos > (*i)->length()) {
3519 				new_end = pos + (*i)->length() - 1;
3520 			} else {
3521 				new_end = max_samplepos;
3522 			}
3523 			new_end = min (end, new_end);
3524 			new_length = new_end - new_start + 1;
3525 
3526 			(*i)->clear_changes ();
3527 			(*i)->trim_to (new_start, new_length);
3528 			_session->add_command (new StatefulDiffCommand (*i));
3529 		}
3530 	}
3531 }
3532 
3533 void
region_fill_track()3534 Editor::region_fill_track ()
3535 {
3536 	boost::shared_ptr<Playlist> playlist;
3537 	RegionSelection regions = get_regions_from_selection_and_entered ();
3538 	RegionSelection foo;
3539 
3540 	samplepos_t const end = _session->current_end_sample ();
3541 
3542 	if (regions.empty () || regions.end_sample () + 1 >= end) {
3543 		return;
3544 	}
3545 
3546 	samplepos_t const start_sample = regions.start ();
3547 	samplepos_t const end_sample = regions.end_sample ();
3548 	samplecnt_t const gap = end_sample - start_sample + 1;
3549 
3550 	begin_reversible_command (Operations::region_fill);
3551 
3552 	selection->clear_regions ();
3553 
3554 	for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3555 
3556 		boost::shared_ptr<Region> r ((*i)->region());
3557 
3558 		TimeAxisView& tv = (*i)->get_time_axis_view();
3559 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3560 		latest_regionviews.clear ();
3561 		sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3562 
3563 		samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3564 		playlist = (*i)->region()->playlist();
3565 		playlist->clear_changes ();
3566 		playlist->duplicate_until (r, position, gap, end);
3567 		_session->add_command(new StatefulDiffCommand (playlist));
3568 
3569 		c.disconnect ();
3570 
3571 		foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3572 	}
3573 
3574 	if (!foo.empty()) {
3575 		selection->set (foo);
3576 	}
3577 
3578 	commit_reversible_command ();
3579 }
3580 
3581 void
set_region_sync_position()3582 Editor::set_region_sync_position ()
3583 {
3584 	set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3585 }
3586 
3587 void
set_sync_point(samplepos_t where,const RegionSelection & rs)3588 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3589 {
3590 	bool in_command = false;
3591 
3592 	for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3593 
3594 		if (!(*r)->region()->covers (where)) {
3595 			continue;
3596 		}
3597 
3598 		boost::shared_ptr<Region> region ((*r)->region());
3599 
3600 		if (!in_command) {
3601 			begin_reversible_command (_("set sync point"));
3602 			in_command = true;
3603 		}
3604 
3605 		region->clear_changes ();
3606 		region->set_sync_position (where);
3607 		_session->add_command(new StatefulDiffCommand (region));
3608 	}
3609 
3610 	if (in_command) {
3611 		commit_reversible_command ();
3612 	}
3613 }
3614 
3615 /** Remove the sync positions of the selection */
3616 void
remove_region_sync()3617 Editor::remove_region_sync ()
3618 {
3619 	RegionSelection rs = get_regions_from_selection_and_entered ();
3620 
3621 	if (rs.empty()) {
3622 		return;
3623 	}
3624 
3625 	begin_reversible_command (_("remove region sync"));
3626 
3627 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3628 
3629 		(*i)->region()->clear_changes ();
3630 		(*i)->region()->clear_sync_position ();
3631 		_session->add_command(new StatefulDiffCommand ((*i)->region()));
3632 	}
3633 
3634 	commit_reversible_command ();
3635 }
3636 
3637 void
naturalize_region()3638 Editor::naturalize_region ()
3639 {
3640 	RegionSelection rs = get_regions_from_selection_and_entered ();
3641 
3642 	if (rs.empty()) {
3643 		return;
3644 	}
3645 
3646 	if (rs.size() > 1) {
3647 		begin_reversible_command (_("move regions to original position"));
3648 	} else {
3649 		begin_reversible_command (_("move region to original position"));
3650 	}
3651 
3652 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3653 		(*i)->region()->clear_changes ();
3654 		(*i)->region()->move_to_natural_position ();
3655 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
3656 	}
3657 
3658 	commit_reversible_command ();
3659 }
3660 
3661 void
align_regions(RegionPoint what)3662 Editor::align_regions (RegionPoint what)
3663 {
3664 	RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3665 
3666 	if (rs.empty()) {
3667 		return;
3668 	}
3669 
3670 	begin_reversible_command (_("align selection"));
3671 
3672 	samplepos_t const position = get_preferred_edit_position ();
3673 
3674 	for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3675 		align_region_internal ((*i)->region(), what, position);
3676 	}
3677 
3678 	commit_reversible_command ();
3679 }
3680 
3681 struct RegionSortByTime {
operator ()RegionSortByTime3682 	bool operator() (const RegionView* a, const RegionView* b) {
3683 		return a->region()->position() < b->region()->position();
3684 	}
3685 };
3686 
3687 void
align_regions_relative(RegionPoint point)3688 Editor::align_regions_relative (RegionPoint point)
3689 {
3690 	RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3691 
3692 	if (rs.empty()) {
3693 		return;
3694 	}
3695 
3696 	samplepos_t const position = get_preferred_edit_position ();
3697 
3698 	samplepos_t distance = 0;
3699 	samplepos_t pos = 0;
3700 	int dir = 1;
3701 
3702 	list<RegionView*> sorted;
3703 	rs.by_position (sorted);
3704 
3705 	boost::shared_ptr<Region> r ((*sorted.begin())->region());
3706 
3707 	switch (point) {
3708 	case Start:
3709 		pos = position;
3710 		if (position > r->position()) {
3711 			distance = position - r->position();
3712 		} else {
3713 			distance = r->position() - position;
3714 			dir = -1;
3715 		}
3716 		break;
3717 
3718 	case End:
3719 		if (position > r->last_sample()) {
3720 			distance = position - r->last_sample();
3721 			pos = r->position() + distance;
3722 		} else {
3723 			distance = r->last_sample() - position;
3724 			pos = r->position() - distance;
3725 			dir = -1;
3726 		}
3727 		break;
3728 
3729 	case SyncPoint:
3730 		pos = r->adjust_to_sync (position);
3731 		if (pos > r->position()) {
3732 			distance = pos - r->position();
3733 		} else {
3734 			distance = r->position() - pos;
3735 			dir = -1;
3736 		}
3737 		break;
3738 	}
3739 
3740 	if (pos == r->position()) {
3741 		return;
3742 	}
3743 
3744 	begin_reversible_command (_("align selection (relative)"));
3745 
3746 	/* move first one specially */
3747 
3748 	r->clear_changes ();
3749 	r->set_position (pos);
3750 	_session->add_command(new StatefulDiffCommand (r));
3751 
3752 	/* move rest by the same amount */
3753 
3754 	sorted.pop_front();
3755 
3756 	for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3757 
3758 		boost::shared_ptr<Region> region ((*i)->region());
3759 
3760 		region->clear_changes ();
3761 
3762 		if (dir > 0) {
3763 			region->set_position (region->position() + distance);
3764 		} else {
3765 			region->set_position (region->position() - distance);
3766 		}
3767 
3768 		_session->add_command(new StatefulDiffCommand (region));
3769 
3770 	}
3771 
3772 	commit_reversible_command ();
3773 }
3774 
3775 void
align_region(boost::shared_ptr<Region> region,RegionPoint point,samplepos_t position)3776 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3777 {
3778 	begin_reversible_command (_("align region"));
3779 	align_region_internal (region, point, position);
3780 	commit_reversible_command ();
3781 }
3782 
3783 void
align_region_internal(boost::shared_ptr<Region> region,RegionPoint point,samplepos_t position)3784 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3785 {
3786 	region->clear_changes ();
3787 
3788 	switch (point) {
3789 	case SyncPoint:
3790 		region->set_position (region->adjust_to_sync (position));
3791 		break;
3792 
3793 	case End:
3794 		if (position > region->length()) {
3795 			region->set_position (position - region->length());
3796 		}
3797 		break;
3798 
3799 	case Start:
3800 		region->set_position (position);
3801 		break;
3802 	}
3803 
3804 	_session->add_command(new StatefulDiffCommand (region));
3805 }
3806 
3807 void
trim_region_front()3808 Editor::trim_region_front ()
3809 {
3810 	trim_region (true);
3811 }
3812 
3813 void
trim_region_back()3814 Editor::trim_region_back ()
3815 {
3816 	trim_region (false);
3817 }
3818 
3819 void
trim_region(bool front)3820 Editor::trim_region (bool front)
3821 {
3822 	samplepos_t where = get_preferred_edit_position();
3823 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
3824 
3825 	if (rs.empty()) {
3826 		return;
3827 	}
3828 
3829 	begin_reversible_command (front ? _("trim front") : _("trim back"));
3830 
3831 	for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3832 		if (!(*i)->region()->locked()) {
3833 
3834 			(*i)->region()->clear_changes ();
3835 
3836 			if (front) {
3837 				(*i)->region()->trim_front (where);
3838 			} else {
3839 				(*i)->region()->trim_end (where);
3840 			}
3841 
3842 			_session->add_command (new StatefulDiffCommand ((*i)->region()));
3843 		}
3844 	}
3845 
3846 	commit_reversible_command ();
3847 }
3848 
3849 /** Trim the end of the selected regions to the position of the edit cursor */
3850 void
trim_region_to_loop()3851 Editor::trim_region_to_loop ()
3852 {
3853 	Location* loc = _session->locations()->auto_loop_location();
3854 	if (!loc) {
3855 		return;
3856 	}
3857 	trim_region_to_location (*loc, _("trim to loop"));
3858 }
3859 
3860 void
trim_region_to_punch()3861 Editor::trim_region_to_punch ()
3862 {
3863 	Location* loc = _session->locations()->auto_punch_location();
3864 	if (!loc) {
3865 		return;
3866 	}
3867 	trim_region_to_location (*loc, _("trim to punch"));
3868 }
3869 
3870 void
trim_region_to_location(const Location & loc,const char * str)3871 Editor::trim_region_to_location (const Location& loc, const char* str)
3872 {
3873 	RegionSelection rs = get_regions_from_selection_and_entered ();
3874 	bool in_command = false;
3875 
3876 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3877 		RegionView* rv = (*x);
3878 
3879 		/* require region to span proposed trim */
3880 		switch (rv->region()->coverage (loc.start(), loc.end())) {
3881 		case Evoral::OverlapNone:
3882 			continue;
3883 		default:
3884 			break;
3885 		}
3886 
3887 		RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3888 		if (!tav) {
3889 			return;
3890 		}
3891 
3892 		samplepos_t start;
3893 		samplepos_t end;
3894 
3895 		start = max (loc.start(), rv->region()->position());
3896 		end = min (loc.end(), rv->region()->position() + rv->region()->length());
3897 
3898 		rv->region()->clear_changes ();
3899 		rv->region()->trim_to (start, (end - start));
3900 
3901 		if (!in_command) {
3902 			begin_reversible_command (str);
3903 			in_command = true;
3904 		}
3905 		_session->add_command(new StatefulDiffCommand (rv->region()));
3906 	}
3907 
3908 	if (in_command) {
3909 		commit_reversible_command ();
3910 	}
3911 }
3912 
3913 void
trim_region_to_previous_region_end()3914 Editor::trim_region_to_previous_region_end ()
3915 {
3916 	return trim_to_region(false);
3917 }
3918 
3919 void
trim_region_to_next_region_start()3920 Editor::trim_region_to_next_region_start ()
3921 {
3922 	return trim_to_region(true);
3923 }
3924 
3925 void
trim_to_region(bool forward)3926 Editor::trim_to_region(bool forward)
3927 {
3928 	RegionSelection rs = get_regions_from_selection_and_entered ();
3929 	bool in_command = false;
3930 
3931 	boost::shared_ptr<Region> next_region;
3932 
3933 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3934 
3935 		AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3936 
3937 		if (!arv) {
3938 			continue;
3939 		}
3940 
3941 		AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3942 
3943 		if (!atav) {
3944 			continue;
3945 		}
3946 
3947 		boost::shared_ptr<Region> region = arv->region();
3948 		boost::shared_ptr<Playlist> playlist (region->playlist());
3949 
3950 		region->clear_changes ();
3951 
3952 		if (forward) {
3953 
3954 			next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3955 
3956 			if (!next_region) {
3957 				continue;
3958 			}
3959 
3960 		    region->trim_end (next_region->first_sample() - 1);
3961 		    arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3962 		}
3963 		else {
3964 
3965 			next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3966 
3967 			if (!next_region) {
3968 				continue;
3969 			}
3970 
3971 			region->trim_front (next_region->last_sample() + 1);
3972 			arv->region_changed (ARDOUR::bounds_change);
3973 		}
3974 
3975 		if (!in_command) {
3976 			begin_reversible_command (_("trim to region"));
3977 			in_command = true;
3978 		}
3979 		_session->add_command(new StatefulDiffCommand (region));
3980 	}
3981 
3982 	if (in_command) {
3983 		commit_reversible_command ();
3984 	}
3985 }
3986 
3987 void
unfreeze_route()3988 Editor::unfreeze_route ()
3989 {
3990 	if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3991 		return;
3992 	}
3993 
3994 	clicked_routeview->track()->unfreeze ();
3995 }
3996 
3997 void*
_freeze_thread(void * arg)3998 Editor::_freeze_thread (void* arg)
3999 {
4000 	return static_cast<Editor*>(arg)->freeze_thread ();
4001 }
4002 
4003 void*
freeze_thread()4004 Editor::freeze_thread ()
4005 {
4006 	/* create event pool because we may need to talk to the session */
4007 	SessionEvent::create_per_thread_pool ("freeze events", 64);
4008 	/* create per-thread buffers for process() tree to use */
4009 	clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
4010 	current_interthread_info->done = true;
4011 	return 0;
4012 }
4013 
4014 void
freeze_route()4015 Editor::freeze_route ()
4016 {
4017 	if (!_session) {
4018 		return;
4019 	}
4020 
4021 	/* stop transport before we start. this is important */
4022 	_session->request_stop();
4023 
4024 	/* wait for just a little while, because the above call is asynchronous */
4025 	int timeout = 10;
4026 	do {
4027 		Glib::usleep (_session->engine().usecs_per_cycle ());
4028 	} while (!_session->transport_stopped() && --timeout > 0);
4029 
4030 	if (timeout == 0) {
4031 		ArdourMessageDialog d (
4032 			_("Transport cannot be stopped, likely due to external timecode sync.\n"
4033 			  "Freezing a track requires the transport to be stopped.")
4034 			);
4035 		d.set_title (_("Cannot freeze"));
4036 		d.run ();
4037 		return;
4038 		return;
4039 	}
4040 
4041 	if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
4042 		return;
4043 	}
4044 
4045 	if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
4046 		ArdourMessageDialog d (
4047 			_("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
4048 			  "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
4049 			);
4050 		d.set_title (_("Cannot freeze"));
4051 		d.run ();
4052 		return;
4053 	}
4054 
4055 	if (clicked_routeview->track()->has_external_redirects()) {
4056 		ArdourMessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return/sidechain as part of its signal flow.\n\n"
4057 		                                         "Freezing will only process the signal as far as the first send/insert/return/sidechain."),
4058 		                                       clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
4059 
4060 		d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
4061 		d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
4062 		d.set_title (_("Freeze Limits"));
4063 
4064 		int response = d.run ();
4065 
4066 		switch (response) {
4067 			case Gtk::RESPONSE_OK:
4068 				break;
4069 			default:
4070 				return;
4071 		}
4072 	}
4073 
4074 	InterThreadInfo itt;
4075 	current_interthread_info = &itt;
4076 
4077 	InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
4078 
4079 	pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
4080 
4081 	CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4082 
4083 	while (!itt.done && !itt.cancel) {
4084 		gtk_main_iteration ();
4085 	}
4086 
4087 	pthread_join (itt.thread, 0);
4088 	current_interthread_info = 0;
4089 }
4090 
4091 void
bounce_range_selection(bool replace,bool enable_processing)4092 Editor::bounce_range_selection (bool replace, bool enable_processing)
4093 {
4094 	if (selection->time.empty()) {
4095 		return;
4096 	}
4097 
4098 	string bounce_name;
4099 	if (replace) {
4100 		bounce_name = "Consolidated";
4101 	} else {
4102 		bounce_name = "Bounced";
4103 	}
4104 
4105 	TrackSelection views = selection->tracks;
4106 
4107 	for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4108 
4109 		if (enable_processing) {
4110 
4111 			RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4112 
4113 			if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4114 				ArdourMessageDialog d (
4115 					_("You can't perform this operation because the processing of the signal "
4116 					  "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4117 					  "You can do this without processing, which is a different operation.")
4118 					);
4119 				d.set_title (_("Cannot bounce"));
4120 				d.run ();
4121 				return;
4122 			}
4123 		}
4124 	}
4125 
4126 	/*prompt the user for a new name*/
4127 	{
4128 		ArdourWidgets::Prompter dialog (true);
4129 
4130 		if (replace) {
4131 			dialog.set_prompt (_("Name for Consolidated Region:"));
4132 		} else {
4133 			dialog.set_prompt (_("Name for Bounced Region:"));
4134 		}
4135 
4136 		dialog.set_name ("BounceNameWindow");
4137 		dialog.set_size_request (400, -1);
4138 		dialog.set_position (Gtk::WIN_POS_MOUSE);
4139 
4140 		dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
4141 		dialog.set_initial_text (bounce_name);
4142 
4143 		if (!replace) {
4144 			Label label;
4145 			label.set_text (_("Bounced Range will appear in the Source list."));
4146 			dialog.get_vbox()->set_spacing (8);
4147 			dialog.get_vbox()->pack_start (label);
4148 			label.show();
4149 		}
4150 
4151 		dialog.show ();
4152 
4153 		switch (dialog.run ()) {
4154 		case RESPONSE_ACCEPT:
4155 			break;
4156 		default:
4157 			return;
4158 		}
4159 		dialog.get_result(bounce_name);
4160 	}
4161 
4162 	samplepos_t start = selection->time[clicked_selection].start;
4163 	samplepos_t end = selection->time[clicked_selection].end;
4164 	samplepos_t cnt = end - start + 1;
4165 	bool in_command = false;
4166 
4167 	for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4168 
4169 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4170 
4171 		if (!rtv) {
4172 			continue;
4173 		}
4174 
4175 		boost::shared_ptr<Playlist> playlist;
4176 
4177 		if ((playlist = rtv->playlist()) == 0) {
4178 			continue;
4179 		}
4180 
4181 		InterThreadInfo itt;
4182 
4183 		playlist->clear_changes ();
4184 		playlist->clear_owned_changes ();
4185 
4186 		boost::shared_ptr<Region> r;
4187 
4188 		/*make the "source" (whole-file region)*/
4189 		/*note: bounce_range() will append the playlist name to the resulting region and filename*/
4190 		if (enable_processing) {
4191 			r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false, bounce_name);
4192 		} else {
4193 			r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false, bounce_name);
4194 		}
4195 
4196 		if (!r) {
4197 			continue;
4198 		}
4199 
4200 		if (!in_command) {
4201 			begin_reversible_command (_("bounce range"));
4202 			in_command = true;
4203 		}
4204 
4205 		if (replace) {
4206 			/*remove the edxisting regions under the edit range*/
4207 			list<AudioRange> ranges;
4208 			ranges.push_back (AudioRange (start, start+cnt, 0));
4209 			playlist->cut (ranges); // discard result
4210 
4211 			/*SPECIAL CASE:  we are bouncing to a new Source *AND* replacing the existing range on the timeline  (consolidate)*/
4212 			/*we don't add the whole_file region here; we insert a discrete copy*/
4213 			PropertyList plist;
4214 			plist.add (ARDOUR::Properties::whole_file, false);
4215 			boost::shared_ptr<Region> copy (RegionFactory::create (r, plist));
4216 			playlist->add_region (copy, start);
4217 		}
4218 
4219 		vector<Command*> cmds;
4220 		playlist->rdiff (cmds);
4221 		_session->add_commands (cmds);
4222 
4223 		_session->add_command (new StatefulDiffCommand (playlist));
4224 	}
4225 
4226 	if (in_command && !_session->abort_empty_reversible_command ()) {
4227 		commit_reversible_command ();
4228 	}
4229 }
4230 
4231 /** Delete selected regions, automation points or a time range */
4232 void
delete_()4233 Editor::delete_ ()
4234 {
4235 	//special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4236 	//we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4237 	bool deleted = false;
4238 	if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4239 		deleted = current_mixer_strip->delete_processors ();
4240 
4241 	if (!deleted)
4242 		cut_copy (Delete);
4243 }
4244 
4245 /** Cut selected regions, automation points or a time range */
4246 void
cut()4247 Editor::cut ()
4248 {
4249 	cut_copy (Cut);
4250 }
4251 
4252 /** Copy selected regions, automation points or a time range */
4253 void
copy()4254 Editor::copy ()
4255 {
4256 	cut_copy (Copy);
4257 }
4258 
4259 
4260 /** @return true if a Cut, Copy or Clear is possible */
4261 bool
can_cut_copy() const4262 Editor::can_cut_copy () const
4263 {
4264 	if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4265 		return true;
4266 
4267 	return false;
4268 }
4269 
4270 
4271 /** Cut, copy or clear selected regions, automation points or a time range.
4272  * @param op Operation (Delete, Cut, Copy or Clear)
4273  */
4274 void
cut_copy(CutCopyOp op)4275 Editor::cut_copy (CutCopyOp op)
4276 {
4277 	/* only cancel selection if cut/copy is successful.*/
4278 
4279 	string opname;
4280 
4281 	switch (op) {
4282 	case Delete:
4283 		opname = _("delete");
4284 		break;
4285 	case Cut:
4286 		opname = _("cut");
4287 		break;
4288 	case Copy:
4289 		opname = _("copy");
4290 		break;
4291 	case Clear:
4292 		opname = _("clear");
4293 		break;
4294 	}
4295 
4296 	/* if we're deleting something, and the mouse is still pressed,
4297 	   the thing we started a drag for will be gone when we release
4298 	   the mouse button(s). avoid this. see part 2 at the end of
4299 	   this function.
4300 	*/
4301 
4302 	if (op == Delete || op == Cut || op == Clear) {
4303 		if (_drags->active ()) {
4304 			_drags->abort ();
4305 		}
4306 	}
4307 
4308 	if (op != Delete) { //"Delete" doesn't change copy/paste buf
4309 		cut_buffer->clear ();
4310 	}
4311 
4312 	if (entered_marker) {
4313 
4314 		/* cut/delete op while pointing at a marker */
4315 
4316 		remove_marker (entered_marker);
4317 		entered_marker = NULL;
4318 		_drags->abort ();
4319 		return;
4320 	}
4321 
4322 	switch (mouse_mode) {
4323 	case MouseDraw:
4324 	case MouseContent:
4325 		begin_reversible_command (opname + ' ' + X_("MIDI"));
4326 		cut_copy_midi (op);
4327 		commit_reversible_command ();
4328 		return;
4329 	default:
4330 		break;
4331 	}
4332 
4333 	bool did_edit = false;
4334 
4335 	if (!selection->regions.empty() || !selection->points.empty()) {
4336 		begin_reversible_command (opname + ' ' + _("objects"));
4337 		did_edit = true;
4338 
4339 		if (!selection->regions.empty()) {
4340 			cut_copy_regions (op, selection->regions);
4341 
4342 			if (op == Cut || op == Delete) {
4343 				selection->clear_regions ();
4344 			}
4345 		}
4346 
4347 		if (!selection->points.empty()) {
4348 			cut_copy_points (op);
4349 
4350 			if (op == Cut || op == Delete) {
4351 				selection->clear_points ();
4352 			}
4353 		}
4354 	} else if (selection->time.empty()) {
4355 		samplepos_t start, end;
4356 		/* no time selection, see if we can get an edit range
4357 		   and use that.
4358 		*/
4359 		if (get_edit_op_range (start, end)) {
4360 			selection->set (start, end);
4361 		}
4362 	} else if (!selection->time.empty()) {
4363 		begin_reversible_command (opname + ' ' + _("range"));
4364 
4365 		did_edit = true;
4366 		cut_copy_ranges (op);
4367 
4368 		if (op == Cut || op == Delete) {
4369 			selection->clear_time ();
4370 		}
4371 	}
4372 
4373 	if (did_edit) {
4374 		/* reset repeated paste state */
4375 		paste_count    = 0;
4376 		last_paste_pos = -1;
4377 		commit_reversible_command ();
4378 	}
4379 
4380 	if (op == Delete || op == Cut || op == Clear) {
4381 		_drags->abort ();
4382 	}
4383 }
4384 
4385 
4386 struct AutomationRecord {
AutomationRecordAutomationRecord4387 	AutomationRecord () : state (0) , line(NULL) {}
AutomationRecordAutomationRecord4388 	AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4389 
4390 	XMLNode* state; ///< state before any operation
4391 	const AutomationLine* line; ///< line this came from
4392 	boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4393 };
4394 
4395 struct PointsSelectionPositionSorter {
operator ()PointsSelectionPositionSorter4396 	bool operator() (ControlPoint* a, ControlPoint* b) {
4397 		return (*(a->model()))->when < (*(b->model()))->when;
4398 	}
4399 };
4400 
4401 /** Cut, copy or clear selected automation points.
4402  *  @param op Operation (Cut, Copy or Clear)
4403  */
4404 void
cut_copy_points(Editing::CutCopyOp op,Temporal::Beats earliest,bool midi)4405 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4406 {
4407 	if (selection->points.empty ()) {
4408 		return;
4409 	}
4410 
4411 	/* XXX: not ideal, as there may be more than one track involved in the point selection */
4412 	_last_cut_copy_source_track = &selection->points.front()->line().trackview;
4413 
4414 	/* Keep a record of the AutomationLists that we end up using in this operation */
4415 	typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4416 	Lists lists;
4417 
4418 	/* user could select points in any order */
4419 	selection->points.sort(PointsSelectionPositionSorter ());
4420 
4421 	/* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4422 	for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4423 		const AutomationLine&                   line = (*sel_point)->line();
4424 		const boost::shared_ptr<AutomationList> al   = line.the_list();
4425 		if (lists.find (al) == lists.end ()) {
4426 			/* We haven't seen this list yet, so make a record for it.  This includes
4427 			   taking a copy of its current state, in case this is needed for undo later.
4428 			*/
4429 			lists[al] = AutomationRecord (&al->get_state (), &line);
4430 		}
4431 	}
4432 
4433 	if (op == Cut || op == Copy) {
4434 		/* This operation will involve putting things in the cut buffer, so create an empty
4435 		   ControlList for each of our source lists to put the cut buffer data in.
4436 		*/
4437 		for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4438 			i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4439 		}
4440 
4441 		/* Add all selected points to the relevant copy ControlLists */
4442 		MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4443 		for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4444 			boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
4445 			AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4446 
4447 			lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4448 			if (midi) {
4449 				/* Update earliest MIDI start time in beats */
4450 				earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4451 			} else {
4452 				/* Update earliest session start time in samples */
4453 				start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4454 			}
4455 		}
4456 
4457 		/* Snap start time backwards, so copy/paste is snap aligned. */
4458 		if (midi) {
4459 			if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4460 				earliest = Temporal::Beats();  // Weird... don't offset
4461 			}
4462 			earliest.round_down_to_beat();
4463 		} else {
4464 			if (start.sample == std::numeric_limits<double>::max()) {
4465 				start.sample = 0;  // Weird... don't offset
4466 			}
4467 			snap_to(start, RoundDownMaybe);
4468 		}
4469 
4470 		const double line_offset = midi ? earliest.to_double() : start.sample;
4471 		for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4472 			/* Correct this copy list so that it is relative to the earliest
4473 			   start time, so relative ordering between points is preserved
4474 			   when copying from several lists and the paste starts at the
4475 			   earliest copied piece of data. */
4476 			boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4477 			for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4478 				(*ctrl_evt)->when -= line_offset;
4479 			}
4480 
4481 			/* And add it to the cut buffer */
4482 			cut_buffer->add (al_cpy);
4483 		}
4484 	}
4485 
4486 	if (op == Delete || op == Cut) {
4487 		/* This operation needs to remove things from the main AutomationList, so do that now */
4488 
4489 		for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4490 			i->first->freeze ();
4491 		}
4492 
4493 		/* Remove each selected point from its AutomationList */
4494 		for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4495 			AutomationLine& line = (*sel_point)->line ();
4496 			boost::shared_ptr<AutomationList> al = line.the_list();
4497 
4498 			bool erase = true;
4499 
4500 			if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4501 				/* removing of first and last gain point in region gain lines is prohibited*/
4502 				if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4503 					erase = false;
4504 				}
4505 			}
4506 
4507 			if(erase) {
4508 				al->erase ((*sel_point)->model ());
4509 			}
4510 		}
4511 
4512 		/* Thaw the lists and add undo records for them */
4513 		for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4514 			boost::shared_ptr<AutomationList> al = i->first;
4515 			al->thaw ();
4516 			_session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4517 		}
4518 	}
4519 }
4520 
4521 /** Cut, copy or clear selected automation points.
4522  * @param op Operation (Cut, Copy or Clear)
4523  */
4524 void
cut_copy_midi(CutCopyOp op)4525 Editor::cut_copy_midi (CutCopyOp op)
4526 {
4527 	Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4528 
4529 	MidiRegionSelection ms = selection->midi_regions ();
4530 	cerr << "CCM, mrv = " << ms.size() << endl;
4531 
4532 	for (MidiRegionSelection::iterator i = ms.begin(); i != ms.end(); ++i) {
4533 
4534 		MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
4535 
4536 		if (!mrv->selection().empty()) {
4537 			earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4538 		}
4539 		mrv->cut_copy_clear (op);
4540 
4541 		/* XXX: not ideal, as there may be more than one track involved in the selection */
4542 		_last_cut_copy_source_track = &mrv->get_time_axis_view();
4543 	}
4544 
4545 	if (!selection->points.empty()) {
4546 		cut_copy_points (op, earliest, true);
4547 		if (op == Cut || op == Delete) {
4548 			selection->clear_points ();
4549 		}
4550 	}
4551 }
4552 
4553 struct lt_playlist {
operator ()lt_playlist4554 	bool operator () (const PlaylistState& a, const PlaylistState& b) {
4555 		return a.playlist < b.playlist;
4556 	}
4557 };
4558 
4559 struct PlaylistMapping {
4560 	TimeAxisView* tv;
4561 	boost::shared_ptr<Playlist> pl;
4562 
PlaylistMappingPlaylistMapping4563 	PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4564 };
4565 
4566 /** Remove `clicked_regionview' */
4567 void
remove_clicked_region()4568 Editor::remove_clicked_region ()
4569 {
4570 	if (clicked_routeview == 0 || clicked_regionview == 0) {
4571 		return;
4572 	}
4573 
4574 	begin_reversible_command (_("remove region"));
4575 
4576 	boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4577 	boost::shared_ptr<Region> region = clicked_regionview->region();
4578 
4579 	playlist->clear_changes ();
4580 	playlist->clear_owned_changes ();
4581 	playlist->remove_region (region);
4582 
4583 	if (Config->get_edit_mode() == Ripple) {
4584 		playlist->ripple (region->position(), - region->length(), boost::shared_ptr<Region>());
4585 	}
4586 
4587 	/* We might have removed regions, which alters other regions' layering_index,
4588 	   so we need to do a recursive diff here.
4589 	*/
4590 	vector<Command*> cmds;
4591 	playlist->rdiff (cmds);
4592 	_session->add_commands (cmds);
4593 
4594 	_session->add_command(new StatefulDiffCommand (playlist));
4595 	commit_reversible_command ();
4596 }
4597 
4598 
4599 void
recover_regions(ARDOUR::RegionList regions)4600 Editor::recover_regions (ARDOUR::RegionList regions)
4601 {
4602 #ifdef RECOVER_REGIONS_IS_WORKING
4603 	begin_reversible_command (_("recover regions"));
4604 
4605 	for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
4606 		boost::shared_ptr<ARDOUR::Source> source = (*i)->source();
4607 
4608 		RouteList routes = _session->get_routelist();
4609 		for (RouteList::iterator it = routes.begin(); it != routes.end(); ++it) {
4610 			boost::shared_ptr<ARDOUR::Track> track = boost::dynamic_pointer_cast<Track>(*it);
4611 			if (track) {
4612 				//ToDo
4613 				if (source->captured_for() == track->) {
4614 					//_session->add_command(new StatefulDiffCommand (playlist));
4615 				}
4616 			}
4617 		}
4618 	}
4619 
4620 	commit_reversible_command ();
4621 #endif
4622 }
4623 
4624 
4625 /** Remove the selected regions */
4626 void
remove_selected_regions()4627 Editor::remove_selected_regions ()
4628 {
4629 	RegionSelection rs = get_regions_from_selection_and_entered ();
4630 
4631 	if (!_session || rs.empty()) {
4632 		return;
4633 	}
4634 
4635 	list<boost::shared_ptr<Region> > regions_to_remove;
4636 
4637 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4638 		// we can't just remove the region(s) in this loop because
4639 		// this removes them from the RegionSelection, and they thus
4640 		// disappear from underneath the iterator, and the ++i above
4641 		// SEGVs in a puzzling fashion.
4642 
4643 		// so, first iterate over the regions to be removed from rs and
4644 		// add them to the regions_to_remove list, and then
4645 		// iterate over the list to actually remove them.
4646 
4647 		regions_to_remove.push_back ((*i)->region());
4648 	}
4649 
4650 	vector<boost::shared_ptr<Playlist> > playlists;
4651 
4652 	for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4653 
4654 		boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4655 
4656 		if (!playlist) {
4657 			// is this check necessary?
4658 			continue;
4659 		}
4660 
4661 		/* get_regions_from_selection_and_entered() guarantees that
4662 		   the playlists involved are unique, so there is no need
4663 		   to check here.
4664 		*/
4665 
4666 		playlists.push_back (playlist);
4667 
4668 		playlist->clear_changes ();
4669 		playlist->clear_owned_changes ();
4670 		playlist->freeze ();
4671 		playlist->remove_region (*rl);
4672 		if (Config->get_edit_mode() == Ripple) {
4673 			playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4674 		}
4675 
4676 	}
4677 
4678 	vector<boost::shared_ptr<Playlist> >::iterator pl;
4679 	bool in_command = false;
4680 
4681 	for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4682 		(*pl)->thaw ();
4683 
4684 		/* We might have removed regions, which alters other regions' layering_index,
4685 		   so we need to do a recursive diff here.
4686 		*/
4687 
4688 		if (!in_command) {
4689 			begin_reversible_command (_("remove region"));
4690 			in_command = true;
4691 		}
4692 		vector<Command*> cmds;
4693 		(*pl)->rdiff (cmds);
4694 		_session->add_commands (cmds);
4695 
4696 		_session->add_command(new StatefulDiffCommand (*pl));
4697 	}
4698 
4699 	if (in_command) {
4700 		commit_reversible_command ();
4701 	}
4702 }
4703 
4704 /** Cut, copy or clear selected regions.
4705  * @param op Operation (Cut, Copy or Clear)
4706  */
4707 void
cut_copy_regions(CutCopyOp op,RegionSelection & rs)4708 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4709 {
4710 	/* we can't use a std::map here because the ordering is important, and we can't trivially sort
4711 	   a map when we want ordered access to both elements. i think.
4712 	*/
4713 
4714 	vector<PlaylistMapping> pmap;
4715 
4716 	samplepos_t first_position = max_samplepos;
4717 
4718 	typedef set<boost::shared_ptr<Playlist> > FreezeList;
4719 	FreezeList freezelist;
4720 
4721 	/* get ordering correct before we cut/copy */
4722 
4723 	rs.sort_by_position_and_track ();
4724 
4725 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4726 
4727 		first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4728 
4729 		if (op == Cut || op == Clear || op == Delete) {
4730 			boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4731 
4732 			if (pl) {
4733 				FreezeList::iterator fl;
4734 
4735 				// only take state if this is a new playlist.
4736 				for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4737 					if ((*fl) == pl) {
4738 						break;
4739 					}
4740 				}
4741 
4742 				if (fl == freezelist.end()) {
4743 					pl->clear_changes();
4744 					pl->clear_owned_changes ();
4745 					pl->freeze ();
4746 					freezelist.insert (pl);
4747 				}
4748 			}
4749 		}
4750 
4751 		TimeAxisView* tv = &(*x)->get_time_axis_view();
4752 		vector<PlaylistMapping>::iterator z;
4753 
4754 		for (z = pmap.begin(); z != pmap.end(); ++z) {
4755 			if ((*z).tv == tv) {
4756 				break;
4757 			}
4758 		}
4759 
4760 		if (z == pmap.end()) {
4761 			pmap.push_back (PlaylistMapping (tv));
4762 		}
4763 	}
4764 
4765 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4766 
4767 		boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4768 
4769 		if (!pl) {
4770 			/* region not yet associated with a playlist (e.g. unfinished
4771 			   capture pass.
4772 			*/
4773 			++x;
4774 			continue;
4775 		}
4776 
4777 		TimeAxisView& tv = (*x)->get_time_axis_view();
4778 		boost::shared_ptr<Playlist> npl;
4779 		RegionSelection::iterator tmp;
4780 
4781 		tmp = x;
4782 		++tmp;
4783 
4784 		if (op != Delete) {
4785 
4786 			vector<PlaylistMapping>::iterator z;
4787 
4788 			for (z = pmap.begin(); z != pmap.end(); ++z) {
4789 				if ((*z).tv == &tv) {
4790 					break;
4791 				}
4792 			}
4793 
4794 			assert (z != pmap.end());
4795 
4796 			if (!(*z).pl) {
4797 				npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4798 				npl->freeze();
4799 				(*z).pl = npl;
4800 			} else {
4801 				npl = (*z).pl;
4802 			}
4803 		}
4804 
4805 		boost::shared_ptr<Region> r = (*x)->region();
4806 		boost::shared_ptr<Region> _xx;
4807 
4808 		assert (r != 0);
4809 
4810 		switch (op) {
4811 		case Delete:
4812 			pl->remove_region (r);
4813 			if (Config->get_edit_mode() == Ripple) {
4814 				pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4815 			}
4816 			break;
4817 
4818 		case Cut:
4819 			_xx = RegionFactory::create (r, false);
4820 			npl->add_region (_xx, r->position() - first_position);
4821 			pl->remove_region (r);
4822 			if (Config->get_edit_mode() == Ripple) {
4823 				pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4824 			}
4825 			break;
4826 
4827 		case Copy:
4828 			/* copy region before adding, so we're not putting same object into two different playlists */
4829 			npl->add_region (RegionFactory::create (r, false), r->position() - first_position);
4830 			break;
4831 
4832 		case Clear:
4833 			pl->remove_region (r);
4834 			if (Config->get_edit_mode() == Ripple) {
4835 				pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4836 			}
4837 			break;
4838 		}
4839 
4840 		x = tmp;
4841 	}
4842 
4843 	if (op != Delete) {
4844 
4845 		list<boost::shared_ptr<Playlist> > foo;
4846 
4847 		/* the pmap is in the same order as the tracks in which selected regions occurred */
4848 
4849 		for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4850 			if ((*i).pl) {
4851 				(*i).pl->thaw();
4852 				foo.push_back ((*i).pl);
4853 			}
4854 		}
4855 
4856 		if (!foo.empty()) {
4857 			cut_buffer->set (foo);
4858 		}
4859 
4860 		if (pmap.empty()) {
4861 			_last_cut_copy_source_track = 0;
4862 		} else {
4863 			_last_cut_copy_source_track = pmap.front().tv;
4864 		}
4865 	}
4866 
4867 	for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4868 		(*pl)->thaw ();
4869 
4870 		/* We might have removed regions, which alters other regions' layering_index,
4871 		   so we need to do a recursive diff here.
4872 		*/
4873 		vector<Command*> cmds;
4874 		(*pl)->rdiff (cmds);
4875 		_session->add_commands (cmds);
4876 
4877 		_session->add_command (new StatefulDiffCommand (*pl));
4878 	}
4879 }
4880 
4881 void
cut_copy_ranges(CutCopyOp op)4882 Editor::cut_copy_ranges (CutCopyOp op)
4883 {
4884 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4885 
4886 	/* Sort the track selection now, so that it if is used, the playlists
4887 	   selected by the calls below to cut_copy_clear are in the order that
4888 	   their tracks appear in the editor.  This makes things like paste
4889 	   of ranges work properly.
4890 	*/
4891 
4892 	sort_track_selection (ts);
4893 
4894 	if (ts.empty()) {
4895 		if (!entered_track) {
4896 			return;
4897 		}
4898 		ts.push_back (entered_track);
4899 	}
4900 
4901 	for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4902 		(*i)->cut_copy_clear (*selection, op);
4903 	}
4904 }
4905 
4906 void
paste(float times,bool from_context)4907 Editor::paste (float times, bool from_context)
4908 {
4909 	DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4910 	MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4911 	paste_internal (where.sample, times, 0);
4912 }
4913 
4914 void
mouse_paste()4915 Editor::mouse_paste ()
4916 {
4917 	MusicSample where (0, 0);
4918 	bool ignored;
4919 	if (!mouse_sample (where.sample, ignored)) {
4920 		return;
4921 	}
4922 
4923 	snap_to (where);
4924 	paste_internal (where.sample, 1, where.division);
4925 }
4926 
4927 void
paste_internal(samplepos_t position,float times,const int32_t sub_num)4928 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4929 {
4930 	DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4931 
4932 	if (cut_buffer->empty(internal_editing())) {
4933 		return;
4934 	}
4935 
4936 	if (position == max_samplepos) {
4937 		position = get_preferred_edit_position();
4938 		DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4939 	}
4940 
4941 	if (position != last_paste_pos) {
4942 		/* paste in new location, reset repeated paste state */
4943 		paste_count = 0;
4944 		last_paste_pos = position;
4945 	}
4946 
4947 	/* get everything in the correct order */
4948 
4949 	TrackViewList ts;
4950 	if (!selection->tracks.empty()) {
4951 		/* If there is a track selection, paste into exactly those tracks and
4952 		 * only those tracks.  This allows the user to be explicit and override
4953 		 * the below "do the reasonable thing" logic. */
4954 		ts = selection->tracks.filter_to_unique_playlists ();
4955 		sort_track_selection (ts);
4956 	} else {
4957 		/* Figure out which track to base the paste at. */
4958 		TimeAxisView* base_track = NULL;
4959 		if (_edit_point == Editing::EditAtMouse && entered_track) {
4960 			/* With the mouse edit point, paste onto the track under the mouse. */
4961 			base_track = entered_track;
4962 		} else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4963 			/* With the mouse edit point, paste onto the track of the region under the mouse. */
4964 			base_track = &entered_regionview->get_time_axis_view();
4965 		} else if (_last_cut_copy_source_track) {
4966 			/* Paste to the track that the cut/copy came from (see mantis #333). */
4967 			base_track = _last_cut_copy_source_track;
4968 		} else {
4969 			/* This is "impossible" since we've copied... well, do nothing. */
4970 			return;
4971 		}
4972 
4973 		/* Walk up to parent if necessary, so base track is a route. */
4974 		while (base_track->get_parent()) {
4975 			base_track = base_track->get_parent();
4976 		}
4977 
4978 		/* Add base track and all tracks below it.  The paste logic will select
4979 		   the appropriate object types from the cut buffer in relative order. */
4980 		for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4981 			if ((*i)->order() >= base_track->order()) {
4982 				ts.push_back(*i);
4983 			}
4984 		}
4985 
4986 		/* Sort tracks so the nth track of type T will pick the nth object of type T. */
4987 		sort_track_selection (ts);
4988 
4989 		/* Add automation children of each track in order, for pasting several lines. */
4990 		for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4991 			/* Add any automation children for pasting several lines */
4992 			RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4993 			if (!rtv) {
4994 				continue;
4995 			}
4996 
4997 			typedef RouteTimeAxisView::AutomationTracks ATracks;
4998 			const ATracks& atracks = rtv->automation_tracks();
4999 			for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
5000 				i = ts.insert(i, a->second.get());
5001 				++i;
5002 			}
5003 		}
5004 
5005 		/* We now have a list of trackviews starting at base_track, including
5006 		   automation children, in the order shown in the editor, e.g. R1,
5007 		   R1.A1, R1.A2, R2, R2.A1, ... */
5008 	}
5009 
5010 	begin_reversible_command (Operations::paste);
5011 
5012 	if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
5013 	    dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
5014 	    /* Only one line copied, and one automation track selected.  Do a
5015 	       "greedy" paste from one automation type to another. */
5016 
5017 		PasteContext ctx(paste_count, times, ItemCounts(), true);
5018 		ts.front()->paste (position, *cut_buffer, ctx, sub_num);
5019 
5020 	} else {
5021 
5022 		/* Paste into tracks */
5023 
5024 		PasteContext ctx(paste_count, times, ItemCounts(), false);
5025 		for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5026 			(*i)->paste (position, *cut_buffer, ctx, sub_num);
5027 		}
5028 	}
5029 
5030 	++paste_count;
5031 
5032 	commit_reversible_command ();
5033 }
5034 
5035 void
duplicate_regions(float times)5036 Editor::duplicate_regions (float times)
5037 {
5038 	RegionSelection rs (get_regions_from_selection_and_entered());
5039 	duplicate_some_regions (rs, times);
5040 }
5041 
5042 void
duplicate_some_regions(RegionSelection & regions,float times)5043 Editor::duplicate_some_regions (RegionSelection& regions, float times)
5044 {
5045 	if (regions.empty ()) {
5046 		return;
5047 	}
5048 
5049 	boost::shared_ptr<Playlist> playlist;
5050 	std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
5051 	RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
5052 	RegionSelection foo;
5053 
5054 	samplepos_t const start_sample = regions.start ();
5055 	samplepos_t const end_sample = regions.end_sample ();
5056 	samplecnt_t const span = end_sample - start_sample + 1;
5057 
5058 	begin_reversible_command (Operations::duplicate_region);
5059 
5060 	selection->clear_regions ();
5061 
5062 	/* ripple first so that we don't move the duplicates that will be added */
5063 
5064 	if (Config->get_edit_mode() == Ripple) {
5065 
5066 		/* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
5067 
5068 		RegionList exclude;
5069 
5070 		for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
5071 			exclude.push_back ((*i)->region());
5072 			playlist = (*i)->region()->playlist();
5073 			if (playlists.insert (playlist).second) {
5074 				/* successfully inserted into set, so it's the first time we've seen this playlist */
5075 				playlist->clear_changes ();
5076 				playlist->clear_owned_changes ();
5077 			}
5078 		}
5079 
5080 		for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
5081 			(*p)->ripple (start_sample, span * times, &exclude);
5082 		}
5083 	}
5084 
5085 	for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
5086 
5087 		boost::shared_ptr<Region> r ((*i)->region());
5088 
5089 		TimeAxisView& tv = (*i)->get_time_axis_view();
5090 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
5091 		latest_regionviews.clear ();
5092 		sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
5093 
5094 		samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
5095 		playlist = (*i)->region()->playlist();
5096 
5097 		if (Config->get_edit_mode() != Ripple) {
5098 			if (playlists.insert (playlist).second) {
5099 				playlist->clear_changes ();
5100 				playlist->clear_owned_changes ();
5101 			}
5102 		}
5103 
5104 		playlist->duplicate (r, position, span, times);
5105 
5106 		c.disconnect ();
5107 
5108 		foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
5109 	}
5110 
5111 	for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
5112 		_session->add_command (new StatefulDiffCommand (*p));
5113 		vector<Command*> cmds;
5114 		(*p)->rdiff (cmds);
5115 		_session->add_commands (cmds);
5116 	}
5117 
5118 	if (!foo.empty()) {
5119 		selection->set (foo);
5120 	}
5121 
5122 	commit_reversible_command ();
5123 }
5124 
5125 void
duplicate_selection(float times)5126 Editor::duplicate_selection (float times)
5127 {
5128 	if (selection->time.empty() || selection->tracks.empty()) {
5129 		return;
5130 	}
5131 
5132 	boost::shared_ptr<Playlist> playlist;
5133 
5134 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5135 
5136 	bool in_command = false;
5137 
5138 	for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5139 		if ((playlist = (*i)->playlist()) == 0) {
5140 			continue;
5141 		}
5142 		playlist->clear_changes ();
5143 
5144 		if (clicked_selection) {
5145 			playlist->duplicate_range (selection->time[clicked_selection], times);
5146 		} else {
5147 			playlist->duplicate_ranges (selection->time, times);
5148 		}
5149 
5150 		if (!in_command) {
5151 			begin_reversible_command (_("duplicate range selection"));
5152 			in_command = true;
5153 		}
5154 		_session->add_command (new StatefulDiffCommand (playlist));
5155 
5156 	}
5157 
5158 	if (in_command) {
5159 		if (times == 1.0f) {
5160 			// now "move" range selection to after the current range selection
5161 			samplecnt_t distance = 0;
5162 
5163 			if (clicked_selection) {
5164 				distance =
5165 				    selection->time[clicked_selection].end - selection->time[clicked_selection].start;
5166 			} else {
5167 				distance = selection->time.end_sample () - selection->time.start ();
5168 			}
5169 
5170 			selection->move_time (distance);
5171 		}
5172 		commit_reversible_command ();
5173 	}
5174 }
5175 
5176 /** Reset all selected points to the relevant default value */
5177 void
reset_point_selection()5178 Editor::reset_point_selection ()
5179 {
5180 	for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
5181 		ARDOUR::AutomationList::iterator j = (*i)->model ();
5182 		(*j)->value = (*i)->line().the_list()->descriptor ().normal;
5183 	}
5184 }
5185 
5186 void
center_playhead()5187 Editor::center_playhead ()
5188 {
5189 	float const page = _visible_canvas_width * samples_per_pixel;
5190 	center_screen_internal (_playhead_cursor->current_sample (), page);
5191 }
5192 
5193 void
center_edit_point()5194 Editor::center_edit_point ()
5195 {
5196 	float const page = _visible_canvas_width * samples_per_pixel;
5197 	center_screen_internal (get_preferred_edit_position(), page);
5198 }
5199 
5200 /** Caller must begin and commit a reversible command */
5201 void
clear_playlist(boost::shared_ptr<Playlist> playlist)5202 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5203 {
5204 	playlist->clear_changes ();
5205 	playlist->clear ();
5206 	_session->add_command (new StatefulDiffCommand (playlist));
5207 }
5208 
5209 void
nudge_track(bool use_edit,bool forwards)5210 Editor::nudge_track (bool use_edit, bool forwards)
5211 {
5212 	boost::shared_ptr<Playlist> playlist;
5213 	samplepos_t distance;
5214 	samplepos_t next_distance;
5215 	samplepos_t start;
5216 
5217 	if (use_edit) {
5218 		start = get_preferred_edit_position();
5219 	} else {
5220 		start = 0;
5221 	}
5222 
5223 	if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5224 		return;
5225 	}
5226 
5227 	if (selection->tracks.empty()) {
5228 		return;
5229 	}
5230 
5231 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5232 	bool in_command = false;
5233 
5234 	for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5235 
5236 		if ((playlist = (*i)->playlist()) == 0) {
5237 			continue;
5238 		}
5239 
5240 		playlist->clear_changes ();
5241 		playlist->clear_owned_changes ();
5242 
5243 		playlist->nudge_after (start, distance, forwards);
5244 
5245 		if (!in_command) {
5246 			begin_reversible_command (_("nudge track"));
5247 			in_command = true;
5248 		}
5249 		vector<Command*> cmds;
5250 
5251 		playlist->rdiff (cmds);
5252 		_session->add_commands (cmds);
5253 
5254 		_session->add_command (new StatefulDiffCommand (playlist));
5255 	}
5256 
5257 	if (in_command) {
5258 		commit_reversible_command ();
5259 	}
5260 }
5261 
5262 void
remove_last_capture()5263 Editor::remove_last_capture ()
5264 {
5265 	vector<string> choices;
5266 	string prompt;
5267 
5268 	if (!_session) {
5269 		return;
5270 	}
5271 
5272 	if (Config->get_verify_remove_last_capture()) {
5273 		prompt  = _("Do you really want to destroy the last capture?"
5274 		            "\n(This is destructive and cannot be undone)");
5275 
5276 		choices.push_back (_("No, do nothing."));
5277 		choices.push_back (_("Yes, destroy it."));
5278 
5279 		Choice prompter (_("Destroy last capture"), prompt, choices);
5280 
5281 		if (prompter.run () == 1) {
5282 			_session->remove_last_capture ();
5283 			_regions->redisplay ();
5284 		}
5285 
5286 	} else {
5287 		_session->remove_last_capture();
5288 		_regions->redisplay ();
5289 	}
5290 }
5291 
5292 void
tag_regions(RegionList regions)5293 Editor::tag_regions (RegionList regions)
5294 {
5295 	ArdourDialog d (_("Tag Last Capture"), true, false);
5296 	Entry entry;
5297 	Label label (_("Tag:"));
5298 	HBox hbox;
5299 
5300 	hbox.set_spacing (6);
5301 	hbox.pack_start (label, false, false);
5302 	hbox.pack_start (entry, true, true);
5303 
5304 	d.get_vbox()->set_border_width (12);
5305 	d.get_vbox()->pack_start (hbox, false, false);
5306 
5307 	d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
5308 	d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
5309 
5310 	d.set_size_request (300, -1);
5311 
5312 	entry.set_text (_("Good"));
5313 	entry.select_region (0, -1);
5314 
5315 	entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
5316 
5317 	d.show_all ();
5318 
5319 	entry.grab_focus();
5320 
5321 	int const ret = d.run();
5322 
5323 	d.hide ();
5324 
5325 	if (ret != RESPONSE_OK) {
5326 		return;
5327 	}
5328 
5329 	std::string tagstr = entry.get_text();
5330 	strip_whitespace_edges (tagstr);
5331 
5332 	if (!tagstr.empty()) {
5333 		for (RegionList::iterator r = regions.begin(); r != regions.end(); r++) {
5334 			(*r)->set_tags(tagstr);
5335 		}
5336 
5337 		_regions->redisplay ();
5338 	}
5339 }
5340 
5341 void
tag_selected_region()5342 Editor::tag_selected_region ()
5343 {
5344 	std::list<boost::shared_ptr<Region> > rlist;
5345 
5346 	RegionSelection rs = get_regions_from_selection_and_entered ();
5347 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); r++) {
5348 		rlist.push_back((*r)->region());
5349 	}
5350 
5351 	tag_regions(rlist);
5352 }
5353 
5354 void
tag_last_capture()5355 Editor::tag_last_capture ()
5356 {
5357 	if (!_session) {
5358 		return;
5359 	}
5360 
5361 	std::list<boost::shared_ptr<Region> > rlist;
5362 
5363 	std::list<boost::shared_ptr<Source> > srcs;
5364 	_session->get_last_capture_sources (srcs);
5365 	for (std::list<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
5366 		boost::shared_ptr<ARDOUR::Source> source = (*i);
5367 		if (source) {
5368 
5369 			set<boost::shared_ptr<Region> > regions;
5370 			RegionFactory::get_regions_using_source (source, regions);
5371 			for (set<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); r++) {
5372 				rlist.push_back(*r);
5373 			}
5374 
5375 		}
5376 	}
5377 
5378 	tag_regions(rlist);
5379 }
5380 
5381 void
normalize_region()5382 Editor::normalize_region ()
5383 {
5384 	if (!_session) {
5385 		return;
5386 	}
5387 
5388 	RegionSelection rs = get_regions_from_selection_and_entered ();
5389 
5390 	if (rs.empty()) {
5391 		return;
5392 	}
5393 
5394 	NormalizeDialog dialog (rs.size() > 1);
5395 
5396 	if (dialog.run () != RESPONSE_ACCEPT) {
5397 		return;
5398 	}
5399 
5400 	CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5401 	gdk_flush ();
5402 
5403 	/* XXX: should really only count audio regions here */
5404 	int const regions = rs.size ();
5405 
5406 	/* Make a list of the selected audio regions' maximum amplitudes, and also
5407 	   obtain the maximum amplitude of them all.
5408 	*/
5409 	list<double> max_amps;
5410 	list<double> rms_vals;
5411 	double max_amp = 0;
5412 	double max_rms = 0;
5413 	bool use_rms = dialog.constrain_rms ();
5414 
5415 	for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5416 		AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5417 		if (!arv) {
5418 			continue;
5419 		}
5420 		dialog.descend (1.0 / regions);
5421 		double const a = arv->audio_region()->maximum_amplitude (&dialog);
5422 		if (use_rms) {
5423 			double r = arv->audio_region()->rms (&dialog);
5424 			max_rms = max (max_rms, r);
5425 			rms_vals.push_back (r);
5426 		}
5427 
5428 		if (a == -1) {
5429 			/* the user cancelled the operation */
5430 			return;
5431 		}
5432 
5433 		max_amps.push_back (a);
5434 		max_amp = max (max_amp, a);
5435 		dialog.ascend ();
5436 	}
5437 
5438 	list<double>::const_iterator a = max_amps.begin ();
5439 	list<double>::const_iterator l = rms_vals.begin ();
5440 	bool in_command = false;
5441 
5442 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5443 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5444 		if (!arv) {
5445 			continue;
5446 		}
5447 
5448 		arv->region()->clear_changes ();
5449 
5450 		double amp = dialog.normalize_individually() ? *a : max_amp;
5451 		double target = dialog.target_peak (); // dB
5452 
5453 		if (use_rms) {
5454 			double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5455 			const double t_rms = dialog.target_rms ();
5456 			const gain_t c_peak = dB_to_coefficient (target);
5457 			const gain_t c_rms  = dB_to_coefficient (t_rms);
5458 			if ((amp_rms / c_rms) > (amp / c_peak)) {
5459 				amp = amp_rms;
5460 				target = t_rms;
5461 			}
5462 		}
5463 
5464 		arv->audio_region()->normalize (amp, target);
5465 
5466 		if (!in_command) {
5467 			begin_reversible_command (_("normalize"));
5468 			in_command = true;
5469 		}
5470 		_session->add_command (new StatefulDiffCommand (arv->region()));
5471 
5472 		++a;
5473 		++l;
5474 	}
5475 
5476 	if (in_command) {
5477 		commit_reversible_command ();
5478 	}
5479 }
5480 
5481 
5482 void
reset_region_scale_amplitude()5483 Editor::reset_region_scale_amplitude ()
5484 {
5485 	if (!_session) {
5486 		return;
5487 	}
5488 
5489 	RegionSelection rs = get_regions_from_selection_and_entered ();
5490 
5491 	if (rs.empty()) {
5492 		return;
5493 	}
5494 
5495 	bool in_command = false;
5496 
5497 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5498 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5499 		if (!arv)
5500 			continue;
5501 		arv->region()->clear_changes ();
5502 		arv->audio_region()->set_scale_amplitude (1.0f);
5503 
5504 		if(!in_command) {
5505 				begin_reversible_command ("reset gain");
5506 				in_command = true;
5507 		}
5508 		_session->add_command (new StatefulDiffCommand (arv->region()));
5509 	}
5510 
5511 	if (in_command) {
5512 		commit_reversible_command ();
5513 	}
5514 }
5515 
5516 void
adjust_region_gain(bool up)5517 Editor::adjust_region_gain (bool up)
5518 {
5519 	RegionSelection rs = get_regions_from_selection_and_entered ();
5520 
5521 	if (!_session || rs.empty()) {
5522 		return;
5523 	}
5524 
5525 	bool in_command = false;
5526 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5527 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5528 		if (arv) {
5529 			arv->region()->playlist()->freeze ();
5530 		}
5531 	}
5532 
5533 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5534 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5535 		if (!arv) {
5536 			continue;
5537 		}
5538 
5539 		arv->region()->clear_changes ();
5540 
5541 		double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5542 
5543 		if (up) {
5544 			dB += 1;
5545 		} else {
5546 			dB -= 1;
5547 		}
5548 
5549 		arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5550 
5551 		if (!in_command) {
5552 				begin_reversible_command ("adjust region gain");
5553 				in_command = true;
5554 		}
5555 		_session->add_command (new StatefulDiffCommand (arv->region()));
5556 	}
5557 
5558 	if (in_command) {
5559 		commit_reversible_command ();
5560 	}
5561 
5562 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5563 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5564 		if (arv) {
5565 			arv->region()->playlist()->thaw ();
5566 		}
5567 	}
5568 }
5569 
5570 void
reset_region_gain()5571 Editor::reset_region_gain ()
5572 {
5573 	RegionSelection rs = get_regions_from_selection_and_entered ();
5574 
5575 	if (!_session || rs.empty()) {
5576 		return;
5577 	}
5578 
5579 	bool in_command = false;
5580 
5581 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5582 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5583 		if (!arv) {
5584 			continue;
5585 		}
5586 
5587 		arv->region()->clear_changes ();
5588 
5589 		arv->audio_region()->set_scale_amplitude (1.0f);
5590 
5591 		if (!in_command) {
5592 				begin_reversible_command ("reset region gain");
5593 				in_command = true;
5594 		}
5595 		_session->add_command (new StatefulDiffCommand (arv->region()));
5596 	}
5597 
5598 	if (in_command) {
5599 		commit_reversible_command ();
5600 	}
5601 }
5602 
5603 void
reverse_region()5604 Editor::reverse_region ()
5605 {
5606 	if (!_session) {
5607 		return;
5608 	}
5609 
5610 	Reverse rev (*_session);
5611 	apply_filter (rev, _("reverse regions"));
5612 }
5613 
5614 void
strip_region_silence()5615 Editor::strip_region_silence ()
5616 {
5617 	if (!_session) {
5618 		return;
5619 	}
5620 
5621 	RegionSelection rs = get_regions_from_selection_and_entered ();
5622 
5623 	if (rs.empty()) {
5624 		return;
5625 	}
5626 
5627 	std::list<RegionView*> audio_only;
5628 
5629 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5630 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5631 		if (arv) {
5632 			audio_only.push_back (arv);
5633 		}
5634 	}
5635 
5636 	assert (!audio_only.empty());
5637 
5638 	StripSilenceDialog d (_session, audio_only);
5639 	int const r = d.run ();
5640 
5641 	d.drop_rects ();
5642 
5643 	if (r == Gtk::RESPONSE_OK) {
5644 		ARDOUR::AudioIntervalMap silences;
5645 		d.silences (silences);
5646 		StripSilence s (*_session, silences, d.fade_length());
5647 
5648 		apply_filter (s, _("strip silence"), &d);
5649 	}
5650 }
5651 
5652 Command*
apply_midi_note_edit_op_to_region(MidiOperator & op,MidiRegionView & mrv)5653 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5654 {
5655 	Evoral::Sequence<Temporal::Beats>::Notes selected;
5656 	mrv.selection_as_notelist (selected, true);
5657 
5658 	vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5659 	v.push_back (selected);
5660 
5661 	Temporal::Beats pos_beats  = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5662 
5663 	return op (mrv.midi_region()->model(), pos_beats, v);
5664 }
5665 
5666 void
apply_midi_note_edit_op(MidiOperator & op,const RegionSelection & rs)5667 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5668 {
5669 	if (rs.empty()) {
5670 		return;
5671 	}
5672 
5673 	bool in_command = false;
5674 
5675 	vector<MidiRegionView*> views = filter_to_unique_midi_region_views (rs);
5676 
5677 	for (vector<MidiRegionView*>::iterator mrv = views.begin(); mrv != views.end(); ++mrv) {
5678 
5679 		Command* cmd = apply_midi_note_edit_op_to_region (op, **mrv);
5680 		if (cmd) {
5681 			if (!in_command) {
5682 				begin_reversible_command (op.name ());
5683 				in_command = true;
5684 			}
5685 			(*cmd)();
5686 			_session->add_command (cmd);
5687 			}
5688 	}
5689 
5690 	if (in_command) {
5691 		commit_reversible_command ();
5692 		_session->set_dirty ();
5693 	}
5694 }
5695 
5696 void
fork_region()5697 Editor::fork_region ()
5698 {
5699 	RegionSelection rs = get_regions_from_selection_and_entered ();
5700 
5701 	if (rs.empty()) {
5702 		return;
5703 	}
5704 
5705 	CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5706 	bool in_command = false;
5707 
5708 	gdk_flush ();
5709 
5710 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5711 		RegionSelection::iterator tmp = r;
5712 		++tmp;
5713 
5714 		MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5715 
5716 		if (mrv) {
5717 			try {
5718 				boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5719 				boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5720 				boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5721 
5722 				if (!in_command) {
5723 					begin_reversible_command (_("Fork Region(s)"));
5724 					in_command = true;
5725 				}
5726 				playlist->clear_changes ();
5727 				playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5728 				_session->add_command(new StatefulDiffCommand (playlist));
5729 			} catch (...) {
5730 				error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5731 			}
5732 		}
5733 
5734 		r = tmp;
5735 	}
5736 
5737 	if (in_command) {
5738 		commit_reversible_command ();
5739 	}
5740 }
5741 
5742 void
quantize_region()5743 Editor::quantize_region ()
5744 {
5745 	if (_session) {
5746 		quantize_regions(get_regions_from_selection_and_entered ());
5747 	}
5748 }
5749 
5750 void
quantize_regions(const RegionSelection & rs)5751 Editor::quantize_regions (const RegionSelection& rs)
5752 {
5753 	if (rs.n_midi_regions() == 0) {
5754 		return;
5755 	}
5756 
5757 	if (!quantize_dialog) {
5758 		quantize_dialog = new QuantizeDialog (*this);
5759 	}
5760 
5761 	if (quantize_dialog->is_mapped()) {
5762 		/* in progress already */
5763 		return;
5764 	}
5765 
5766 	quantize_dialog->present ();
5767 	const int r = quantize_dialog->run ();
5768 	quantize_dialog->hide ();
5769 
5770 	if (r == Gtk::RESPONSE_OK) {
5771 		Quantize quant (quantize_dialog->snap_start(),
5772 		                quantize_dialog->snap_end(),
5773 				quantize_dialog->start_grid_size(),
5774 		                quantize_dialog->end_grid_size(),
5775 				quantize_dialog->strength(),
5776 		                quantize_dialog->swing(),
5777 		                quantize_dialog->threshold());
5778 
5779 		apply_midi_note_edit_op (quant, rs);
5780 	}
5781 }
5782 
5783 void
legatize_region(bool shrink_only)5784 Editor::legatize_region (bool shrink_only)
5785 {
5786 	if (_session) {
5787 		legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5788 	}
5789 }
5790 
5791 void
legatize_regions(const RegionSelection & rs,bool shrink_only)5792 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5793 {
5794 	if (rs.n_midi_regions() == 0) {
5795 		return;
5796 	}
5797 
5798 	Legatize legatize(shrink_only);
5799 	apply_midi_note_edit_op (legatize, rs);
5800 }
5801 
5802 void
transform_region()5803 Editor::transform_region ()
5804 {
5805 	if (_session) {
5806 		transform_regions(get_regions_from_selection_and_entered ());
5807 	}
5808 }
5809 
5810 void
transform_regions(const RegionSelection & rs)5811 Editor::transform_regions (const RegionSelection& rs)
5812 {
5813 	if (rs.n_midi_regions() == 0) {
5814 		return;
5815 	}
5816 
5817 	TransformDialog td;
5818 
5819 	td.present();
5820 	const int r = td.run();
5821 	td.hide();
5822 
5823 	if (r == Gtk::RESPONSE_OK) {
5824 		Transform transform(td.get());
5825 		apply_midi_note_edit_op(transform, rs);
5826 	}
5827 }
5828 
5829 void
transpose_region()5830 Editor::transpose_region ()
5831 {
5832 	if (_session) {
5833 		transpose_regions(get_regions_from_selection_and_entered ());
5834 	}
5835 }
5836 
5837 void
transpose_regions(const RegionSelection & rs)5838 Editor::transpose_regions (const RegionSelection& rs)
5839 {
5840 	if (rs.n_midi_regions() == 0) {
5841 		return;
5842 	}
5843 
5844 	TransposeDialog d;
5845 	int const r = d.run ();
5846 
5847 	if (r == RESPONSE_ACCEPT) {
5848 		Transpose transpose(d.semitones ());
5849 		apply_midi_note_edit_op (transpose, rs);
5850 	}
5851 }
5852 
5853 void
insert_patch_change(bool from_context)5854 Editor::insert_patch_change (bool from_context)
5855 {
5856 	RegionSelection rs = get_regions_from_selection_and_entered ();
5857 
5858 	if (rs.empty ()) {
5859 		return;
5860 	}
5861 
5862 	const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5863 
5864 	/* XXX: bit of a hack; use the MIDNAM from the first selected region;
5865 	   there may be more than one, but the PatchChangeDialog can only offer
5866 	   one set of patch menus.
5867 	*/
5868 	MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5869 
5870 	Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5871 	PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5872 
5873 	switch (d.run()) {
5874 		case Gtk::RESPONSE_ACCEPT:
5875 			break;
5876 		default:
5877 			return;
5878 	}
5879 
5880 	for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5881 		MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5882 		if (mrv) {
5883 			if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5884 				mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5885 			}
5886 		}
5887 	}
5888 }
5889 
5890 void
apply_filter(Filter & filter,string command,ProgressReporter * progress)5891 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5892 {
5893 	RegionSelection rs = get_regions_from_selection_and_entered ();
5894 
5895 	if (rs.empty()) {
5896 		return;
5897 	}
5898 
5899 	CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5900 	bool in_command = false;
5901 
5902 	gdk_flush ();
5903 
5904 	int n = 0;
5905 	int const N = rs.size ();
5906 
5907 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5908 		RegionSelection::iterator tmp = r;
5909 		++tmp;
5910 
5911 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5912 		if (arv) {
5913 			boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5914 
5915 			if (progress) {
5916 				progress->descend (1.0 / N);
5917 			}
5918 
5919 			if (arv->audio_region()->apply (filter, progress) == 0) {
5920 
5921 				playlist->clear_changes ();
5922 				playlist->clear_owned_changes ();
5923 
5924 				if (!in_command) {
5925 					begin_reversible_command (command);
5926 					in_command = true;
5927 				}
5928 
5929 				if (filter.results.empty ()) {
5930 
5931 					/* no regions returned; remove the old one */
5932 					playlist->remove_region (arv->region ());
5933 
5934 				} else {
5935 
5936 					std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5937 
5938 					/* first region replaces the old one */
5939 					playlist->replace_region (arv->region(), *res, (*res)->position());
5940 					++res;
5941 
5942 					/* add the rest */
5943 					while (res != filter.results.end()) {
5944 						playlist->add_region (*res, (*res)->position());
5945 						++res;
5946 					}
5947 
5948 				}
5949 
5950 				/* We might have removed regions, which alters other regions' layering_index,
5951 				   so we need to do a recursive diff here.
5952 				*/
5953 				vector<Command*> cmds;
5954 				playlist->rdiff (cmds);
5955 				_session->add_commands (cmds);
5956 
5957 				_session->add_command(new StatefulDiffCommand (playlist));
5958 			}
5959 
5960 			if (progress) {
5961 				progress->ascend ();
5962 			}
5963 		}
5964 
5965 		r = tmp;
5966 		++n;
5967 	}
5968 
5969 	if (in_command) {
5970 		commit_reversible_command ();
5971 	}
5972 }
5973 
5974 void
external_edit_region()5975 Editor::external_edit_region ()
5976 {
5977 	/* more to come */
5978 }
5979 
5980 void
reset_region_gain_envelopes()5981 Editor::reset_region_gain_envelopes ()
5982 {
5983 	RegionSelection rs = get_regions_from_selection_and_entered ();
5984 
5985 	if (!_session || rs.empty()) {
5986 		return;
5987 	}
5988 
5989 	bool in_command = false;
5990 
5991 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5992 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5993 		if (arv) {
5994 			boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5995 			XMLNode& before (alist->get_state());
5996 
5997 			arv->audio_region()->set_default_envelope ();
5998 
5999 			if (!in_command) {
6000 				begin_reversible_command (_("reset region gain"));
6001 				in_command = true;
6002 			}
6003 			_session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
6004 		}
6005 	}
6006 
6007 	if (in_command) {
6008 		commit_reversible_command ();
6009 	}
6010 }
6011 
6012 void
set_region_gain_visibility(RegionView * rv)6013 Editor::set_region_gain_visibility (RegionView* rv)
6014 {
6015 	AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
6016 	if (arv) {
6017 		arv->update_envelope_visibility();
6018 	}
6019 }
6020 
6021 void
set_gain_envelope_visibility()6022 Editor::set_gain_envelope_visibility ()
6023 {
6024 	if (!_session) {
6025 		return;
6026 	}
6027 
6028 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6029 		AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6030 		if (v) {
6031 			v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
6032 		}
6033 	}
6034 }
6035 
6036 void
toggle_gain_envelope_active()6037 Editor::toggle_gain_envelope_active ()
6038 {
6039 	if (_ignore_region_action) {
6040 		return;
6041 	}
6042 
6043 	RegionSelection rs = get_regions_from_selection_and_entered ();
6044 
6045 	if (!_session || rs.empty()) {
6046 		return;
6047 	}
6048 
6049 	bool in_command = false;
6050 
6051 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6052 		AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
6053 		if (arv) {
6054 			arv->region()->clear_changes ();
6055 			arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
6056 
6057 			if (!in_command) {
6058 				begin_reversible_command (_("region gain envelope active"));
6059 				in_command = true;
6060 			}
6061 			_session->add_command (new StatefulDiffCommand (arv->region()));
6062 		}
6063 	}
6064 
6065 	if (in_command) {
6066 		commit_reversible_command ();
6067 	}
6068 }
6069 
6070 void
toggle_region_lock()6071 Editor::toggle_region_lock ()
6072 {
6073 	if (_ignore_region_action) {
6074 		return;
6075 	}
6076 
6077 	RegionSelection rs = get_regions_from_selection_and_entered ();
6078 
6079 	if (!_session || rs.empty()) {
6080 		return;
6081 	}
6082 
6083 	begin_reversible_command (_("toggle region lock"));
6084 
6085 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6086 		(*i)->region()->clear_changes ();
6087 		(*i)->region()->set_locked (!(*i)->region()->locked());
6088 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
6089 	}
6090 
6091 	commit_reversible_command ();
6092 }
6093 
6094 void
toggle_region_video_lock()6095 Editor::toggle_region_video_lock ()
6096 {
6097 	if (_ignore_region_action) {
6098 		return;
6099 	}
6100 
6101 	RegionSelection rs = get_regions_from_selection_and_entered ();
6102 
6103 	if (!_session || rs.empty()) {
6104 		return;
6105 	}
6106 
6107 	begin_reversible_command (_("Toggle Video Lock"));
6108 
6109 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6110 		(*i)->region()->clear_changes ();
6111 		(*i)->region()->set_video_locked (!(*i)->region()->video_locked());
6112 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
6113 	}
6114 
6115 	commit_reversible_command ();
6116 }
6117 
6118 void
toggle_region_lock_style()6119 Editor::toggle_region_lock_style ()
6120 {
6121 	if (_ignore_region_action) {
6122 		return;
6123 	}
6124 
6125 	RegionSelection rs = get_regions_from_selection_and_entered ();
6126 
6127 	if (!_session || rs.empty()) {
6128 		return;
6129 	}
6130 
6131 	Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
6132 	vector<Widget*> proxies = a->get_proxies();
6133 	Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
6134 
6135 	assert (cmi);
6136 
6137 	begin_reversible_command (_("toggle region lock style"));
6138 
6139 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6140 		(*i)->region()->clear_changes ();
6141 		PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
6142 		(*i)->region()->set_position_lock_style (ns);
6143 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
6144 	}
6145 
6146 	commit_reversible_command ();
6147 }
6148 
6149 void
toggle_opaque_region()6150 Editor::toggle_opaque_region ()
6151 {
6152 	if (_ignore_region_action) {
6153 		return;
6154 	}
6155 
6156 	RegionSelection rs = get_regions_from_selection_and_entered ();
6157 
6158 	if (!_session || rs.empty()) {
6159 		return;
6160 	}
6161 
6162 	begin_reversible_command (_("change region opacity"));
6163 
6164 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6165 		(*i)->region()->clear_changes ();
6166 		(*i)->region()->set_opaque (!(*i)->region()->opaque());
6167 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
6168 	}
6169 
6170 	commit_reversible_command ();
6171 }
6172 
6173 void
toggle_record_enable()6174 Editor::toggle_record_enable ()
6175 {
6176 	bool new_state = false;
6177 	bool first = true;
6178 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6179 		RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
6180 		if (!rtav)
6181 			continue;
6182 		if (!rtav->is_track())
6183 			continue;
6184 
6185 		if (first) {
6186 			new_state = !rtav->track()->rec_enable_control()->get_value();
6187 			first = false;
6188 		}
6189 
6190 		rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
6191 	}
6192 }
6193 
6194 StripableList
tracklist_to_stripables(TrackViewList list)6195 tracklist_to_stripables (TrackViewList list)
6196 {
6197 	StripableList ret;
6198 
6199 	for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
6200 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
6201 
6202 		if (rtv && rtv->is_track()) {
6203 			ret.push_back (rtv->track());
6204 		}
6205 	}
6206 
6207 	return ret;
6208 }
6209 
6210 void
play_solo_selection(bool restart)6211 Editor::play_solo_selection (bool restart)
6212 {
6213 	//note: session::solo_selection takes care of invalidating the region playlist
6214 
6215 	if ((!selection->tracks.empty()) && selection->time.length() > 0) {  //a range is selected; solo the tracks and roll
6216 
6217 		StripableList sl = tracklist_to_stripables (selection->tracks);
6218 		_session->solo_selection (sl, true);
6219 
6220 		if (restart) {
6221 			samplepos_t start = selection->time.start();
6222 			samplepos_t end = selection->time.end_sample();
6223 			_session->request_bounded_roll (start, end);
6224 		}
6225 	} else if (! selection->tracks.empty()) {  //no range is selected, but tracks are selected; solo the tracks and roll
6226 		StripableList sl = tracklist_to_stripables (selection->tracks);
6227 		_session->solo_selection (sl, true);
6228 		_session->request_cancel_play_range();
6229 		transition_to_rolling (true);
6230 
6231 	} else if (! selection->regions.empty()) {  //solo any tracks with selected regions, and roll
6232 		StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
6233 		_session->solo_selection (sl, true);
6234 		_session->request_cancel_play_range();
6235 		transition_to_rolling (true);
6236 	} else {
6237 		_session->request_cancel_play_range();
6238 		transition_to_rolling (true);  //no selection.  just roll.
6239 	}
6240 }
6241 
6242 void
toggle_solo()6243 Editor::toggle_solo ()
6244 {
6245 	bool new_state = false;
6246 	bool first = true;
6247 	boost::shared_ptr<ControlList> cl (new ControlList);
6248 
6249 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6250 		StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6251 
6252 		if (!stav || !stav->stripable()->solo_control()) {
6253 			continue;
6254 		}
6255 
6256 		if (first) {
6257 			new_state = !stav->stripable()->solo_control()->soloed ();
6258 			first = false;
6259 		}
6260 
6261 		cl->push_back (stav->stripable()->solo_control());
6262 	}
6263 
6264 	_session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
6265 }
6266 
6267 void
toggle_mute()6268 Editor::toggle_mute ()
6269 {
6270 	bool new_state = false;
6271 	bool first = true;
6272 	boost::shared_ptr<ControlList> cl (new ControlList);
6273 
6274 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
6275 		StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
6276 
6277 		if (!stav || !stav->stripable()->mute_control()) {
6278 			continue;
6279 		}
6280 
6281 		if (first) {
6282 			new_state = !stav->stripable()->mute_control()->muted();
6283 			first = false;
6284 		}
6285 
6286 		boost::shared_ptr<MuteControl> mc = stav->stripable()->mute_control();
6287 		cl->push_back (mc);
6288 		mc->start_touch (_session->audible_sample ());
6289 	}
6290 
6291 	_session->set_controls (cl, new_state, Controllable::UseGroup);
6292 }
6293 
6294 void
toggle_solo_isolate()6295 Editor::toggle_solo_isolate ()
6296 {
6297 }
6298 
6299 
6300 void
fade_range()6301 Editor::fade_range ()
6302 {
6303 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6304 
6305 	begin_reversible_command (_("fade range"));
6306 
6307 	for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
6308 		(*i)->fade_range (selection->time);
6309 	}
6310 
6311 	commit_reversible_command ();
6312 }
6313 
6314 
6315 void
set_fade_length(bool in)6316 Editor::set_fade_length (bool in)
6317 {
6318 	RegionSelection rs = get_regions_from_selection_and_entered ();
6319 
6320 	if (rs.empty()) {
6321 		return;
6322 	}
6323 
6324 	/* we need a region to measure the offset from the start */
6325 
6326 	RegionView* rv = rs.front ();
6327 
6328 	samplepos_t pos = get_preferred_edit_position();
6329 	samplepos_t len;
6330 	char const * cmd;
6331 
6332 	if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
6333 		/* edit point is outside the relevant region */
6334 		return;
6335 	}
6336 
6337 	if (in) {
6338 		if (pos <= rv->region()->position()) {
6339 			/* can't do it */
6340 			return;
6341 		}
6342 		len = pos - rv->region()->position();
6343 		cmd = _("set fade in length");
6344 	} else {
6345 		if (pos >= rv->region()->last_sample()) {
6346 			/* can't do it */
6347 			return;
6348 		}
6349 		len = rv->region()->last_sample() - pos;
6350 		cmd = _("set fade out length");
6351 	}
6352 
6353 	bool in_command = false;
6354 
6355 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6356 		AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6357 
6358 		if (!tmp) {
6359 			continue;
6360 		}
6361 
6362 		boost::shared_ptr<AutomationList> alist;
6363 		if (in) {
6364 			alist = tmp->audio_region()->fade_in();
6365 		} else {
6366 			alist = tmp->audio_region()->fade_out();
6367 		}
6368 
6369 		XMLNode &before = alist->get_state();
6370 
6371 		if (in) {
6372 			tmp->audio_region()->set_fade_in_length (len);
6373 			tmp->audio_region()->set_fade_in_active (true);
6374 		} else {
6375 			tmp->audio_region()->set_fade_out_length (len);
6376 			tmp->audio_region()->set_fade_out_active (true);
6377 		}
6378 
6379 		if (!in_command) {
6380 			begin_reversible_command (cmd);
6381 			in_command = true;
6382 		}
6383 		XMLNode &after = alist->get_state();
6384 		_session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6385 	}
6386 
6387 	if (in_command) {
6388 		commit_reversible_command ();
6389 	}
6390 }
6391 
6392 void
set_fade_in_shape(FadeShape shape)6393 Editor::set_fade_in_shape (FadeShape shape)
6394 {
6395 	RegionSelection rs = get_regions_from_selection_and_entered ();
6396 
6397 	if (rs.empty()) {
6398 		return;
6399 	}
6400 	bool in_command = false;
6401 
6402 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6403 		AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6404 
6405 		if (!tmp) {
6406 			continue;
6407 		}
6408 
6409 		boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6410 		XMLNode &before = alist->get_state();
6411 
6412 		tmp->audio_region()->set_fade_in_shape (shape);
6413 
6414 		if (!in_command) {
6415 			begin_reversible_command (_("set fade in shape"));
6416 			in_command = true;
6417 		}
6418 		XMLNode &after = alist->get_state();
6419 		_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6420 	}
6421 
6422 	if (in_command) {
6423 		commit_reversible_command ();
6424 	}
6425 }
6426 
6427 void
set_fade_out_shape(FadeShape shape)6428 Editor::set_fade_out_shape (FadeShape shape)
6429 {
6430 	RegionSelection rs = get_regions_from_selection_and_entered ();
6431 
6432 	if (rs.empty()) {
6433 		return;
6434 	}
6435 	bool in_command = false;
6436 
6437 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6438 		AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6439 
6440 		if (!tmp) {
6441 			continue;
6442 		}
6443 
6444 		boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6445 		XMLNode &before = alist->get_state();
6446 
6447 		tmp->audio_region()->set_fade_out_shape (shape);
6448 
6449 		if(!in_command) {
6450 			begin_reversible_command (_("set fade out shape"));
6451 			in_command = true;
6452 		}
6453 		XMLNode &after = alist->get_state();
6454 		_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6455 	}
6456 
6457 	if (in_command) {
6458 		commit_reversible_command ();
6459 	}
6460 }
6461 
6462 void
set_fade_in_active(bool yn)6463 Editor::set_fade_in_active (bool yn)
6464 {
6465 	RegionSelection rs = get_regions_from_selection_and_entered ();
6466 
6467 	if (rs.empty()) {
6468 		return;
6469 	}
6470 	bool in_command = false;
6471 
6472 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6473 		AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6474 
6475 		if (!tmp) {
6476 			continue;
6477 		}
6478 
6479 
6480 		boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6481 
6482 		ar->clear_changes ();
6483 		ar->set_fade_in_active (yn);
6484 
6485 		if (!in_command) {
6486 			begin_reversible_command (_("set fade in active"));
6487 			in_command = true;
6488 		}
6489 		_session->add_command (new StatefulDiffCommand (ar));
6490 	}
6491 
6492 	if (in_command) {
6493 		commit_reversible_command ();
6494 	}
6495 }
6496 
6497 void
set_fade_out_active(bool yn)6498 Editor::set_fade_out_active (bool yn)
6499 {
6500 	RegionSelection rs = get_regions_from_selection_and_entered ();
6501 
6502 	if (rs.empty()) {
6503 		return;
6504 	}
6505 	bool in_command = false;
6506 
6507 	for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6508 		AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6509 
6510 		if (!tmp) {
6511 			continue;
6512 		}
6513 
6514 		boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6515 
6516 		ar->clear_changes ();
6517 		ar->set_fade_out_active (yn);
6518 
6519 		if (!in_command) {
6520 			begin_reversible_command (_("set fade out active"));
6521 			in_command = true;
6522 		}
6523 		_session->add_command(new StatefulDiffCommand (ar));
6524 	}
6525 
6526 	if (in_command) {
6527 		commit_reversible_command ();
6528 	}
6529 }
6530 
6531 void
toggle_region_fades(int dir)6532 Editor::toggle_region_fades (int dir)
6533 {
6534 	if (_ignore_region_action) {
6535 		return;
6536 	}
6537 
6538 	boost::shared_ptr<AudioRegion> ar;
6539 	bool yn = false;
6540 
6541 	RegionSelection rs = get_regions_from_selection_and_entered ();
6542 
6543 	if (rs.empty()) {
6544 		return;
6545 	}
6546 
6547 	RegionSelection::iterator i;
6548 	for (i = rs.begin(); i != rs.end(); ++i) {
6549 		if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6550 			if (dir == -1) {
6551 				yn = ar->fade_out_active ();
6552 			} else {
6553 				yn = ar->fade_in_active ();
6554 			}
6555 			break;
6556 		}
6557 	}
6558 
6559 	if (i == rs.end()) {
6560 		return;
6561 	}
6562 
6563 	/* XXX should this undo-able? */
6564 	bool in_command = false;
6565 
6566 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6567 		if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6568 			continue;
6569 		}
6570 		ar->clear_changes ();
6571 
6572 		if (dir == 1 || dir == 0) {
6573 			ar->set_fade_in_active (!yn);
6574 		}
6575 
6576 		if (dir == -1 || dir == 0) {
6577 			ar->set_fade_out_active (!yn);
6578 		}
6579 		if (!in_command) {
6580 			begin_reversible_command (_("toggle fade active"));
6581 			in_command = true;
6582 		}
6583 		_session->add_command(new StatefulDiffCommand (ar));
6584 	}
6585 
6586 	if (in_command) {
6587 		commit_reversible_command ();
6588 	}
6589 }
6590 
6591 
6592 /** Update region fade visibility after its configuration has been changed */
6593 void
update_region_fade_visibility()6594 Editor::update_region_fade_visibility ()
6595 {
6596 	bool _fade_visibility = _session->config.get_show_region_fades ();
6597 
6598 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6599 		AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6600 		if (v) {
6601 			if (_fade_visibility) {
6602 				v->audio_view()->show_all_fades ();
6603 			} else {
6604 				v->audio_view()->hide_all_fades ();
6605 			}
6606 		}
6607 	}
6608 }
6609 
6610 void
set_edit_point()6611 Editor::set_edit_point ()
6612 {
6613 	bool ignored;
6614 	MusicSample where (0, 0);
6615 
6616 	if (!mouse_sample (where.sample, ignored)) {
6617 		return;
6618 	}
6619 
6620 	snap_to (where);
6621 
6622 	if (selection->markers.empty()) {
6623 
6624 		mouse_add_new_marker (where.sample);
6625 
6626 	} else {
6627 		bool ignored;
6628 
6629 		Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6630 
6631 		if (loc) {
6632 			loc->move_to (where.sample, where.division);
6633 		}
6634 	}
6635 }
6636 
6637 void
set_playhead_cursor()6638 Editor::set_playhead_cursor ()
6639 {
6640 	if (entered_marker) {
6641 		_session->request_locate (entered_marker->position());
6642 	} else {
6643 		MusicSample where (0, 0);
6644 		bool ignored;
6645 
6646 		if (!mouse_sample (where.sample, ignored)) {
6647 			return;
6648 		}
6649 
6650 		snap_to (where);
6651 
6652 		if (_session) {
6653 			_session->request_locate (where.sample);
6654 		}
6655 	}
6656 
6657 //not sure what this was for;  remove it for now.
6658 //	if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6659 //		cancel_time_selection();
6660 //	}
6661 
6662 }
6663 
6664 void
split_region()6665 Editor::split_region ()
6666 {
6667 	if (_dragging_playhead) {
6668 		/*continue*/
6669 	} else if (_drags->active ()) {
6670 		/*any other kind of drag, bail out so we avoid Undo snafu*/
6671 		return;
6672 	}
6673 
6674 	//if a range is selected, separate it
6675 	if (!selection->time.empty()) {
6676 		separate_regions_between (selection->time);
6677 		return;
6678 	}
6679 
6680 	//if no range was selected, try to find some regions to split
6681 	if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) {  //don't try this for Internal Edit, Stretch, Draw, etc.
6682 
6683 		RegionSelection rs;
6684 
6685 		//new behavior:  the Split action will prioritize the entered_regionview rather than selected regions.
6686 		//this fixes the unexpected case where you point at a region, but
6687 		//  * nothing happens OR
6688 		//  * some other region (maybe off-screen) is split.
6689 		//NOTE:  if the entered_regionview is /part of the selection/ then we should operate on the selection as usual
6690 		if (_edit_point == EditAtMouse && entered_regionview && !entered_regionview->selected()) {
6691 			rs.add (entered_regionview);
6692 		} else {
6693 			rs = selection->regions;   //might be empty
6694 		}
6695 
6696 		if (rs.empty()) {
6697 			TrackViewList tracks = selection->tracks;
6698 
6699 			if (!tracks.empty()) {
6700 				/* no region selected or entered, but some selected tracks:
6701 				 * act on all regions on the selected tracks at the edit point
6702 				 */
6703 				samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, false);
6704 				get_regions_at(rs, where, tracks);
6705 			}
6706 		}
6707 
6708 		const samplepos_t pos = get_preferred_edit_position();
6709 		const int32_t division = get_grid_music_divisions (0);
6710 		MusicSample where (pos, division);
6711 
6712 		if (rs.empty()) {
6713 			return;
6714 		}
6715 
6716 		split_regions_at (where, rs);
6717 	}
6718 }
6719 
6720 void
select_next_stripable(bool routes_only)6721 Editor::select_next_stripable (bool routes_only)
6722 {
6723 	_session->selection().select_next_stripable (false, routes_only);
6724 }
6725 
6726 void
select_prev_stripable(bool routes_only)6727 Editor::select_prev_stripable (bool routes_only)
6728 {
6729 	_session->selection().select_prev_stripable (false, routes_only);
6730 }
6731 
6732 void
set_loop_from_selection(bool play)6733 Editor::set_loop_from_selection (bool play)
6734 {
6735 	if (_session == 0) {
6736 		return;
6737 	}
6738 
6739 	samplepos_t start, end;
6740 
6741 	if (!get_selection_extents (start, end)) {
6742 		return;
6743 	}
6744 
6745 	set_loop_range (start, end,  _("set loop range from selection"));
6746 
6747 	if (play) {
6748 		_session->request_play_loop (true, true);
6749 	}
6750 }
6751 
6752 void
set_loop_from_region(bool play)6753 Editor::set_loop_from_region (bool play)
6754 {
6755 	samplepos_t start, end;
6756 	if (!get_selection_extents (start, end))
6757 		return;
6758 
6759 	set_loop_range (start, end, _("set loop range from region"));
6760 
6761 	if (play) {
6762 		_session->request_play_loop (true);
6763 	}
6764 }
6765 
6766 void
set_punch_from_selection()6767 Editor::set_punch_from_selection ()
6768 {
6769 	if (_session == 0) {
6770 		return;
6771 	}
6772 
6773 	samplepos_t start, end;
6774 	if (!get_selection_extents (start, end))
6775 		return;
6776 
6777 	set_punch_range (start, end,  _("set punch range from selection"));
6778 }
6779 
6780 void
set_auto_punch_range()6781 Editor::set_auto_punch_range ()
6782 {
6783 	// auto punch in/out button from a single button
6784 	// If Punch In is unset, set punch range from playhead to end, enable punch in
6785 	// If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6786 	//   rewound beyond the Punch In marker, in which case that marker will be moved back
6787 	//   to the current playhead position.
6788 	// If punch out is set, it clears the punch range and Punch In/Out buttons
6789 
6790 	if (_session == 0) {
6791 		return;
6792 	}
6793 
6794 	Location* tpl = transport_punch_location();
6795 	samplepos_t now = _playhead_cursor->current_sample();
6796 	samplepos_t begin = now;
6797 	samplepos_t end = _session->current_end_sample();
6798 
6799 	if (!_session->config.get_punch_in()) {
6800 		// First Press - set punch in and create range from here to eternity
6801 		set_punch_range (begin, end, _("Auto Punch In"));
6802 		_session->config.set_punch_in(true);
6803 	} else if (tpl && !_session->config.get_punch_out()) {
6804 		// Second press - update end range marker and set punch_out
6805 		if (now < tpl->start()) {
6806 			// playhead has been rewound - move start back  and pretend nothing happened
6807 			begin = now;
6808 			set_punch_range (begin, end, _("Auto Punch In/Out"));
6809 		} else {
6810 			// normal case for 2nd press - set the punch out
6811 			end = _playhead_cursor->current_sample ();
6812 			set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6813 			_session->config.set_punch_out(true);
6814 		}
6815 	} else {
6816 		if (_session->config.get_punch_out()) {
6817 			_session->config.set_punch_out(false);
6818 		}
6819 
6820 		if (_session->config.get_punch_in()) {
6821 			_session->config.set_punch_in(false);
6822 		}
6823 
6824 		if (tpl)
6825 		{
6826 			// third press - unset punch in/out and remove range
6827 			_session->locations()->remove(tpl);
6828 		}
6829 	}
6830 
6831 }
6832 
6833 void
set_session_extents_from_selection()6834 Editor::set_session_extents_from_selection ()
6835 {
6836 	if (_session == 0) {
6837 		return;
6838 	}
6839 
6840 	samplepos_t start, end;
6841 	if (!get_selection_extents (start, end))
6842 		return;
6843 
6844 	Location* loc;
6845 	if ((loc = _session->locations()->session_range_location()) == 0) {
6846 		_session->set_session_extents (start, end);  // this will create a new session range;  no need for UNDO
6847 	} else {
6848 		XMLNode &before = loc->get_state();
6849 
6850 		_session->set_session_extents (start, end);
6851 
6852 		XMLNode &after = loc->get_state();
6853 
6854 		begin_reversible_command (_("set session start/end from selection"));
6855 
6856 		_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6857 
6858 		commit_reversible_command ();
6859 	}
6860 
6861 	_session->set_session_range_is_free (false);
6862 }
6863 
6864 void
set_punch_start_from_edit_point()6865 Editor::set_punch_start_from_edit_point ()
6866 {
6867 	if (_session) {
6868 
6869 		MusicSample start (0, 0);
6870 		samplepos_t end = max_samplepos;
6871 
6872 		//use the existing punch end, if any
6873 		Location* tpl = transport_punch_location();
6874 		if (tpl) {
6875 			end = tpl->end();
6876 		}
6877 
6878 		if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6879 			start.sample = _session->audible_sample();
6880 		} else {
6881 			start.sample = get_preferred_edit_position();
6882 		}
6883 
6884 		//if there's not already a sensible selection endpoint, go "forever"
6885 		if (start.sample > end) {
6886 			end = max_samplepos;
6887 		}
6888 
6889 		set_punch_range (start.sample, end, _("set punch start from EP"));
6890 	}
6891 
6892 }
6893 
6894 void
set_punch_end_from_edit_point()6895 Editor::set_punch_end_from_edit_point ()
6896 {
6897 	if (_session) {
6898 
6899 		samplepos_t start = 0;
6900 		MusicSample end (max_samplepos, 0);
6901 
6902 		//use the existing punch start, if any
6903 		Location* tpl = transport_punch_location();
6904 		if (tpl) {
6905 			start = tpl->start();
6906 		}
6907 
6908 		if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6909 			end.sample = _session->audible_sample();
6910 		} else {
6911 			end.sample = get_preferred_edit_position();
6912 		}
6913 
6914 		set_punch_range (start, end.sample, _("set punch end from EP"));
6915 
6916 	}
6917 }
6918 
6919 void
set_loop_start_from_edit_point()6920 Editor::set_loop_start_from_edit_point ()
6921 {
6922 	if (_session) {
6923 
6924 		MusicSample start (0, 0);
6925 		samplepos_t end = max_samplepos;
6926 
6927 		//use the existing loop end, if any
6928 		Location* tpl = transport_loop_location();
6929 		if (tpl) {
6930 			end = tpl->end();
6931 		}
6932 
6933 		if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6934 			start.sample = _session->audible_sample();
6935 		} else {
6936 			start.sample = get_preferred_edit_position();
6937 		}
6938 
6939 		//if there's not already a sensible selection endpoint, go "forever"
6940 		if (start.sample > end) {
6941 			end = max_samplepos;
6942 		}
6943 
6944 		set_loop_range (start.sample, end, _("set loop start from EP"));
6945 	}
6946 
6947 }
6948 
6949 void
set_loop_end_from_edit_point()6950 Editor::set_loop_end_from_edit_point ()
6951 {
6952 	if (_session) {
6953 
6954 		samplepos_t start = 0;
6955 		MusicSample end (max_samplepos, 0);
6956 
6957 		//use the existing loop start, if any
6958 		Location* tpl = transport_loop_location();
6959 		if (tpl) {
6960 			start = tpl->start();
6961 		}
6962 
6963 		if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6964 			end.sample = _session->audible_sample();
6965 		} else {
6966 			end.sample = get_preferred_edit_position();
6967 		}
6968 
6969 		set_loop_range (start, end.sample, _("set loop end from EP"));
6970 	}
6971 }
6972 
6973 void
set_punch_from_region()6974 Editor::set_punch_from_region ()
6975 {
6976 	samplepos_t start, end;
6977 	if (!get_selection_extents (start, end))
6978 		return;
6979 
6980 	set_punch_range (start, end, _("set punch range from region"));
6981 }
6982 
6983 void
pitch_shift_region()6984 Editor::pitch_shift_region ()
6985 {
6986 	RegionSelection rs = get_regions_from_selection_and_entered ();
6987 
6988 	RegionSelection audio_rs;
6989 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6990 		if (dynamic_cast<AudioRegionView*> (*i)) {
6991 			audio_rs.push_back (*i);
6992 		}
6993 	}
6994 
6995 	if (audio_rs.empty()) {
6996 		return;
6997 	}
6998 
6999 	pitch_shift (audio_rs, 1.2);
7000 }
7001 
7002 void
set_tempo_from_region()7003 Editor::set_tempo_from_region ()
7004 {
7005 	RegionSelection rs = get_regions_from_selection_and_entered ();
7006 
7007 	if (!_session || rs.empty()) {
7008 		return;
7009 	}
7010 
7011 	RegionView* rv = rs.front();
7012 
7013 	define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
7014 }
7015 
7016 void
use_range_as_bar()7017 Editor::use_range_as_bar ()
7018 {
7019 	samplepos_t start, end;
7020 	if (get_edit_op_range (start, end)) {
7021 		define_one_bar (start, end);
7022 	}
7023 }
7024 
7025 void
define_one_bar(samplepos_t start,samplepos_t end)7026 Editor::define_one_bar (samplepos_t start, samplepos_t end)
7027 {
7028 	samplepos_t length = end - start;
7029 
7030 	const Meter& m (_session->tempo_map().meter_at_sample (start));
7031 
7032 	/* length = 1 bar */
7033 
7034 	/* We're going to deliver a constant tempo here,
7035 	   so we can use samples per beat to determine length.
7036 	   now we want samples per beat.
7037 	   we have samples per bar, and beats per bar, so ...
7038 	*/
7039 
7040 	/* XXXX METER MATH */
7041 
7042 	double samples_per_beat = length / m.divisions_per_bar();
7043 
7044 	/* beats per minute = */
7045 
7046 	double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
7047 
7048 	/* now decide whether to:
7049 
7050 	    (a) set global tempo
7051 	    (b) add a new tempo marker
7052 
7053 	*/
7054 
7055 	const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
7056 
7057 	bool do_global = false;
7058 
7059 	if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
7060 
7061 		/* only 1 tempo & 1 meter: ask if the user wants to set the tempo
7062 		   at the start, or create a new marker
7063 		*/
7064 
7065 		vector<string> options;
7066 		options.push_back (_("Cancel"));
7067 		options.push_back (_("Add new marker"));
7068 		options.push_back (_("Set global tempo"));
7069 
7070 		Choice c (
7071 			_("Define one bar"),
7072 			_("Do you want to set the global tempo or add a new tempo marker?"),
7073 			options
7074 			);
7075 
7076 		c.set_default_response (2);
7077 
7078 		switch (c.run()) {
7079 		case 0:
7080 			return;
7081 
7082 		case 2:
7083 			do_global = true;
7084 			break;
7085 
7086 		default:
7087 			do_global = false;
7088 		}
7089 
7090 	} else {
7091 
7092 		/* more than 1 tempo and/or meter section already, go ahead do the "usual":
7093 		   if the marker is at the region starter, change it, otherwise add
7094 		   a new tempo marker
7095 		*/
7096 	}
7097 
7098 	begin_reversible_command (_("set tempo from region"));
7099 	XMLNode& before (_session->tempo_map().get_state());
7100 
7101 	if (do_global) {
7102 		_session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
7103 	} else if (t.sample() == start) {
7104 		_session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
7105 	} else {
7106 		/* constant tempo */
7107 		const Tempo tempo (beats_per_minute, t.note_type());
7108 		_session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
7109 	}
7110 
7111 	XMLNode& after (_session->tempo_map().get_state());
7112 
7113 	_session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7114 	commit_reversible_command ();
7115 }
7116 
7117 void
split_region_at_transients()7118 Editor::split_region_at_transients ()
7119 {
7120 	AnalysisFeatureList positions;
7121 
7122 	RegionSelection rs = get_regions_from_selection_and_entered ();
7123 
7124 	if (!_session || rs.empty()) {
7125 		return;
7126 	}
7127 
7128 	begin_reversible_command (_("split regions"));
7129 
7130 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
7131 
7132 		RegionSelection::iterator tmp;
7133 
7134 		tmp = i;
7135 		++tmp;
7136 
7137 		boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
7138 
7139 		if (ar) {
7140 			ar->transients (positions);
7141 			split_region_at_points ((*i)->region(), positions, true);
7142 			positions.clear ();
7143 		}
7144 
7145 		i = tmp;
7146 	}
7147 
7148 	commit_reversible_command ();
7149 
7150 }
7151 
7152 void
split_region_at_points(boost::shared_ptr<Region> r,AnalysisFeatureList & positions,bool can_ferret,bool select_new)7153 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
7154 {
7155 	bool use_rhythmic_rodent = false;
7156 
7157 	boost::shared_ptr<Playlist> pl = r->playlist();
7158 
7159 	list<boost::shared_ptr<Region> > new_regions;
7160 
7161 	if (!pl) {
7162 		return;
7163 	}
7164 
7165 	if (positions.empty()) {
7166 		return;
7167 	}
7168 
7169 	if (positions.size() > 20 && can_ferret) {
7170 		std::string msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), r->name(), positions.size() + 1);
7171 		ArdourMessageDialog msg (msgstr,
7172 		                         false,
7173 		                         Gtk::MESSAGE_INFO,
7174 		                         Gtk::BUTTONS_OK_CANCEL);
7175 
7176 		if (can_ferret) {
7177 			msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
7178 			msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
7179 		} else {
7180 			msg.set_secondary_text (_("Press OK to continue with this split operation"));
7181 		}
7182 
7183 		msg.set_title (_("Excessive split?"));
7184 		int response = msg.run();
7185 		msg.hide ();
7186 
7187 		switch (response) {
7188 		case RESPONSE_OK:
7189 			break;
7190 		case RESPONSE_APPLY:
7191 			use_rhythmic_rodent = true;
7192 			break;
7193 		default:
7194 			return;
7195 		}
7196 	}
7197 
7198 	if (use_rhythmic_rodent) {
7199 		show_rhythm_ferret ();
7200 		return;
7201 	}
7202 
7203 	AnalysisFeatureList::const_iterator x;
7204 
7205 	pl->clear_changes ();
7206 	pl->clear_owned_changes ();
7207 
7208 	x = positions.begin();
7209 
7210 	if (x == positions.end()) {
7211 		return;
7212 	}
7213 
7214 	pl->freeze ();
7215 	pl->remove_region (r);
7216 
7217 	samplepos_t pos = 0;
7218 
7219 	samplepos_t rstart = r->first_sample ();
7220 	samplepos_t rend = r->last_sample ();
7221 
7222 	while (x != positions.end()) {
7223 
7224 		/* deal with positons that are out of scope of present region bounds */
7225 		if (*x <= rstart || *x > rend) {
7226 			++x;
7227 			continue;
7228 		}
7229 
7230 		/* file start = original start + how far we from the initial position ?  */
7231 
7232 		samplepos_t file_start = r->start() + pos;
7233 
7234 		/* length = next position - current position */
7235 
7236 		samplepos_t len = (*x) - pos - rstart;
7237 
7238 		/* XXX we do we really want to allow even single-sample regions?
7239 		 * shouldn't we have some kind of lower limit on region size?
7240 		 */
7241 
7242 		if (len <= 0) {
7243 			break;
7244 		}
7245 
7246 		string new_name;
7247 
7248 		if (RegionFactory::region_name (new_name, r->name())) {
7249 			break;
7250 		}
7251 
7252 		/* do NOT announce new regions 1 by one, just wait till they are all done */
7253 
7254 		PropertyList plist;
7255 
7256 		plist.add (ARDOUR::Properties::start, file_start);
7257 		plist.add (ARDOUR::Properties::length, len);
7258 		plist.add (ARDOUR::Properties::name, new_name);
7259 		plist.add (ARDOUR::Properties::layer, 0);
7260 		// TODO set transients_offset
7261 
7262 		boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7263 		/* because we set annouce to false, manually add the new region to the
7264 		 * RegionFactory map
7265 		 */
7266 		RegionFactory::map_add (nr);
7267 
7268 		pl->add_region (nr, rstart + pos);
7269 
7270 		if (select_new) {
7271 			new_regions.push_front(nr);
7272 		}
7273 
7274 		pos += len;
7275 		++x;
7276 	}
7277 
7278 	string new_name;
7279 
7280 	RegionFactory::region_name (new_name, r->name());
7281 
7282 	/* Add the final region */
7283 	PropertyList plist;
7284 
7285 	plist.add (ARDOUR::Properties::start, r->start() + pos);
7286 	plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
7287 	plist.add (ARDOUR::Properties::name, new_name);
7288 	plist.add (ARDOUR::Properties::layer, 0);
7289 
7290 	boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
7291 	/* because we set annouce to false, manually add the new region to the
7292 	   RegionFactory map
7293 	*/
7294 	RegionFactory::map_add (nr);
7295 	pl->add_region (nr, r->position() + pos);
7296 
7297 	if (select_new) {
7298 		new_regions.push_front(nr);
7299 	}
7300 
7301 	pl->thaw ();
7302 
7303 	/* We might have removed regions, which alters other regions' layering_index,
7304 	   so we need to do a recursive diff here.
7305 	*/
7306 	vector<Command*> cmds;
7307 	pl->rdiff (cmds);
7308 	_session->add_commands (cmds);
7309 
7310 	_session->add_command (new StatefulDiffCommand (pl));
7311 
7312 	if (select_new) {
7313 
7314 		for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7315 			set_selected_regionview_from_region_list ((*i), Selection::Add);
7316 		}
7317 	}
7318 }
7319 
7320 void
place_transient()7321 Editor::place_transient()
7322 {
7323 	if (!_session) {
7324 		return;
7325 	}
7326 
7327 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
7328 
7329 	if (rs.empty()) {
7330 		return;
7331 	}
7332 
7333 	samplepos_t where = get_preferred_edit_position();
7334 
7335 	begin_reversible_command (_("place transient"));
7336 
7337 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7338 		(*r)->region()->add_transient(where);
7339 	}
7340 
7341 	commit_reversible_command ();
7342 }
7343 
7344 void
remove_transient(ArdourCanvas::Item * item)7345 Editor::remove_transient(ArdourCanvas::Item* item)
7346 {
7347 	if (!_session) {
7348 		return;
7349 	}
7350 
7351 	ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7352 	assert (_line);
7353 
7354 	AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7355 	_arv->remove_transient (*(float*) _line->get_data ("position"));
7356 }
7357 
7358 void
snap_regions_to_grid()7359 Editor::snap_regions_to_grid ()
7360 {
7361 	list <boost::shared_ptr<Playlist > > used_playlists;
7362 
7363 	RegionSelection rs = get_regions_from_selection_and_entered ();
7364 
7365 	if (!_session || rs.empty()) {
7366 		return;
7367 	}
7368 
7369 	begin_reversible_command (_("snap regions to grid"));
7370 
7371 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7372 
7373 		boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7374 
7375 		if (!pl->frozen()) {
7376 			/* we haven't seen this playlist before */
7377 
7378 			/* remember used playlists so we can thaw them later */
7379 			used_playlists.push_back(pl);
7380 			pl->freeze();
7381 		}
7382 		(*r)->region()->clear_changes ();
7383 
7384 		MusicSample start ((*r)->region()->first_sample (), 0);
7385 		snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
7386 		(*r)->region()->set_position (start.sample, start.division);
7387 		_session->add_command(new StatefulDiffCommand ((*r)->region()));
7388 	}
7389 
7390 	while (used_playlists.size() > 0) {
7391 		list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7392 		(*i)->thaw();
7393 		used_playlists.pop_front();
7394 	}
7395 
7396 	commit_reversible_command ();
7397 }
7398 
7399 void
close_region_gaps()7400 Editor::close_region_gaps ()
7401 {
7402 	list <boost::shared_ptr<Playlist > > used_playlists;
7403 
7404 	RegionSelection rs = get_regions_from_selection_and_entered ();
7405 
7406 	if (!_session || rs.empty()) {
7407 		return;
7408 	}
7409 
7410 	Dialog dialog (_("Close Region Gaps"));
7411 
7412 	Table table (2, 3);
7413 	table.set_spacings (12);
7414 	table.set_border_width (12);
7415 	Label* l = manage (left_aligned_label (_("Crossfade length")));
7416 	table.attach (*l, 0, 1, 0, 1);
7417 
7418 	SpinButton spin_crossfade (1, 0);
7419 	spin_crossfade.set_range (0, 15);
7420 	spin_crossfade.set_increments (1, 1);
7421 	spin_crossfade.set_value (5);
7422 	table.attach (spin_crossfade, 1, 2, 0, 1);
7423 
7424 	table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7425 
7426 	l = manage (left_aligned_label (_("Pull-back length")));
7427 	table.attach (*l, 0, 1, 1, 2);
7428 
7429 	SpinButton spin_pullback (1, 0);
7430 	spin_pullback.set_range (0, 100);
7431 	spin_pullback.set_increments (1, 1);
7432 	spin_pullback.set_value(30);
7433 	table.attach (spin_pullback, 1, 2, 1, 2);
7434 
7435 	table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7436 
7437 	dialog.get_vbox()->pack_start (table);
7438 	dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7439 	dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7440 	dialog.show_all ();
7441 
7442 	switch (dialog.run ()) {
7443 		case Gtk::RESPONSE_ACCEPT:
7444 		case Gtk::RESPONSE_OK:
7445 			break;
7446 		default:
7447 			return;
7448 	}
7449 
7450 	samplepos_t crossfade_len = spin_crossfade.get_value();
7451 	samplepos_t pull_back_samples = spin_pullback.get_value();
7452 
7453 	crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7454 	pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7455 
7456 	/* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7457 
7458 	begin_reversible_command (_("close region gaps"));
7459 
7460 	int idx = 0;
7461 	boost::shared_ptr<Region> last_region;
7462 
7463 	rs.sort_by_position_and_track();
7464 
7465 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7466 
7467 		boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7468 
7469 		if (!pl->frozen()) {
7470 			/* we haven't seen this playlist before */
7471 
7472 			/* remember used playlists so we can thaw them later */
7473 			used_playlists.push_back(pl);
7474 			pl->freeze();
7475 		}
7476 
7477 		samplepos_t position = (*r)->region()->position();
7478 
7479 		if (idx == 0 || position < last_region->position()){
7480 			last_region = (*r)->region();
7481 			idx++;
7482 			continue;
7483 		}
7484 
7485 		(*r)->region()->clear_changes ();
7486 		(*r)->region()->trim_front((position - pull_back_samples));
7487 
7488 		last_region->clear_changes ();
7489 		last_region->trim_end ((position - pull_back_samples + crossfade_len));
7490 
7491 		_session->add_command (new StatefulDiffCommand ((*r)->region()));
7492 		_session->add_command (new StatefulDiffCommand (last_region));
7493 
7494 		last_region = (*r)->region();
7495 		idx++;
7496 	}
7497 
7498 	while (used_playlists.size() > 0) {
7499 		list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7500 		(*i)->thaw();
7501 		used_playlists.pop_front();
7502 	}
7503 
7504 	commit_reversible_command ();
7505 }
7506 
7507 void
tab_to_transient(bool forward)7508 Editor::tab_to_transient (bool forward)
7509 {
7510 	AnalysisFeatureList positions;
7511 
7512 	RegionSelection rs = get_regions_from_selection_and_entered ();
7513 
7514 	if (!_session) {
7515 		return;
7516 	}
7517 
7518 	samplepos_t pos = _session->audible_sample ();
7519 
7520 	if (!selection->tracks.empty()) {
7521 
7522 		/* don't waste time searching for transients in duplicate playlists.
7523 		 */
7524 
7525 		TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7526 
7527 		for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7528 
7529 			RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7530 
7531 			if (rtv) {
7532 				boost::shared_ptr<Track> tr = rtv->track();
7533 				if (tr) {
7534 					boost::shared_ptr<Playlist> pl = tr->playlist ();
7535 					if (pl) {
7536 						samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7537 
7538 						if (result >= 0) {
7539 							positions.push_back (result);
7540 						}
7541 					}
7542 				}
7543 			}
7544 		}
7545 
7546 	} else {
7547 
7548 		if (rs.empty()) {
7549 			return;
7550 		}
7551 
7552 		for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7553 			(*r)->region()->get_transients (positions);
7554 		}
7555 	}
7556 
7557 	TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7558 
7559 	if (forward) {
7560 		AnalysisFeatureList::iterator x;
7561 
7562 		for (x = positions.begin(); x != positions.end(); ++x) {
7563 			if ((*x) > pos) {
7564 				break;
7565 			}
7566 		}
7567 
7568 		if (x != positions.end ()) {
7569 			_session->request_locate (*x);
7570 		}
7571 
7572 	} else {
7573 		AnalysisFeatureList::reverse_iterator x;
7574 
7575 		for (x = positions.rbegin(); x != positions.rend(); ++x) {
7576 			if ((*x) < pos) {
7577 				break;
7578 			}
7579 		}
7580 
7581 		if (x != positions.rend ()) {
7582 			_session->request_locate (*x);
7583 		}
7584 	}
7585 }
7586 
7587 void
playhead_forward_to_grid()7588 Editor::playhead_forward_to_grid ()
7589 {
7590 	if (!_session) {
7591 		return;
7592 	}
7593 
7594 	MusicSample pos  (_playhead_cursor->current_sample (), 0);
7595 
7596 	if ( _grid_type == GridTypeNone) {
7597 		if (pos.sample < max_samplepos - current_page_samples()*0.1) {
7598 			pos.sample += current_page_samples()*0.1;
7599 			_session->request_locate (pos.sample);
7600 		} else {
7601 			_session->request_locate (0);
7602 		}
7603 	} else {
7604 
7605 		if (pos.sample < max_samplepos - 1) {
7606 			pos.sample += 2;
7607 			pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
7608 			_session->request_locate (pos.sample);
7609 		}
7610 	}
7611 
7612 
7613 	/* keep PH visible in window */
7614 	if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
7615 		reset_x_origin (pos.sample - (current_page_samples()*0.9));
7616 	}
7617 }
7618 
7619 
7620 void
playhead_backward_to_grid()7621 Editor::playhead_backward_to_grid ()
7622 {
7623 	if (!_session) {
7624 		return;
7625 	}
7626 
7627 	MusicSample pos  (_playhead_cursor->current_sample (), 0);
7628 
7629 	if ( _grid_type == GridTypeNone) {
7630 		if ( pos.sample > current_page_samples()*0.1 ) {
7631 			pos.sample -= current_page_samples()*0.1;
7632 			_session->request_locate (pos.sample);
7633 		} else {
7634 			_session->request_locate (0);
7635 		}
7636 	} else {
7637 
7638 		if (pos.sample > 2) {
7639 			pos.sample -= 2;
7640 			pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7641 		}
7642 
7643 		//handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
7644 		//also see:  jump_backward_to_mark
7645 		if (_session->transport_rolling()) {
7646 			if ((_playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
7647 				pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
7648 			}
7649 		}
7650 
7651 		_session->request_locate (pos.sample);
7652 	}
7653 
7654 	/* keep PH visible in window */
7655 	if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
7656 		reset_x_origin (pos.sample - (current_page_samples()*0.1));
7657 	}
7658 }
7659 
7660 void
set_track_height(Height h)7661 Editor::set_track_height (Height h)
7662 {
7663 	TrackSelection& ts (selection->tracks);
7664 
7665 	for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7666 		(*x)->set_height_enum (h);
7667 	}
7668 }
7669 
7670 void
toggle_tracks_active()7671 Editor::toggle_tracks_active ()
7672 {
7673 	TrackSelection& ts (selection->tracks);
7674 	bool first = true;
7675 	bool target = false;
7676 
7677 	if (ts.empty()) {
7678 		return;
7679 	}
7680 
7681 	for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7682 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7683 
7684 		if (rtv) {
7685 			if (first) {
7686 				target = !rtv->route()->active();
7687 				first = false;
7688 			}
7689 			rtv->route()->set_active (target, this);
7690 		}
7691 	}
7692 }
7693 
7694 void
remove_tracks()7695 Editor::remove_tracks ()
7696 {
7697 	/* this will delete GUI objects that may be the subject of an event
7698 	   handler in which this method is called. Defer actual deletion to the
7699 	   next idle callback, when all event handling is finished.
7700 	*/
7701 	Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7702 }
7703 
7704 bool
idle_remove_tracks()7705 Editor::idle_remove_tracks ()
7706 {
7707 	Session::StateProtector sp (_session);
7708 	_remove_tracks ();
7709 	return false; /* do not call again */
7710 }
7711 
7712 void
_remove_tracks()7713 Editor::_remove_tracks ()
7714 {
7715 	TrackSelection& ts (selection->tracks);
7716 
7717 	if (ts.empty()) {
7718 		return;
7719 	}
7720 
7721 	if (!ARDOUR_UI_UTILS::engine_is_running ()) {
7722 		return;
7723 	}
7724 
7725 	vector<string> choices;
7726 	string prompt;
7727 	int ntracks = 0;
7728 	int nbusses = 0;
7729 	int nvcas = 0;
7730 	const char* trackstr;
7731 	const char* busstr;
7732 	const char* vcastr;
7733 	vector<boost::shared_ptr<Route> > routes;
7734 	vector<boost::shared_ptr<VCA> > vcas;
7735 	bool special_bus = false;
7736 
7737 	for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7738 		VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7739 		if (vtv) {
7740 			vcas.push_back (vtv->vca());
7741 			++nvcas;
7742 			continue;
7743 		}
7744 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7745 		if (!rtv) {
7746 			continue;
7747 		}
7748 		if (rtv->is_track()) {
7749 			++ntracks;
7750 		} else {
7751 			++nbusses;
7752 		}
7753 		routes.push_back (rtv->route());
7754 
7755 		if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7756 			special_bus = true;
7757 		}
7758 	}
7759 
7760 	if (special_bus && !Config->get_allow_special_bus_removal()) {
7761 		ArdourMessageDialog msg (_("That would be bad news ...."),
7762 		                         false,
7763 		                         Gtk::MESSAGE_INFO,
7764 		                         Gtk::BUTTONS_OK);
7765 		msg.set_secondary_text (string_compose (_("Removing the master or monitor bus is such a bad idea\n\
7766 that %1 is not going to allow it.\n\
7767 \n\
7768 If you really want to do this sort of thing\n\
7769 edit your ardour.rc file to set the\n\
7770 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7771 
7772 		msg.run ();
7773 		return;
7774 	}
7775 
7776 	if (ntracks + nbusses + nvcas == 0) {
7777 		return;
7778 	}
7779 
7780 	string title;
7781 
7782 	trackstr = P_("track", "tracks", ntracks);
7783 	busstr = P_("bus", "busses", nbusses);
7784 	vcastr = P_("VCA", "VCAs", nvcas);
7785 
7786 	if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7787 		title = _("Remove various strips");
7788 		prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7789 						  ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7790 	}
7791 	else if (ntracks > 0 && nbusses > 0) {
7792 		title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7793 		prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7794 				ntracks, trackstr, nbusses, busstr);
7795 	}
7796 	else if (ntracks > 0 && nvcas > 0) {
7797 		title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7798 		prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7799 				ntracks, trackstr, nvcas, vcastr);
7800 	}
7801 	else if (nbusses > 0 && nvcas > 0) {
7802 		title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7803 		prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7804 				nbusses, busstr, nvcas, vcastr);
7805 	}
7806 	else if (ntracks > 0) {
7807 		title = string_compose (_("Remove %1"), trackstr);
7808 		prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7809 				ntracks, trackstr);
7810 	}
7811 	else if (nbusses > 0) {
7812 		title = string_compose (_("Remove %1"), busstr);
7813 		prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7814 				nbusses, busstr);
7815 	}
7816 	else if (nvcas > 0) {
7817 		title = string_compose (_("Remove %1"), vcastr);
7818 		prompt  = string_compose (_("Do you really want to remove %1 %2?"),
7819 				nvcas, vcastr);
7820 	}
7821 	else {
7822 		assert (0);
7823 	}
7824 
7825 	if (ntracks > 0) {
7826 			prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7827 	}
7828 
7829 	prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7830 
7831 	choices.push_back (_("No, do nothing."));
7832 	if (ntracks + nbusses + nvcas > 1) {
7833 		choices.push_back (_("Yes, remove them."));
7834 	} else {
7835 		choices.push_back (_("Yes, remove it."));
7836 	}
7837 
7838 	Choice prompter (title, prompt, choices);
7839 
7840 	if (prompter.run () != 1) {
7841 		return;
7842 	}
7843 
7844 	if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7845 		/* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7846 		 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7847 		 * likely because deletion requires selection) this will call
7848 		 * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
7849 		 * It's likewise likely that the route that has just been displayed in the
7850 		 * Editor-Mixer will be next in line for deletion.
7851 		 *
7852 		 * So simply switch to the master-bus (if present)
7853 		 */
7854 		for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7855 			if ((*i)->stripable ()->is_master ()) {
7856 				set_selected_mixer_strip (*(*i));
7857 				break;
7858 			}
7859 		}
7860 	}
7861 
7862 	{
7863 		PresentationInfo::ChangeSuspender cs;
7864 		DisplaySuspender ds;
7865 
7866 		boost::shared_ptr<RouteList> rl (new RouteList);
7867 		for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7868 			rl->push_back (*x);
7869 		}
7870 		_session->remove_routes (rl);
7871 
7872 		for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7873 			_session->vca_manager().remove_vca (*x);
7874 		}
7875 
7876 	}
7877 	/* TrackSelection and RouteList leave scope,
7878 	 * destructors are called,
7879 	 * diskstream drops references, save_state is called (again for every track)
7880 	 */
7881 }
7882 
7883 void
do_insert_time()7884 Editor::do_insert_time ()
7885 {
7886 	if (selection->tracks.empty()) {
7887 		ArdourMessageDialog msg (_("You must first select some tracks to Insert Time."),
7888 	                           true, MESSAGE_INFO, BUTTONS_OK, true);
7889 		msg.run ();
7890 		return;
7891 	}
7892 
7893 	if (Config->get_edit_mode() == Lock) {
7894 		ArdourMessageDialog msg (_("You cannot insert time in Lock Edit mode."),
7895 		                         true, MESSAGE_INFO, BUTTONS_OK, true);
7896 		msg.run ();
7897 		return;
7898 	}
7899 
7900 	InsertRemoveTimeDialog d (*this);
7901 	int response = d.run ();
7902 
7903 	if (response != RESPONSE_OK) {
7904 		return;
7905 	}
7906 
7907 	if (d.distance() == 0) {
7908 		return;
7909 	}
7910 
7911 	insert_time (
7912 		d.position(),
7913 		d.distance(),
7914 		d.intersected_region_action (),
7915 		d.all_playlists(),
7916 		d.move_glued(),
7917 		d.move_markers(),
7918 		d.move_glued_markers(),
7919 		d.move_locked_markers(),
7920 		d.move_tempos()
7921 		);
7922 }
7923 
7924 void
insert_time(samplepos_t pos,samplecnt_t samples,InsertTimeOption opt,bool all_playlists,bool ignore_music_glue,bool markers_too,bool glued_markers_too,bool locked_markers_too,bool tempo_too)7925 Editor::insert_time (
7926 	samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7927 	bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7928 	)
7929 {
7930 
7931 	if (Config->get_edit_mode() == Lock) {
7932 		return;
7933 	}
7934 	bool in_command = false;
7935 
7936 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7937 
7938 	for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7939 
7940 		/* regions */
7941 
7942 		/* don't operate on any playlist more than once, which could
7943 		 * happen if "all playlists" is enabled, but there is more
7944 		 * than 1 track using playlists "from" a given track.
7945 		 */
7946 
7947 		set<boost::shared_ptr<Playlist> > pl;
7948 
7949 		if (all_playlists) {
7950 			RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7951 			if (rtav && rtav->track ()) {
7952 				vector<boost::shared_ptr<Playlist> > all = _session->playlists()->playlists_for_track (rtav->track ());
7953 				for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7954 					pl.insert (*p);
7955 				}
7956 			}
7957 		} else {
7958 			if ((*x)->playlist ()) {
7959 				pl.insert ((*x)->playlist ());
7960 			}
7961 		}
7962 
7963 		for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7964 
7965 			(*i)->clear_changes ();
7966 			(*i)->clear_owned_changes ();
7967 
7968 			if (!in_command) {
7969 				begin_reversible_command (_("insert time"));
7970 				in_command = true;
7971 			}
7972 
7973 			if (opt == SplitIntersected) {
7974 				/* non musical split */
7975 				(*i)->split (MusicSample (pos, 0));
7976 			}
7977 
7978 			(*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7979 
7980 			vector<Command*> cmds;
7981 			(*i)->rdiff (cmds);
7982 			_session->add_commands (cmds);
7983 
7984 			_session->add_command (new StatefulDiffCommand (*i));
7985 		}
7986 
7987 		/* automation */
7988 		RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7989 		if (rtav) {
7990 			if (!in_command) {
7991 				begin_reversible_command (_("insert time"));
7992 				in_command = true;
7993 			}
7994 			rtav->route ()->shift (pos, samples);
7995 		}
7996 	}
7997 
7998 	/* markers */
7999 	if (markers_too) {
8000 		bool moved = false;
8001 		const int32_t divisions = get_grid_music_divisions (0);
8002 		XMLNode& before (_session->locations()->get_state());
8003 		Locations::LocationList copy (_session->locations()->list());
8004 
8005 		for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
8006 
8007 			Locations::LocationList::const_iterator tmp;
8008 
8009 			if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
8010 				bool const was_locked = (*i)->locked ();
8011 				if (locked_markers_too) {
8012 					(*i)->unlock ();
8013 				}
8014 
8015 				if ((*i)->start() >= pos) {
8016 					// move end first, in case we're moving by more than the length of the range
8017 					if (!(*i)->is_mark()) {
8018 						(*i)->set_end ((*i)->end() + samples, false, true, divisions);
8019 					}
8020 					(*i)->set_start ((*i)->start() + samples, false, true, divisions);
8021 					moved = true;
8022 				}
8023 
8024 				if (was_locked) {
8025 					(*i)->lock ();
8026 				}
8027 			}
8028 		}
8029 
8030 		if (moved) {
8031 			if (!in_command) {
8032 				begin_reversible_command (_("insert time"));
8033 				in_command = true;
8034 			}
8035 			XMLNode& after (_session->locations()->get_state());
8036 			_session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8037 		}
8038 	}
8039 
8040 	if (tempo_too) {
8041 		if (!in_command) {
8042 			begin_reversible_command (_("insert time"));
8043 			in_command = true;
8044 		}
8045 		XMLNode& before (_session->tempo_map().get_state());
8046 		_session->tempo_map().insert_time (pos, samples);
8047 		XMLNode& after (_session->tempo_map().get_state());
8048 		_session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8049 	}
8050 
8051 	if (in_command) {
8052 		commit_reversible_command ();
8053 	}
8054 }
8055 
8056 void
do_remove_time()8057 Editor::do_remove_time ()
8058 {
8059 	if (selection->tracks.empty()) {
8060 		ArdourMessageDialog msg (_("You must first select some tracks to Remove Time."),
8061 		                         true, MESSAGE_INFO, BUTTONS_OK, true);
8062 		msg.run ();
8063 		return;
8064 	}
8065 
8066 	if (Config->get_edit_mode() == Lock) {
8067 		ArdourMessageDialog msg (_("You cannot remove time in Lock Edit mode."),
8068 		                         true, MESSAGE_INFO, BUTTONS_OK, true);
8069 		msg.run ();
8070 		return;
8071 	}
8072 
8073 	InsertRemoveTimeDialog d (*this, true);
8074 
8075 	int response = d.run ();
8076 
8077 	if (response != RESPONSE_OK) {
8078 		return;
8079 	}
8080 
8081 	samplecnt_t distance = d.distance();
8082 
8083 	if (distance == 0) {
8084 		return;
8085 	}
8086 
8087 	remove_time (
8088 		d.position(),
8089 		distance,
8090 		SplitIntersected,
8091 		d.move_glued(),
8092 		d.move_markers(),
8093 		d.move_glued_markers(),
8094 		d.move_locked_markers(),
8095 		d.move_tempos()
8096 	);
8097 }
8098 
8099 void
remove_time(samplepos_t pos,samplecnt_t samples,InsertTimeOption opt,bool ignore_music_glue,bool markers_too,bool glued_markers_too,bool locked_markers_too,bool tempo_too)8100 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
8101                      bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
8102 {
8103 	if (Config->get_edit_mode() == Lock) {
8104 		error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
8105 		return;
8106 	}
8107 	bool in_command = false;
8108 
8109 	for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
8110 		/* regions */
8111 		boost::shared_ptr<Playlist> pl = (*x)->playlist();
8112 
8113 		if (pl) {
8114 
8115 			XMLNode &before = pl->get_state();
8116 
8117 			if (!in_command) {
8118 				begin_reversible_command (_("remove time"));
8119 				in_command = true;
8120 			}
8121 
8122 			std::list<AudioRange> rl;
8123 			AudioRange ar(pos, pos+samples, 0);
8124 			rl.push_back(ar);
8125 			pl->cut (rl);
8126 			pl->shift (pos, -samples, true, ignore_music_glue);
8127 
8128 			XMLNode &after = pl->get_state();
8129 
8130 			_session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
8131 		}
8132 
8133 		/* automation */
8134 		RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
8135 		if (rtav) {
8136 			if (!in_command) {
8137 				begin_reversible_command (_("remove time"));
8138 				in_command = true;
8139 			}
8140 			rtav->route ()->shift (pos, -samples);
8141 		}
8142 	}
8143 
8144 	const int32_t divisions = get_grid_music_divisions (0);
8145 	std::list<Location*> loc_kill_list;
8146 
8147 	/* markers */
8148 	if (markers_too) {
8149 		bool moved = false;
8150 		XMLNode& before (_session->locations()->get_state());
8151 		Locations::LocationList copy (_session->locations()->list());
8152 
8153 		for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
8154 			if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
8155 
8156 				bool const was_locked = (*i)->locked ();
8157 				if (locked_markers_too) {
8158 					(*i)->unlock ();
8159 				}
8160 
8161 				if (!(*i)->is_mark()) {  // it's a range;  have to handle both start and end
8162 					if ((*i)->end() >= pos
8163 					&& (*i)->end() < pos+samples
8164 					&& (*i)->start() >= pos
8165 					&& (*i)->end() < pos+samples) {  // range is completely enclosed;  kill it
8166 						moved = true;
8167 						loc_kill_list.push_back(*i);
8168 					} else {  // only start or end is included, try to do the right thing
8169 						// move start before moving end, to avoid trying to move the end to before the start
8170 						// if we're removing more time than the length of the range
8171 						if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8172 							// start is within cut
8173 							(*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
8174 							moved = true;
8175 						} else if ((*i)->start() >= pos+samples) {
8176 							// start (and thus entire range) lies beyond end of cut
8177 							(*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
8178 							moved = true;
8179 						}
8180 						if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
8181 							// end is inside cut
8182 							(*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
8183 							moved = true;
8184 						} else if ((*i)->end() >= pos+samples) {
8185 							// end is beyond end of cut
8186 							(*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
8187 							moved = true;
8188 						}
8189 
8190 					}
8191 				} else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
8192 					loc_kill_list.push_back(*i);
8193 					moved = true;
8194 				} else if ((*i)->start() >= pos) {
8195 					(*i)->set_start ((*i)->start() -samples, false, true, divisions);
8196 					moved = true;
8197 				}
8198 
8199 				if (was_locked) {
8200 					(*i)->lock ();
8201 				}
8202 			}
8203 		}
8204 
8205 		for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
8206 			_session->locations()->remove (*i);
8207 		}
8208 
8209 		if (moved) {
8210 			if (!in_command) {
8211 				begin_reversible_command (_("remove time"));
8212 				in_command = true;
8213 			}
8214 			XMLNode& after (_session->locations()->get_state());
8215 			_session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
8216 		}
8217 	}
8218 
8219 	if (tempo_too) {
8220 		XMLNode& before (_session->tempo_map().get_state());
8221 
8222 		if (_session->tempo_map().remove_time (pos, samples)) {
8223 			if (!in_command) {
8224 				begin_reversible_command (_("remove time"));
8225 				in_command = true;
8226 			}
8227 			XMLNode& after (_session->tempo_map().get_state());
8228 			_session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
8229 		}
8230 	}
8231 
8232 	if (in_command) {
8233 		commit_reversible_command ();
8234 	}
8235 }
8236 
8237 void
fit_selection()8238 Editor::fit_selection ()
8239 {
8240 	if (!selection->tracks.empty()) {
8241 		fit_tracks (selection->tracks);
8242 	} else {
8243 		TrackViewList tvl;
8244 
8245 		/* no selected tracks - use tracks with selected regions */
8246 
8247 		if (!selection->regions.empty()) {
8248 			for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
8249 				tvl.push_back (&(*r)->get_time_axis_view ());
8250 			}
8251 
8252 			if (!tvl.empty()) {
8253 				fit_tracks (tvl);
8254 			}
8255 		} else if (internal_editing()) {
8256 			/* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
8257 			 * the entered track
8258 			 */
8259 			if (entered_track) {
8260 				tvl.push_back (entered_track);
8261 				fit_tracks (tvl);
8262 			}
8263 		}
8264 	}
8265 }
8266 
8267 void
fit_tracks(TrackViewList & tracks)8268 Editor::fit_tracks (TrackViewList & tracks)
8269 {
8270 	if (tracks.empty()) {
8271 		return;
8272 	}
8273 
8274 	uint32_t child_heights = 0;
8275 	int visible_tracks = 0;
8276 
8277 	for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
8278 
8279 		if (!(*t)->marked_for_display()) {
8280 			continue;
8281 		}
8282 
8283 		child_heights += (*t)->effective_height() - (*t)->current_height();
8284 		++visible_tracks;
8285 	}
8286 
8287 	/* compute the per-track height from:
8288 	 *
8289 	 * total canvas visible height
8290 	 *  - height that will be taken by visible children of selected tracks
8291 	 *  - height of the ruler/hscroll area
8292 	 */
8293 	uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
8294 	double first_y_pos = DBL_MAX;
8295 
8296 	if (h < TimeAxisView::preset_height (HeightSmall)) {
8297 		ArdourMessageDialog msg (_("There are too many tracks to fit in the current window"));
8298 		msg.run ();
8299 		/* too small to be displayed, just use smallest possible */
8300 		h = HeightSmall;
8301 	}
8302 
8303 	undo_visual_stack.push_back (current_visual_state (true));
8304 	PBD::Unwinder<bool> nsv (no_save_visual, true);
8305 
8306 	/* build a list of all tracks, including children */
8307 
8308 	TrackViewList all;
8309 	for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
8310 		all.push_back (*i);
8311 		TimeAxisView::Children c = (*i)->get_child_list ();
8312 		for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
8313 			all.push_back (j->get());
8314 		}
8315 	}
8316 
8317 
8318 	// find selection range.
8319 	// if someone knows how to user TrackViewList::iterator for this
8320 	// I'm all ears.
8321 	int selected_top = -1;
8322 	int selected_bottom = -1;
8323 	int i = 0;
8324 	for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8325 		if ((*t)->marked_for_display ()) {
8326 			if (tracks.contains(*t)) {
8327 				if (selected_top == -1) {
8328 					selected_top = i;
8329 				}
8330 				selected_bottom = i;
8331 			}
8332 		}
8333 	}
8334 
8335 	i = 0;
8336 	for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
8337 		if ((*t)->marked_for_display ()) {
8338 			if (tracks.contains(*t)) {
8339 				(*t)->set_height (h);
8340 				first_y_pos = std::min ((*t)->y_position (), first_y_pos);
8341 			} else {
8342 				if (i > selected_top && i < selected_bottom) {
8343 					hide_track_in_display (*t);
8344 				}
8345 			}
8346 		}
8347 	}
8348 
8349 	/*
8350 	   set the controls_layout height now, because waiting for its size
8351 	   request signal handler will cause the vertical adjustment setting to fail
8352 	*/
8353 
8354 	controls_layout.property_height () = _full_canvas_height;
8355 	vertical_adjustment.set_value (first_y_pos);
8356 
8357 	redo_visual_stack.push_back (current_visual_state (true));
8358 
8359 	visible_tracks_selector.set_text (_("Sel"));
8360 }
8361 
8362 void
save_visual_state(uint32_t n)8363 Editor::save_visual_state (uint32_t n)
8364 {
8365 	while (visual_states.size() <= n) {
8366 		visual_states.push_back (0);
8367 	}
8368 
8369 	if (visual_states[n] != 0) {
8370 		delete visual_states[n];
8371 	}
8372 
8373 	visual_states[n] = current_visual_state (true);
8374 	gdk_beep ();
8375 }
8376 
8377 void
goto_visual_state(uint32_t n)8378 Editor::goto_visual_state (uint32_t n)
8379 {
8380 	if (visual_states.size() <= n) {
8381 		return;
8382 	}
8383 
8384 	if (visual_states[n] == 0) {
8385 		return;
8386 	}
8387 
8388 	use_visual_state (*visual_states[n]);
8389 }
8390 
8391 void
start_visual_state_op(uint32_t n)8392 Editor::start_visual_state_op (uint32_t n)
8393 {
8394 	save_visual_state (n);
8395 
8396 	PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8397 	char buf[32];
8398 	snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8399 	pup->set_text (buf);
8400 	pup->touch();
8401 }
8402 
8403 void
cancel_visual_state_op(uint32_t n)8404 Editor::cancel_visual_state_op (uint32_t n)
8405 {
8406 	goto_visual_state (n);
8407 }
8408 
8409 void
toggle_region_mute()8410 Editor::toggle_region_mute ()
8411 {
8412 	if (_ignore_region_action) {
8413 		return;
8414 	}
8415 
8416 	RegionSelection rs = get_regions_from_selection_and_entered ();
8417 
8418 	if (rs.empty ()) {
8419 		return;
8420 	}
8421 
8422 	if (rs.size() > 1) {
8423 		begin_reversible_command (_("mute regions"));
8424 	} else {
8425 		begin_reversible_command (_("mute region"));
8426 	}
8427 
8428 	for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8429 
8430 		(*i)->region()->playlist()->clear_changes ();
8431 		(*i)->region()->set_muted (!(*i)->region()->muted ());
8432 		_session->add_command (new StatefulDiffCommand ((*i)->region()));
8433 
8434 	}
8435 
8436 	commit_reversible_command ();
8437 }
8438 
8439 void
combine_regions()8440 Editor::combine_regions ()
8441 {
8442 	/* foreach track with selected regions, take all selected regions
8443 	   and join them into a new region containing the subregions (as a
8444 	   playlist)
8445 	*/
8446 
8447 	typedef set<RouteTimeAxisView*> RTVS;
8448 	RTVS tracks;
8449 
8450 	if (selection->regions.empty()) {
8451 		return;
8452 	}
8453 
8454 	for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8455 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8456 
8457 		if (rtv) {
8458 			tracks.insert (rtv);
8459 		}
8460 	}
8461 
8462 	begin_reversible_command (_("combine regions"));
8463 
8464 	vector<RegionView*> new_selection;
8465 
8466 	for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8467 		RegionView* rv;
8468 
8469 		if ((rv = (*i)->combine_regions ()) != 0) {
8470 			new_selection.push_back (rv);
8471 		}
8472 	}
8473 
8474 	selection->clear_regions ();
8475 	for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8476 		selection->add (*i);
8477 	}
8478 
8479 	commit_reversible_command ();
8480 }
8481 
8482 void
uncombine_regions()8483 Editor::uncombine_regions ()
8484 {
8485 	typedef set<RouteTimeAxisView*> RTVS;
8486 	RTVS tracks;
8487 
8488 	if (selection->regions.empty()) {
8489 		return;
8490 	}
8491 
8492 	for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8493 		RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8494 
8495 		if (rtv) {
8496 			tracks.insert (rtv);
8497 		}
8498 	}
8499 
8500 	begin_reversible_command (_("uncombine regions"));
8501 
8502 	for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8503 		(*i)->uncombine_regions ();
8504 	}
8505 
8506 	commit_reversible_command ();
8507 }
8508 
8509 void
toggle_midi_input_active(bool flip_others)8510 Editor::toggle_midi_input_active (bool flip_others)
8511 {
8512 	bool onoff = false;
8513 	boost::shared_ptr<RouteList> rl (new RouteList);
8514 
8515 	for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8516 		RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8517 
8518 		if (!rtav) {
8519 			continue;
8520 		}
8521 
8522 		boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8523 
8524 		if (mt) {
8525 			rl->push_back (rtav->route());
8526 			onoff = !mt->input_active();
8527 		}
8528 	}
8529 
8530 	_session->set_exclusive_input_active (rl, onoff, flip_others);
8531 }
8532 
ok_fine(GdkEventAny *)8533 static bool ok_fine (GdkEventAny*) { return true; }
8534 
8535 void
lock()8536 Editor::lock ()
8537 {
8538 	if (!lock_dialog) {
8539 		lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8540 
8541 		Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8542 		lock_dialog->get_vbox()->pack_start (*padlock);
8543 		lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8544 
8545 		ArdourButton* b = manage (new ArdourButton);
8546 		b->set_name ("lock button");
8547 		b->set_text (_("Click to unlock"));
8548 		b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8549 		lock_dialog->get_vbox()->pack_start (*b);
8550 
8551 		lock_dialog->get_vbox()->show_all ();
8552 		lock_dialog->set_size_request (200, 200);
8553 	}
8554 
8555 	delete _main_menu_disabler;
8556 	_main_menu_disabler = new MainMenuDisabler;
8557 
8558 	lock_dialog->present ();
8559 
8560 	lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8561 }
8562 
8563 void
unlock()8564 Editor::unlock ()
8565 {
8566 	lock_dialog->hide ();
8567 
8568 	delete _main_menu_disabler;
8569 	_main_menu_disabler = 0;
8570 
8571 	if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8572 		start_lock_event_timing ();
8573 	}
8574 }
8575 
8576 void
bring_in_callback(Gtk::Label * label,uint32_t n,uint32_t total,string name)8577 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8578 {
8579 	Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8580 }
8581 
8582 void
update_bring_in_message(Gtk::Label * label,uint32_t n,uint32_t total,string name)8583 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8584 {
8585 	Timers::TimerSuspender t;
8586 	label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8587 	Gtkmm2ext::UI::instance()->flush_pending (1);
8588 }
8589 
8590 void
bring_all_sources_into_session()8591 Editor::bring_all_sources_into_session ()
8592 {
8593 	if (!_session) {
8594 		return;
8595 	}
8596 
8597 	Gtk::Label msg;
8598 	ArdourDialog w (_("Moving embedded files into session folder"));
8599 	w.get_vbox()->pack_start (msg);
8600 	w.present ();
8601 
8602 	/* flush all pending GUI events because we're about to start copying
8603 	 * files
8604 	 */
8605 
8606 	Timers::TimerSuspender t;
8607 	Gtkmm2ext::UI::instance()->flush_pending (3);
8608 
8609 	cerr << " Do it\n";
8610 
8611 	_session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));
8612 }
8613 
8614 void
toggle_all_existing_automation()8615 Editor::toggle_all_existing_automation ()
8616 {
8617 	TrackViewList & tvl (selection->tracks.empty() ? track_views : selection->tracks);
8618 	bool some_automation_shown = false;
8619 
8620 	for (TrackViewList::const_iterator t = tvl.begin(); t != tvl.end(); ++t) {
8621 		TimeAxisView::Children children = (*t)->get_child_list ();
8622 		for (TimeAxisView::Children::const_iterator c = children.begin(); c != children.end(); ++c) {
8623 			if (boost::dynamic_pointer_cast<AutomationTimeAxisView> (*c)) {
8624 				some_automation_shown = true;
8625 				break;
8626 			}
8627 		}
8628 
8629 		if (some_automation_shown) {
8630 			break;
8631 		}
8632 	}
8633 
8634 	if (!some_automation_shown) {
8635 		tvl.foreach_stripable_time_axis (boost::bind (&StripableTimeAxisView::show_existing_automation, _1, false));
8636 	} else {
8637 		tvl.foreach_stripable_time_axis (boost::bind (&StripableTimeAxisView::hide_all_automation, _1, false));
8638 	}
8639 }
8640 
8641 void
toggle_layer_display()8642 Editor::toggle_layer_display ()
8643 {
8644 	TrackViewList & tvl (selection->tracks.empty() ? track_views : selection->tracks);
8645 	bool seen_stacked = false;
8646 	bool seen_overlaid = false;
8647 
8648 	for (TrackViewList::const_iterator t = tvl.begin(); t != tvl.end(); ++t) {
8649 		RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*t);
8650 
8651 		if (!rtav || !rtav->is_track()) {
8652 			continue;
8653 		}
8654 
8655 		if (rtav->layer_display () == Stacked) {
8656 			seen_stacked = true;
8657 		} else if (rtav->layer_display() == Overlaid) {
8658 			seen_overlaid = true;
8659 		}
8660 	}
8661 
8662 	if (seen_stacked && seen_overlaid) {
8663 		/* inconsistent current display - go to overlaid */
8664 		tvl.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, Overlaid));
8665 
8666 	} else {
8667 		tvl.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::toggle_layer_display, _1));
8668 	}
8669 
8670 }
8671 
8672 vector<MidiRegionView*>
filter_to_unique_midi_region_views(RegionSelection const & ms) const8673 Editor::filter_to_unique_midi_region_views (RegionSelection const & ms) const
8674 {
8675 	typedef std::pair<boost::shared_ptr<MidiSource>,samplepos_t> MapEntry;
8676 	std::set<MapEntry> single_region_set;
8677 
8678 	vector<MidiRegionView*> views;
8679 
8680 	/* build a list of regions that are unique with respect to their source
8681 	 * and start position. Note: this is non-exhaustive... if someone has a
8682 	 * non-forked copy of a MIDI region and then suitably modifies it, this
8683 	 * will still put both regions into the list of things to be acted
8684 	 * upon.
8685 	 *
8686 	 * Solution: user should not select both regions, or should fork one of them.
8687 	 */
8688 
8689 	for (MidiRegionSelection::const_iterator i = ms.begin(); i != ms.end(); ++i) {
8690 
8691 		MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
8692 
8693 		if (!mrv) {
8694 			continue;
8695 		}
8696 
8697 		MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->region()->start());
8698 
8699 		if (single_region_set.insert (entry).second) {
8700 			views.push_back (mrv);
8701 		}
8702 	}
8703 
8704 	return views;
8705 }
8706 
8707 
8708 void
midi_action(void (MidiRegionView::* method)())8709 Editor::midi_action (void (MidiRegionView::*method)())
8710 {
8711 	MidiRegionSelection ms = selection->midi_regions();
8712 
8713 	if (ms.empty()) {
8714 		return;
8715 	}
8716 
8717 	if (ms.size() > 1) {
8718 
8719 		vector<MidiRegionView*> views = filter_to_unique_midi_region_views (ms);
8720 
8721 		for (vector<MidiRegionView*>::iterator mrv = views.begin(); mrv != views.end(); ++mrv) {
8722 			((*mrv)->*method) ();
8723 		}
8724 
8725 	} else {
8726 
8727 		MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(ms.front());
8728 
8729 		if (mrv) {
8730 			(mrv->*method)();
8731 		}
8732 	}
8733 }
8734 
8735 void
add_region_marker()8736 Editor::add_region_marker ()
8737 {
8738 	if (!_session) {
8739 		return;
8740 	}
8741 
8742 	/* get these before we display the dialog, since it will interfere if
8743 	   the edit point is "mouse"
8744 	*/
8745 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
8746 	samplepos_t position = get_preferred_edit_position ();
8747 
8748 	cerr << "adding cue marker @ " << position << " in " << rs.size() << endl;
8749 
8750 	ArdourDialog d (_("New Cue Marker Name"), true, false);
8751 	Gtk::Entry e;
8752 	d.get_vbox()->pack_start (e);
8753 	e.show ();
8754 	e.set_activates_default ();
8755 	d.add_button (Stock::CANCEL, RESPONSE_CANCEL);
8756 	d.add_button (Stock::OK, RESPONSE_OK);
8757 	d.set_default_response (RESPONSE_OK);
8758 
8759 	int result = d.run();
8760 	string str = e.get_text();
8761 
8762 	if (result != RESPONSE_OK || str.empty()) {
8763 		return;
8764 	}
8765 
8766 
8767 	bool in_command = false;
8768 
8769 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
8770 
8771 		boost::shared_ptr<Region> region ((*r)->region());
8772 
8773 		if (position < region->position() || position >= region->position() + region->length()) {
8774 			cerr << "nope on that one\n";
8775 			continue;
8776 		}
8777 
8778 		SourceList & sources = region->sources_for_edit ();
8779 
8780 		CueMarker marker (str, region->start() + (position - region->position()));
8781 
8782 		for (SourceList::iterator s = sources.begin(); s != sources.end(); ++s) {
8783 
8784 			XMLNode* before_cues = (*s)->get_state().child (X_("Cues"));
8785 
8786 			if (!(*s)->add_cue_marker (marker)) {
8787 				delete before_cues;
8788 				continue;
8789 			}
8790 
8791 			if (!in_command) {
8792 				begin_reversible_command (_("add cue marker"));
8793 				in_command = true;
8794 			}
8795 
8796 
8797 			XMLNode* after_cues = (*s)->get_state().child (X_("Cues"));
8798 
8799 			if (!before_cues) {
8800 				before_cues = new XMLNode (X_("Cues"));
8801 			}
8802 
8803 			if (!after_cues) {
8804 				after_cues = new XMLNode (X_("Cues"));
8805 			}
8806 
8807 			_session->add_command (new MementoCommand<Source> (**s, before_cues, after_cues));
8808 		}
8809 	}
8810 
8811 	if (in_command) {
8812 		commit_reversible_command ();
8813 	}
8814 }
8815 
8816 void
remove_region_marker(CueMarker & cm)8817 Editor::remove_region_marker (CueMarker& cm)
8818 {
8819 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
8820 	bool in_command = false;
8821 
8822 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
8823 		SourceList & sources = (*r)->region()->sources_for_edit ();
8824 		for (SourceList::iterator s = sources.begin(); s != sources.end(); ++s) {
8825 
8826 			XMLNode* before_cues = (*s)->get_state().child (X_("Cues"));
8827 
8828 			if (!(*s)->remove_cue_marker (cm)) {
8829 				delete before_cues;
8830 				continue;
8831 			}
8832 
8833 			if (!in_command) {
8834 				begin_reversible_command (_("remove cue marker"));
8835 				in_command = true;
8836 			}
8837 
8838 			XMLNode* after_cues = (*s)->get_state().child (X_("Cues"));
8839 
8840 			if (!before_cues) {
8841 				before_cues = new XMLNode (X_("Cues"));
8842 			}
8843 
8844 			if (!after_cues) {
8845 				after_cues = new XMLNode (X_("Cues"));
8846 			}
8847 
8848 			_session->add_command (new MementoCommand<Source> (**s, before_cues, after_cues));
8849 		}
8850 	}
8851 
8852 	if (in_command) {
8853 		commit_reversible_command ();
8854 	}
8855 }
8856 
8857 void
clear_region_markers()8858 Editor::clear_region_markers ()
8859 {
8860 
8861 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
8862 	bool in_command = false;
8863 
8864 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
8865 		SourceList & sources = (*r)->region()->sources_for_edit ();
8866 		for (SourceList::iterator s = sources.begin(); s != sources.end(); ++s) {
8867 
8868 			XMLNode* before_cues = (*s)->get_state().child (X_("Cues"));
8869 
8870 			if (!(*s)->clear_cue_markers ()) {
8871 				delete before_cues;
8872 				continue;
8873 			}
8874 
8875 			if (!in_command) {
8876 				begin_reversible_command (_("clear cue markers"));
8877 				in_command = true;
8878 			}
8879 			XMLNode* after_cues = (*s)->get_state().child (X_("Cues"));
8880 
8881 			if (!before_cues) {
8882 				before_cues = new XMLNode (X_("Cues"));
8883 			}
8884 
8885 			if (!after_cues) {
8886 				after_cues = new XMLNode (X_("Cues"));
8887 			}
8888 
8889 			_session->add_command (new MementoCommand<Source> (**s, before_cues, after_cues));
8890 		}
8891 	}
8892 
8893 	if (in_command) {
8894 		commit_reversible_command ();
8895 	}
8896 }
8897 
8898 void
make_region_markers_global(bool as_cd_marker)8899 Editor::make_region_markers_global (bool as_cd_marker)
8900 {
8901 	RegionSelection rs = get_regions_from_selection_and_edit_point ();
8902 	XMLNode& before (_session->locations()->get_state());
8903 	bool in_command = false;
8904 
8905 	for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
8906 		CueMarkers cues;
8907 
8908 		(*r)->region()->get_cue_markers (cues, true);
8909 
8910 		if (!cues.empty()) {
8911 			if (!in_command) {
8912 				in_command = true;
8913 			}
8914 
8915 			for (CueMarkers::iterator cm = cues.begin(); cm != cues.end(); ++cm) {
8916 				/* marker position is absolute within source */
8917 				const samplepos_t absolute_pos = (cm->position() - (*r)->region()->start()) + (*r)->region()->position();
8918 				Location* loc = new Location (*_session, absolute_pos, absolute_pos, cm->text(), as_cd_marker ? Location::Flags (Location::IsMark|Location::IsCDMarker) : Location::IsMark);
8919 				_session->locations()->add (loc, false);
8920 			}
8921 		}
8922 
8923 		cues.clear ();
8924 	}
8925 
8926 	if (in_command) {
8927 		XMLNode& after (_session->locations()->get_state());
8928 		begin_reversible_command (_("region markers -> global markers"));
8929 		_session->add_command (new MementoCommand<Locations> (*(_session->locations()), &before, &after));
8930 		commit_reversible_command ();
8931 	}
8932 }
8933 
8934 void
do_remove_gaps()8935 Editor::do_remove_gaps ()
8936 {
8937 	ArdourDialog d (_("Remove Gaps"), true, false);
8938 
8939 	Gtk::HBox hpacker1;
8940 	Gtk::Label label1 (_("Smallest gap size to remove (seconds):"));
8941 	Gtk::Entry e1;
8942 
8943 	hpacker1.set_spacing (12);
8944 	hpacker1.set_border_width (12);
8945 	hpacker1.pack_start (label1, true, false);
8946 	hpacker1.pack_start (e1, false, false);
8947 
8948 	Gtk::HBox hpacker2;
8949 	Gtk::Label label2 (_("Leave a gap of(seconds):"));
8950 	Gtk::Entry e2;
8951 
8952 	hpacker2.set_spacing (12);
8953 	hpacker2.set_border_width (12);
8954 	hpacker2.pack_start (label2, true, false);
8955 	hpacker2.pack_start (e2, false, false);
8956 
8957 	Gtk::CheckButton markers_too (_("Shift global markers too"));
8958 
8959 	d.get_vbox()->pack_start (hpacker1);
8960 	d.get_vbox()->pack_start (hpacker2);
8961 	d.get_vbox()->pack_start (markers_too);
8962 	d.get_vbox()->show_all ();
8963 
8964 	e2.set_activates_default ();
8965 
8966 	d.add_button (Stock::CANCEL, RESPONSE_CANCEL);
8967 	d.add_button (Stock::OK, RESPONSE_OK);
8968 	d.set_default_response (RESPONSE_OK);
8969 
8970   again:
8971 	int result = d.run ();
8972 
8973 	if (result != RESPONSE_OK) {
8974 		return;
8975 	}
8976 
8977 	float threshold_secs;
8978 
8979 	if (sscanf (e1.get_text().c_str(), "%f", &threshold_secs) != 1) {
8980 		ArdourMessageDialog msg (_("The threshold value you entered is not a number"));
8981 		msg.run();
8982 		goto again;
8983 	}
8984 
8985 	if (threshold_secs < 0) {
8986 		ArdourMessageDialog msg (_("The threshold value must be larger than or equal to zero"));
8987 		msg.run();
8988 		goto again;
8989 	}
8990 
8991 	samplecnt_t threshold_samples = (samplecnt_t) floor (threshold_secs * _session->sample_rate());
8992 
8993 	float leave_secs;
8994 
8995 	if (sscanf (e2.get_text().c_str(), "%f", &leave_secs) != 1) {
8996 		ArdourMessageDialog msg (_("The leave-gap value you entered is not a number"));
8997 		msg.run();
8998 		goto again;
8999 	}
9000 
9001 	if (leave_secs < 0) {
9002 		ArdourMessageDialog msg (_("The threshold value must be larger than or equal to zero"));
9003 		msg.run ();
9004 		goto again;
9005 	}
9006 
9007 	samplecnt_t leave_samples = (samplecnt_t) floor (leave_secs * _session->sample_rate());
9008 
9009 	d.hide ();
9010 
9011 	remove_gaps (threshold_samples, leave_samples, markers_too.get_active());
9012 }
9013 
9014 /* one day, we can use an empty lambda for this */
9015 static
gap_marker_callback_relax(samplepos_t,samplecnt_t)9016 void gap_marker_callback_relax (samplepos_t, samplecnt_t)
9017 {
9018 }
9019 
9020 void
remove_gap_marker_callback(samplepos_t at,samplecnt_t distance)9021 Editor::remove_gap_marker_callback (samplepos_t at, samplecnt_t distance)
9022 {
9023 	_session->locations()->ripple (at, distance, false, false);
9024 }
9025 
9026 void
remove_gaps(samplecnt_t gap_threshold,samplecnt_t leave_gap,bool markers_too)9027 Editor::remove_gaps (samplecnt_t gap_threshold, samplecnt_t leave_gap, bool markers_too)
9028 {
9029 	bool in_command = false;
9030 	TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
9031 	XMLNode* locations_before (0);
9032 
9033 	if (markers_too) {
9034 		locations_before = &_session->locations()->get_state();
9035 	}
9036 
9037 	set<boost::shared_ptr<Playlist> > pl;
9038 
9039 	/* it will not be possible to infer this from the set<>, so keep track
9040 	 * of it explicitly
9041 	 */
9042 
9043 	boost::shared_ptr<Playlist> first_selected_playlist;
9044 
9045 	for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
9046 
9047 		/* don't operate on any playlist more than once, which could
9048 		 * happen if there is more than 1 track using the same
9049 		 * playlist.
9050 		 */
9051 
9052 		if ((*x)->playlist ()) {
9053 			if (!first_selected_playlist) {
9054 				first_selected_playlist = (*x)->playlist();
9055 			}
9056 			pl.insert ((*x)->playlist ());
9057 		}
9058 	}
9059 
9060 	for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
9061 
9062 		(*i)->clear_changes ();
9063 		(*i)->clear_owned_changes ();
9064 
9065 		if (!in_command) {
9066 			begin_reversible_command (_("remove gaps"));
9067 			in_command = true;
9068 		}
9069 
9070 		/* only move markers when closing gaps on the first
9071 		 * selected track/playlist
9072 		 */
9073 
9074 		if (markers_too && (*i == first_selected_playlist)) {
9075 			boost::function<void (samplepos_t, samplecnt_t)> callback (boost::bind (&Editor::remove_gap_marker_callback, this, _1, _2));
9076 			(*i)->remove_gaps (gap_threshold, leave_gap, callback);
9077 		} else {
9078 			boost::function<void (samplepos_t, samplecnt_t)> callback (boost::bind (gap_marker_callback_relax, _1, _2));
9079 			(*i)->remove_gaps (gap_threshold, leave_gap, callback);
9080 		}
9081 
9082 		vector<Command*> cmds;
9083 		(*i)->rdiff (cmds);
9084 		_session->add_commands (cmds);
9085 		_session->add_command (new StatefulDiffCommand (*i));
9086 	}
9087 
9088 	if (in_command) {
9089 		if (markers_too) {
9090 			XMLNode* locations_after = &_session->locations()->get_state();
9091 			_session->add_command (new MementoCommand<Locations> (*_session->locations(), locations_before, locations_after));
9092 		}
9093 		commit_reversible_command ();
9094 	} else {
9095 		if (markers_too) {
9096 			delete locations_before;
9097 		}
9098 	}
9099 }
9100