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