~vcs-imports/ardour/master

« back to all changes in this revision

Viewing changes to gtk2_ardour/editor_ops.cc.orig

  • Committer: Paul Davis
  • Date: 2017-09-18 15:40:54 UTC
  • Revision ID: git-v1:cba53a20233531ef3e6c3692993eac8f74e991a1
add _locked() variants to new tempo experiment

Show diffs side-by-side

added added

removed removed

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