#include "pbd/basename.h"
#include "pbd/pthread_utils.h"
#include "pbd/memento_command.h"
+#include "pbd/unwind.h"
#include "pbd/whitespace.h"
#include "pbd/stateful_diff_command.h"
#include <gtkmm2ext/choice.h>
#include <gtkmm2ext/popup.h>
-#include "ardour/audioengine.h"
-#include "ardour/session.h"
-#include "ardour/audioplaylist.h"
+#include "ardour/audio_track.h"
#include "ardour/audioregion.h"
-#include "ardour/audio_diskstream.h"
-#include "ardour/utils.h"
+#include "ardour/dB.h"
#include "ardour/location.h"
-#include "ardour/audio_track.h"
-#include "ardour/audioplaylist.h"
-#include "ardour/region_factory.h"
+#include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
+#include "ardour/operations.h"
#include "ardour/playlist_factory.h"
-#include "ardour/reverse.h"
-#include "ardour/transient_detector.h"
-#include "ardour/dB.h"
#include "ardour/quantize.h"
+#include "ardour/region_factory.h"
+#include "ardour/reverse.h"
+#include "ardour/session.h"
+#include "ardour/session_playlists.h"
#include "ardour/strip_silence.h"
-#include "ardour/route_group.h"
-#include "ardour/operations.h"
+#include "ardour/transient_detector.h"
+
+#include "canvas/canvas.h"
#include "ardour_ui.h"
#include "debug.h"
#include "route_time_axis.h"
#include "audio_time_axis.h"
#include "automation_time_axis.h"
+#include "control_point.h"
#include "streamview.h"
#include "audio_streamview.h"
#include "audio_region_view.h"
#include "editor_cursors.h"
#include "mouse_cursors.h"
#include "patch_change_dialog.h"
+#include "transpose_dialog.h"
#include "i18n.h"
void
Editor::undo (uint32_t n)
{
+ if (_drags->active ()) {
+ _drags->abort ();
+ }
+
if (_session) {
_session->undo (n);
}
void
Editor::redo (uint32_t n)
{
+ if (_drags->active ()) {
+ _drags->abort ();
+ }
+
if (_session) {
_session->redo (n);
}
}
} else {
snap_to (where);
-
+
frozen = true;
EditorFreeze(); /* Emit Signal */
}
}
commit_reversible_command ();
-
+
if (frozen){
EditorThaw(); /* Emit Signal */
}
}
-boost::shared_ptr<Region>
-Editor::select_region_for_operation (int /*dir*/, TimeAxisView **tv)
+/** Move one extreme of the current range selection. If more than one range is selected,
+ * the start of the earliest range or the end of the latest range is moved.
+ *
+ * @param move_end true to move the end of the current range selection, false to move
+ * the start.
+ * @param next true to move the extreme to the next region boundary, false to move to
+ * the previous.
+ */
+void
+Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
{
- RegionView* rv;
- boost::shared_ptr<Region> region;
- framepos_t start = 0;
-
- if (selection->time.start () == selection->time.end_frame ()) {
-
- /* no current selection-> is there a selected regionview? */
-
- if (selection->regions.empty()) {
- return region;
- }
-
+ if (selection->time.start() == selection->time.end_frame()) {
+ return;
}
- if (!selection->regions.empty()) {
-
- rv = *(selection->regions.begin());
- (*tv) = &rv->get_time_axis_view();
- region = rv->region();
-
- } else if (!selection->tracks.empty()) {
-
- (*tv) = selection->tracks.front();
-
- RouteTimeAxisView* rtv;
-
- if ((rtv = dynamic_cast<RouteTimeAxisView*> (*tv)) != 0) {
- boost::shared_ptr<Playlist> pl;
+ framepos_t start = selection->time.start ();
+ framepos_t end = selection->time.end_frame ();
- if ((pl = rtv->playlist()) == 0) {
- return region;
- }
+ /* the position of the thing we may move */
+ framepos_t pos = move_end ? end : start;
+ int dir = next ? 1 : -1;
- region = pl->top_region_at (start);
- }
+ /* so we don't find the current region again */
+ if (dir > 0 || pos > 0) {
+ pos += dir;
}
- return region;
-}
-
-void
-Editor::extend_selection_to_end_of_region (bool next)
-{
- TimeAxisView *tv;
- boost::shared_ptr<Region> region;
- framepos_t start;
-
- if ((region = select_region_for_operation (next ? 1 : 0, &tv)) == 0) {
+ framepos_t const target = get_region_boundary (pos, dir, true, false);
+ if (target < 0) {
return;
}
- if (region && selection->time.start () == selection->time.end_frame ()) {
- start = region->position();
+ if (move_end) {
+ end = target;
} else {
- start = selection->time.start ();
+ start = target;
}
- begin_reversible_command (_("extend selection"));
- selection->set (start, region->position() + region->length());
- commit_reversible_command ();
-}
-
-void
-Editor::extend_selection_to_start_of_region (bool previous)
-{
- TimeAxisView *tv;
- boost::shared_ptr<Region> region;
- framepos_t end;
-
- if ((region = select_region_for_operation (previous ? -1 : 0, &tv)) == 0) {
+ if (end < start) {
return;
}
- if (region && selection->time.start () == selection->time.end_frame ()) {
- end = region->position() + region->length();
- } else {
- end = selection->time.end_frame ();
- }
-
- /* Try to leave the selection with the same route if possible */
-
- begin_reversible_command (_("extend selection"));
- selection->set (region->position(), end);
+ begin_reversible_command (_("alter selection"));
+ selection->set_preserving_all_ranges (start, end);
commit_reversible_command ();
}
}
r->clear_changes ();
- r->set_position (r->position() + distance, this);
+ r->set_position (r->position() + distance);
_session->add_command (new StatefulDiffCommand (r));
}
commit_reversible_command ();
} else {
- distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
- _session->request_locate (playhead_cursor->current_frame + distance);
+ distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
+ _session->request_locate (playhead_cursor->current_frame () + distance);
}
}
if (next) {
distance = next_distance;
}
-
+
r->clear_changes ();
if (r->position() > distance) {
- r->set_position (r->position() - distance, this);
+ r->set_position (r->position() - distance);
} else {
- r->set_position (0, this);
+ r->set_position (0);
}
_session->add_command (new StatefulDiffCommand (r));
}
} else {
- distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
+ distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
- if (playhead_cursor->current_frame > distance) {
- _session->request_locate (playhead_cursor->current_frame - distance);
+ if (playhead_cursor->current_frame () > distance) {
+ _session->request_locate (playhead_cursor->current_frame () - distance);
} else {
_session->goto_start();
}
Editor::nudge_forward_capture_offset ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (!_session || rs.empty()) {
return;
}
begin_reversible_command (_("nudge forward"));
-
+
framepos_t const distance = _session->worst_output_latency();
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
boost::shared_ptr<Region> r ((*i)->region());
-
+
r->clear_changes ();
- r->set_position (r->position() + distance, this);
+ r->set_position (r->position() + distance);
_session->add_command(new StatefulDiffCommand (r));
}
-
+
commit_reversible_command ();
}
return;
}
- begin_reversible_command (_("nudge forward"));
-
+ begin_reversible_command (_("nudge backward"));
+
framepos_t const distance = _session->worst_output_latency();
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
boost::shared_ptr<Region> r ((*i)->region());
-
+
r->clear_changes ();
-
+
if (r->position() > distance) {
- r->set_position (r->position() - distance, this);
+ r->set_position (r->position() - distance);
} else {
- r->set_position (0, this);
+ r->set_position (0);
}
_session->add_command(new StatefulDiffCommand (r));
}
-
+
commit_reversible_command ();
}
TimeAxisView *ontrack = 0;
TrackViewList tlist;
-
+
if (!selection->tracks.empty()) {
- tlist = selection->tracks;
+ tlist = selection->tracks.filter_to_unique_playlists ();
} else {
- tlist = track_views;
+ tlist = track_views.filter_to_unique_playlists ();
}
while (pos < _session->current_end_frame() && !at_end) {
for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
framepos_t contender;
framecnt_t d;
-
+
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
if (!rtv) {
void
Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
{
- framepos_t pos = playhead_cursor->current_frame;
+ framepos_t pos = playhead_cursor->current_frame ();
framepos_t target;
if (!_session) {
Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
{
boost::shared_ptr<Region> r;
- framepos_t pos = cursor->current_frame;
+ framepos_t pos = cursor->current_frame ();
if (!_session) {
return;
void
Editor::scroll_playhead (bool forward)
{
- framepos_t pos = playhead_cursor->current_frame;
+ framepos_t pos = playhead_cursor->current_frame ();
framecnt_t delta = (framecnt_t) floor (current_page_frames() / 0.8);
if (forward) {
Location* loc = find_location_from_marker (*i, ignored);
if (loc->is_mark()) {
- loc->set_start (playhead_cursor->current_frame);
+ loc->set_start (playhead_cursor->current_frame ());
} else {
- loc->set (playhead_cursor->current_frame,
- playhead_cursor->current_frame + loc->length());
+ loc->set (playhead_cursor->current_frame (),
+ playhead_cursor->current_frame () + loc->length());
}
}
}
void
Editor::scroll_backward (float pages)
{
- framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
+ framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * frames_per_pixel);
framepos_t const cnt = (framepos_t) floor (pages * one_page);
framepos_t frame;
void
Editor::scroll_forward (float pages)
{
- framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
+ framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * frames_per_pixel);
framepos_t const cnt = (framepos_t) floor (pages * one_page);
framepos_t frame;
Editor::scroll_tracks_down ()
{
double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
- if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
- vert_value = vertical_adjustment.get_upper() - _canvas_height;
+ if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
+ vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
}
-
+
vertical_adjustment.set_value (vert_value);
}
{
double vert_value = vertical_adjustment.get_value() + 60;
- if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
- vert_value = vertical_adjustment.get_upper() - _canvas_height;
+ if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
+ vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
}
-
+
vertical_adjustment.set_value (vert_value);
}
void
Editor::tav_zoom_step (bool coarser)
{
- ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
-
_routes->suspend_redisplay ();
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ TrackViewList* ts;
+
+ if (selection->tracks.empty()) {
+ ts = &track_views;
+ } else {
+ ts = &selection->tracks;
+ }
+
+ for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
tv->step_height (coarser);
}
_routes->resume_redisplay ();
}
+void
+Editor::tav_zoom_smooth (bool coarser, bool force_all)
+{
+ _routes->suspend_redisplay ();
+
+ TrackViewList* ts;
+
+ if (selection->tracks.empty() || force_all) {
+ ts = &track_views;
+ } else {
+ ts = &selection->tracks;
+ }
+
+ for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
+ TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
+ uint32_t h = tv->current_height ();
+
+ if (coarser) {
+ if (h > 5) {
+ h -= 5; // pixels
+ if (h >= TimeAxisView::preset_height (HeightSmall)) {
+ tv->set_height (h);
+ }
+ }
+ } else {
+ tv->set_height (h + 5);
+ }
+ }
+
+ _routes->resume_redisplay ();
+}
+
+bool
+Editor::clamp_frames_per_pixel (double& fpp) const
+{
+ bool clamped = false;
+
+ if (fpp < 2.0) {
+ fpp = 2.0;
+ clamped = true;
+ }
+
+ if (max_framepos / fpp < 800) {
+ fpp = max_framepos / 800.0;
+ clamped = true;
+ }
+
+ return clamped;
+}
+
void
Editor::temporal_zoom_step (bool coarser)
{
ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
- double nfpu;
-
- nfpu = frames_per_unit;
+ double nfpp = frames_per_pixel;
if (coarser) {
- nfpu *= 1.61803399;
+ nfpp = min (9e6, nfpp * 1.61803399);
} else {
- nfpu = max(1.0,(nfpu/1.61803399));
+ nfpp = max (1.0, nfpp / 1.61803399);
}
- temporal_zoom (nfpu);
+ temporal_zoom (nfpp);
}
void
-Editor::temporal_zoom (gdouble fpu)
+Editor::temporal_zoom (double fpp)
{
- if (!_session) return;
+ if (!_session) {
+ return;
+ }
framepos_t current_page = current_page_frames();
framepos_t current_leftmost = leftmost_frame;
framepos_t leftmost_after_zoom = 0;
framepos_t where;
bool in_track_canvas;
- double nfpu;
+ double nfpp;
double l;
- /* XXX this limit is also in ::set_frames_per_unit() */
-
- if (frames_per_unit <= 1.0 && fpu <= frames_per_unit) {
+ clamp_frames_per_pixel (fpp);
+ if (fpp == frames_per_pixel) {
return;
}
- nfpu = fpu;
+ nfpp = fpp;
+
+ // Imposing an arbitrary limit to zoom out as too much zoom out produces
+ // segfaults for lack of memory. If somebody decides this is not high enough I
+ // believe it can be raisen to higher values but some limit must be in place.
+ if (nfpp > 8e+08) {
+ nfpp = 8e+08;
+ }
- new_page_size = (framepos_t) floor (_canvas_width * nfpu);
+ new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
half_page_size = new_page_size / 2;
switch (zoom_focus) {
case ZoomFocusPlayhead:
/* centre playhead */
- l = playhead_cursor->current_frame - (new_page_size * 0.5);
+ l = playhead_cursor->current_frame () - (new_page_size * 0.5);
if (l < 0) {
leftmost_after_zoom = 0;
if (!mouse_frame (where, in_track_canvas)) {
/* use playhead instead */
- where = playhead_cursor->current_frame;
+ where = playhead_cursor->current_frame ();
if (where < half_page_size) {
leftmost_after_zoom = 0;
// leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
- reposition_and_zoom (leftmost_after_zoom, nfpu);
+ reposition_and_zoom (leftmost_after_zoom, nfpp);
}
void
}
framepos_t range = end - start;
- double new_fpu = (double)range / (double)_canvas_width;
- framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
+ double new_fpp = (double) range / (double) _visible_canvas_width;
+ framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
if (start > extra_samples) {
start -= extra_samples;
end = max_framepos;
}
- if (both_axes) {
- /* save visual state with track states included, and prevent
- set_frames_per_unit() from doing it again.
- */
- undo_visual_stack.push_back (current_visual_state(true));
- no_save_visual = true;
- }
+ /* if we're zooming on both axes we need to save track heights etc.
+ */
+
+ undo_visual_stack.push_back (current_visual_state (both_axes));
- temporal_zoom_by_frame (start, end, "zoom to region");
+ PBD::Unwinder<bool> nsv (no_save_visual, true);
+ temporal_zoom_by_frame (start, end);
+
if (both_axes) {
- uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
+ uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
/* set visible track heights appropriately */
_routes->resume_redisplay ();
vertical_adjustment.set_value (0.0);
- no_save_visual = false;
}
- redo_visual_stack.push_back (current_visual_state());
+ redo_visual_stack.push_back (current_visual_state (both_axes));
}
void
framepos_t start = selection->time[clicked_selection].start;
framepos_t end = selection->time[clicked_selection].end;
- temporal_zoom_by_frame (start, end, "zoom to selection");
+ temporal_zoom_by_frame (start, end);
}
void
s = 0;
}
framecnt_t const e = _session->current_end_frame() + l * 0.01;
- temporal_zoom_by_frame (framecnt_t (s), e, "zoom to _session");
+ temporal_zoom_by_frame (framecnt_t (s), e);
}
}
void
-Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end, const string & /*op*/)
+Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
{
if (!_session) return;
framepos_t range = end - start;
- double new_fpu = (double)range / (double)_canvas_width;
+ double const new_fpp = (double) range / (double) _visible_canvas_width;
- framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
- framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
- framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
+ framepos_t new_page = (framepos_t) floor (_visible_canvas_width * new_fpp);
+ framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
+ framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
if (new_leftmost > middle) {
new_leftmost = 0;
}
- reposition_and_zoom (new_leftmost, new_fpu);
+ if (new_leftmost < 0) {
+ new_leftmost = 0;
+ }
+
+ reposition_and_zoom (new_leftmost, new_fpp);
}
void
return;
}
double range_before = frame - leftmost_frame;
- double new_fpu;
+ double new_fpp;
- new_fpu = frames_per_unit;
+ new_fpp = frames_per_pixel;
if (coarser) {
- new_fpu *= 1.61803399;
+ new_fpp *= 1.61803399;
range_before *= 1.61803399;
} else {
- new_fpu = max(1.0,(new_fpu/1.61803399));
+ new_fpp = max(1.0,(new_fpp/1.61803399));
range_before /= 1.61803399;
}
- if (new_fpu == frames_per_unit) {
+ if (new_fpp == frames_per_pixel) {
return;
}
new_leftmost = 0;
}
- reposition_and_zoom (new_leftmost, new_fpu);
+ reposition_and_zoom (new_leftmost, new_fpp);
}
Editor::add_locations_from_region ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
Editor::add_location_from_region ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
return;
}
- Location *location = _session->locations()->first_location_after (playhead_cursor->current_frame);
+ framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
- if (location) {
- _session->request_locate (location->start(), _session->transport_rolling());
- } else {
- _session->request_locate (_session->current_end_frame());
+ if (pos < 0) {
+ return;
}
+
+ _session->request_locate (pos, _session->transport_rolling());
}
void
return;
}
- Location *location = _session->locations()->first_location_before (playhead_cursor->current_frame);
+ framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
- if (location) {
- _session->request_locate (location->start(), _session->transport_rolling());
- } else {
- _session->goto_start ();
+ if (pos < 0) {
+ return;
}
+
+ _session->request_locate (pos, _session->transport_rolling());
}
void
string markername;
_session->locations()->next_available_name (markername, "mark");
-
+
if (!choose_new_marker_name (markername)) {
return;
}
-
+
_session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
}
Location * looploc = _session->locations()->auto_loop_location();
Location * punchloc = _session->locations()->auto_punch_location();
+ Location * sessionloc = _session->locations()->session_range_location();
_session->locations()->clear_ranges ();
// re-add these
if (looploc) _session->locations()->add (looploc);
if (punchloc) _session->locations()->add (punchloc);
+ if (sessionloc) _session->locations()->add (sessionloc);
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
RouteTimeAxisView *rtv = 0;
boost::shared_ptr<Playlist> playlist;
- track_canvas->window_to_world (x, y, wx, wy);
+ _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
GdkEvent event;
event.type = GDK_BUTTON_RELEASE;
{
double wx, wy;
double cx, cy;
- framepos_t where;
RouteTimeAxisView *dest_rtv = 0;
RouteTimeAxisView *source_rtv = 0;
- track_canvas->window_to_world (x, y, wx, wy);
- wx += horizontal_position ();
- wy += vertical_adjustment.get_value();
+ _track_canvas_viewport->window_to_canvas (x, y, wx, wy);
GdkEvent event;
event.type = GDK_BUTTON_RELEASE;
event.button.x = wx;
event.button.y = wy;
- where = event_frame (&event, &cx, &cy);
+ event_frame (&event, &cx, &cy);
std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
if (tv.first == 0) {
}
if (_session->config.get_external_sync()) {
- switch (_session->config.get_sync_source()) {
+ switch (Config->get_sync_source()) {
case JACK:
break;
default:
_session->request_play_range (&selection->time, true);
}
+framepos_t
+Editor::get_preroll ()
+{
+ return 1.0 /*Config->get_edit_preroll_seconds()*/ * _session->frame_rate();
+}
+
+
+void
+Editor::maybe_locate_with_edit_preroll ( framepos_t location )
+{
+ if ( _session->transport_rolling() || !Config->get_always_play_range() )
+ return;
+
+ location -= get_preroll();
+
+ //don't try to locate before the beginning of time
+ if ( location < 0 )
+ location = 0;
+
+ //if follow_playhead is on, keep the playhead on the screen
+ if ( _follow_playhead )
+ if ( location < leftmost_frame )
+ location = leftmost_frame;
+
+ _session->request_locate( location );
+}
+
+void
+Editor::play_with_preroll ()
+{
+ if (selection->time.empty()) {
+ return;
+ } else {
+ framepos_t preroll = get_preroll();
+
+ framepos_t start = 0;
+ if (selection->time[clicked_selection].start > preroll)
+ start = selection->time[clicked_selection].start - preroll;
+
+ framepos_t end = selection->time[clicked_selection].end + preroll;
+
+ AudioRange ar (start, end, 0);
+ list<AudioRange> lar;
+ lar.push_back (ar);
+
+ _session->request_play_range (&lar, true);
+ }
+}
+
void
Editor::play_location (Location& location)
{
}
}
+void
+Editor::do_layer_operation (LayerOperation op)
+{
+ if (selection->regions.empty ()) {
+ return;
+ }
+
+ bool const multiple = selection->regions.size() > 1;
+ switch (op) {
+ case Raise:
+ if (multiple) {
+ begin_reversible_command (_("raise regions"));
+ } else {
+ begin_reversible_command (_("raise region"));
+ }
+ break;
+
+ case RaiseToTop:
+ if (multiple) {
+ begin_reversible_command (_("raise regions to top"));
+ } else {
+ begin_reversible_command (_("raise region to top"));
+ }
+ break;
+
+ case Lower:
+ if (multiple) {
+ begin_reversible_command (_("lower regions"));
+ } else {
+ begin_reversible_command (_("lower region"));
+ }
+ break;
+
+ case LowerToBottom:
+ if (multiple) {
+ begin_reversible_command (_("lower regions to bottom"));
+ } else {
+ begin_reversible_command (_("lower region"));
+ }
+ break;
+ }
+
+ set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
+ for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->clear_owned_changes ();
+ }
+
+ for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+ boost::shared_ptr<Region> r = (*i)->region ();
+ switch (op) {
+ case Raise:
+ r->raise ();
+ break;
+ case RaiseToTop:
+ r->raise_to_top ();
+ break;
+ case Lower:
+ r->lower ();
+ break;
+ case LowerToBottom:
+ r->lower_to_bottom ();
+ }
+ }
+
+ for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ vector<Command*> cmds;
+ (*i)->rdiff (cmds);
+ _session->add_commands (cmds);
+ }
+
+ commit_reversible_command ();
+}
+
void
Editor::raise_region ()
{
- selection->foreach_region (&Region::raise);
+ do_layer_operation (Raise);
}
void
Editor::raise_region_to_top ()
{
- selection->foreach_region (&Region::raise_to_top);
+ do_layer_operation (RaiseToTop);
}
void
Editor::lower_region ()
{
- selection->foreach_region (&Region::lower);
+ do_layer_operation (Lower);
}
void
Editor::lower_region_to_bottom ()
{
- selection->foreach_region (&Region::lower_to_bottom);
+ do_layer_operation (LowerToBottom);
}
/** Show the region editor for the selected regions */
Editor::rename_region ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
d.set_size_request (300, -1);
d.set_position (Gtk::WIN_POS_MOUSE);
- entry.set_text (selection->regions.front()->region()->name());
+ entry.set_text (rs.front()->region()->name());
entry.select_region (0, -1);
entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
if (ret != RESPONSE_OK) {
return;
}
-
+
std::string str = entry.get_text();
strip_whitespace_edges (str);
if (!str.empty()) {
framepos_t end = 0;
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
internal_start = start - current->position();
RegionFactory::region_name (new_name, current->name(), true);
- PropertyList plist;
-
+ PropertyList plist;
+
plist.add (ARDOUR::Properties::start, current->start() + internal_start);
plist.add (ARDOUR::Properties::length, selection_cnt);
plist.add (ARDOUR::Properties::name, new_name);
framepos_t start = selection->time[clicked_selection].start;
framepos_t end = selection->time[clicked_selection].end;
- sort_track_selection ();
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+ sort_track_selection (ts);
- for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
boost::shared_ptr<Region> current;
boost::shared_ptr<Playlist> playlist;
framepos_t internal_start;
internal_start = start - current->position();
RegionFactory::region_name (new_name, current->name(), true);
- PropertyList plist;
-
+ PropertyList plist;
+
plist.add (ARDOUR::Properties::start, current->start() + internal_start);
plist.add (ARDOUR::Properties::length, end - start + 1);
plist.add (ARDOUR::Properties::name, new_name);
Editor::split_multichannel_region ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
{
switch (rv->region()->coverage (ar->start, ar->end - 1)) {
- case OverlapNone:
+ case Evoral::OverlapNone:
break;
default:
rs->push_back (rv);
t = selection->tracks;
}
- return t;
+ return t.filter_to_unique_playlists();
}
void
RegionSelection new_selection;
TrackViewList tmptracks = get_tracks_for_range_action ();
- sort_track_selection (&tmptracks);
+ sort_track_selection (tmptracks);
for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
rtv->view()->foreach_regionview (sigc::bind (
sigc::ptr_fun (add_if_covered),
&(*t), &new_selection));
-
+
if (!in_command) {
begin_reversible_command (_("separate"));
in_command = true;
}
-
+
/* pick up changes to existing regions */
vector<Command*> cmds;
returns a single range.
*/
- if (mouse_mode == MouseRange && !selection->time.empty()) {
+ if (!selection->time.empty()) {
separate_regions_between (selection->time);
vector<PlaylistState> playlists;
RegionSelection rs;
-
+
rs = get_regions_from_selection_and_entered();
if (!_session || rs.empty()) {
//Partition on the region bounds
playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
-
+
//Re-add region that was just removed due to the partition operation
playlist->add_region( (*rl), (*rl)->first_frame() );
}
{
vector<boost::shared_ptr<Playlist> > playlists;
boost::shared_ptr<Playlist> playlist;
- TrackViewList* ts;
+ TrackViewList ts;
if (selection->tracks.empty()) {
- ts = &track_views;
+ ts = track_views.filter_to_unique_playlists();
} else {
- sort_track_selection ();
- ts = &selection->tracks;
+ ts = selection->tracks.filter_to_unique_playlists ();
}
- for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
+ sort_track_selection (ts);
+
+ for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
RouteTimeAxisView* rtv;
cnt = the_end - the_start + 1;
region->clear_changes ();
- region->trim_to (the_start, cnt, this);
+ region->trim_to (the_start, cnt);
_session->add_command (new StatefulDiffCommand (region));
}
begin_reversible_command (Operations::fill_selection);
- for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+
+ for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
if ((playlist = (*i)->playlist()) == 0) {
continue;
Editor::remove_region_sync ()
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
begin_reversible_command (_("remove region sync"));
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
(*i)->region()->clear_changes ();
(*i)->region()->clear_sync_position ();
_session->add_command(new StatefulDiffCommand ((*i)->region()));
}
-
+
commit_reversible_command ();
}
} else {
begin_reversible_command (_("move region to original position"));
}
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
(*i)->region()->clear_changes ();
- (*i)->region()->move_to_natural_position (this);
+ (*i)->region()->move_to_natural_position ();
_session->add_command (new StatefulDiffCommand ((*i)->region()));
}
-
+
commit_reversible_command ();
}
Editor::align_regions (RegionPoint what)
{
RegionSelection const rs = get_regions_from_selection_and_edit_point ();
-
+
if (rs.empty()) {
return;
}
Editor::align_regions_relative (RegionPoint point)
{
RegionSelection const rs = get_regions_from_selection_and_edit_point ();
-
+
if (rs.empty()) {
return;
}
/* move first one specially */
r->clear_changes ();
- r->set_position (pos, this);
+ r->set_position (pos);
_session->add_command(new StatefulDiffCommand (r));
/* move rest by the same amount */
region->clear_changes ();
if (dir > 0) {
- region->set_position (region->position() + distance, this);
+ region->set_position (region->position() + distance);
} else {
- region->set_position (region->position() - distance, this);
+ region->set_position (region->position() - distance);
}
-
+
_session->add_command(new StatefulDiffCommand (region));
}
switch (point) {
case SyncPoint:
- region->set_position (region->adjust_to_sync (position), this);
+ region->set_position (region->adjust_to_sync (position));
break;
case End:
if (position > region->length()) {
- region->set_position (position - region->length(), this);
+ region->set_position (position - region->length());
}
break;
case Start:
- region->set_position (position, this);
+ region->set_position (position);
break;
}
framepos_t where = get_preferred_edit_position();
RegionSelection rs = get_regions_from_selection_and_edit_point ();
- cerr << "trim regions\n";
-
if (rs.empty()) {
- cerr << " no regions\n";
return;
}
- cerr << "where = " << where << endl;
-
begin_reversible_command (front ? _("trim front") : _("trim back"));
for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
if (!(*i)->region()->locked()) {
-
+
(*i)->region()->clear_changes ();
-
+
if (front) {
- (*i)->region()->trim_front (where, this);
+ (*i)->region()->trim_front (where);
+ maybe_locate_with_edit_preroll ( where );
} else {
- (*i)->region()->trim_end (where, this);
+ (*i)->region()->trim_end (where);
+ maybe_locate_with_edit_preroll ( where );
}
-
+
_session->add_command (new StatefulDiffCommand ((*i)->region()));
}
}
/* require region to span proposed trim */
switch (rv->region()->coverage (loc.start(), loc.end())) {
- case OverlapInternal:
+ case Evoral::OverlapInternal:
break;
default:
continue;
start = session_frame_to_track_frame (loc.start(), speed);
end = session_frame_to_track_frame (loc.end(), speed);
-
+
rv->region()->clear_changes ();
- rv->region()->trim_to (start, (end - start), this);
+ rv->region()->trim_to (start, (end - start));
_session->add_command(new StatefulDiffCommand (rv->region()));
}
continue;
}
- region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed), this);
+ region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
arv->region_changed (PropertyChange (ARDOUR::Properties::length));
}
else {
continue;
}
- region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed), this);
+ region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
arv->region_changed (ARDOUR::bounds_change);
}
void*
Editor::_freeze_thread (void* arg)
{
- SessionEvent::create_per_thread_pool ("freeze events", 64);
-
return static_cast<Editor*>(arg)->freeze_thread ();
}
void*
Editor::freeze_thread ()
{
+ /* create event pool because we may need to talk to the session */
+ SessionEvent::create_per_thread_pool ("freeze events", 64);
+ /* create per-thread buffers for process() tree to use */
+ current_interthread_info->process_thread.get_buffers ();
clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
current_interthread_info->done = true;
+ current_interthread_info->process_thread.drop_buffers();
return 0;
}
void
Editor::freeze_route ()
{
+ if (!_session) {
+ return;
+ }
+
+ /* stop transport before we start. this is important */
+
+ _session->request_transport_speed (0.0);
+
+ /* wait for just a little while, because the above call is asynchronous */
+
+ ::usleep (250000);
+
if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
return;
}
- if (!clicked_routeview->track()->bounceable()) {
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_routeview);
- if (rtv && !rtv->track()->bounceable()) {
- MessageDialog d (
- _("This route cannot be frozen because it has more outputs than inputs. "
- "You can fix this by increasing the number of inputs.")
- );
- d.set_title (_("Cannot freeze"));
- d.run ();
+ if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
+ MessageDialog d (
+ _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
+ "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
+ );
+ d.set_title (_("Cannot freeze"));
+ d.run ();
+ return;
+ }
+
+ if (clicked_routeview->track()->has_external_redirects()) {
+ 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"
+ "Freezing will only process the signal as far as the first send/insert/return."),
+ clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
+
+ d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
+ d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
+ d.set_title (_("Freeze Limits"));
+
+ int response = d.run ();
+
+ switch (response) {
+ case Gtk::RESPONSE_CANCEL:
return;
+ default:
+ break;
}
}
TrackSelection views = selection->tracks;
for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
- if (rtv && !rtv->track()->bounceable()) {
- MessageDialog d (
- _("One or more selected tracks cannot be bounced because it has more outputs than inputs. "
- "You can fix this by increasing the number of inputs on that track.")
- );
- d.set_title (_("Cannot bounce"));
- d.run ();
- return;
+
+ if (enable_processing) {
+
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
+
+ if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
+ MessageDialog d (
+ _("You can't perform this operation because the processing of the signal "
+ "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
+ "You can do this without processing, which is a different operation.")
+ );
+ d.set_title (_("Cannot bounce"));
+ d.run ();
+ return;
+ }
}
}
playlist->clear_changes ();
playlist->clear_owned_changes ();
-
- boost::shared_ptr<Region> r = rtv->track()->bounce_range (start, start+cnt, itt, enable_processing);
+
+ boost::shared_ptr<Region> r;
+
+ if (enable_processing) {
+ r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
+ } else {
+ r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
+ }
+
+ if (!r) {
+ continue;
+ }
if (replace) {
list<AudioRange> ranges;
commit_reversible_command ();
}
+/** Delete selected regions, automation points or a time range */
+void
+Editor::delete_ ()
+{
+ cut_copy (Delete);
+}
+
/** Cut selected regions, automation points or a time range */
void
Editor::cut ()
bool
Editor::can_cut_copy () const
{
- switch (current_mouse_mode()) {
+ switch (effective_mouse_mode()) {
case MouseObject:
if (!selection->regions.empty() || !selection->points.empty()) {
string opname;
switch (op) {
+ case Delete:
+ opname = _("delete");
+ break;
case Cut:
opname = _("cut");
break;
this function.
*/
- if (op == Cut || op == Clear) {
+ if (op == Delete || op == Cut || op == Clear) {
if (_drags->active ()) {
_drags->abort ();
}
}
- cut_buffer->clear ();
+ if ( op != Clear ) //"Delete" doesn't change copy/paste buf
+ cut_buffer->clear ();
if (entered_marker) {
if (internal_editing()) {
- switch (current_mouse_mode()) {
+ switch (effective_mouse_mode()) {
case MouseObject:
case MouseRange:
cut_copy_midi (op);
} else {
- RegionSelection rs;
+ RegionSelection rs;
- /* we only want to cut regions if some are selected */
+ /* we only want to cut regions if some are selected */
- if (!selection->regions.empty()) {
- rs = selection->regions;
- }
+ if (!selection->regions.empty()) {
+ rs = selection->regions;
+ }
- switch (current_mouse_mode()) {
- case MouseObject:
+ switch (effective_mouse_mode()) {
+/*
+ * case MouseGain: {
+ //find regions's gain line
+ AudioRegionView *rview = dynamic_cast<AudioRegionView*>(clicked_regionview);
+ AutomationTimeAxisView *tview = dynamic_cast<AutomationTimeAxisView*>(clicked_trackview);
+ if (rview) {
+ AudioRegionGainLine *line = rview->get_gain_line();
+ if (!line) break;
+
+ //cut region gain points in the selection
+ AutomationList& alist (line->the_list());
+ XMLNode &before = alist.get_state();
+ AutomationList* what_we_got = 0;
+ if ((what_we_got = alist.cut (selection->time.front().start - rview->audio_region()->position(), selection->time.front().end - rview->audio_region()->position())) != 0) {
+ session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
+ delete what_we_got;
+ what_we_got = 0;
+ }
+
+ rview->set_envelope_visible(true);
+ rview->audio_region()->set_envelope_active(true);
+
+ } else if (tview) {
+ AutomationLine *line = *(tview->lines.begin());
+ if (!line) break;
+
+ //cut auto points in the selection
+ AutomationList& alist (line->the_list());
+ XMLNode &before = alist.get_state();
+ AutomationList* what_we_got = 0;
+ if ((what_we_got = alist.cut (selection->time.front().start, selection->time.front().end)) != 0) {
+ session->add_command(new MementoCommand<AutomationList>(alist, &before, &alist.get_state()));
+ delete what_we_got;
+ what_we_got = 0;
+ }
+ } else
+ break;
+ } break;
+*/
+ case MouseObject:
+ case MouseRange:
if (!rs.empty() || !selection->points.empty()) {
-
begin_reversible_command (opname + _(" objects"));
if (!rs.empty()) {
cut_copy_regions (op, rs);
-
- if (op == Cut) {
+
+ if (op == Cut || op == Delete) {
selection->clear_regions ();
}
}
if (!selection->points.empty()) {
cut_copy_points (op);
- if (op == Cut) {
+ if (op == Cut || op == Delete) {
selection->clear_points ();
}
}
- commit_reversible_command ();
- break; // terminate case statement here
- }
- if (!selection->time.empty()) {
- /* don't cause suprises */
+ commit_reversible_command ();
break;
- }
- // fall thru if there was nothing selected
-
- case MouseRange:
+ }
+
if (selection->time.empty()) {
framepos_t start, end;
if (!get_edit_op_range (start, end)) {
}
selection->set (start, end);
}
-
+
begin_reversible_command (opname + _(" range"));
cut_copy_ranges (op);
commit_reversible_command ();
-
- if (op == Cut) {
+
+ if (op == Cut || op == Delete) {
selection->clear_time ();
}
break;
-
+
default:
break;
}
}
- if (op == Cut || op == Clear) {
+ if (op == Delete || op == Cut || op == Clear) {
_drags->abort ();
}
}
+struct AutomationRecord {
+ AutomationRecord () : state (0) {}
+ AutomationRecord (XMLNode* s) : state (s) {}
+
+ XMLNode* state; ///< state before any operation
+ boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
+};
+
/** Cut, copy or clear selected automation points.
- * @param op Operation (Cut, Copy or Clear)
+ * @param op Operation (Cut, Copy or Clear)
*/
void
Editor::cut_copy_points (CutCopyOp op)
{
+ if (selection->points.empty ()) {
+ return;
+ }
+
+ /* XXX: not ideal, as there may be more than one track involved in the point selection */
+ _last_cut_copy_source_track = &selection->points.front()->line().trackview;
+
+ /* Keep a record of the AutomationLists that we end up using in this operation */
+ typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
+ Lists lists;
+
+ /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
+ boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
+ if (lists.find (al) == lists.end ()) {
+ /* We haven't seen this list yet, so make a record for it. This includes
+ taking a copy of its current state, in case this is needed for undo later.
+ */
+ lists[al] = AutomationRecord (&al->get_state ());
+ }
+ }
+
+ if (op == Cut || op == Copy) {
+ /* This operation will involve putting things in the cut buffer, so create an empty
+ ControlList for each of our source lists to put the cut buffer data in.
+ */
+ for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
+ i->second.copy = i->first->create (i->first->parameter ());
+ }
+
+ /* Add all selected points to the relevant copy ControlLists */
+ for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
+ boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
+ AutomationList::const_iterator j = (*i)->model ();
+ lists[al].copy->add ((*j)->when, (*j)->value);
+ }
+
+ for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
+ /* Correct this copy list so that it starts at time 0 */
+ double const start = i->second.copy->front()->when;
+ for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
+ (*j)->when -= start;
+ }
+
+ /* And add it to the cut buffer */
+ cut_buffer->add (i->second.copy);
+ }
+ }
+
+ if (op == Delete || op == Cut) {
+ /* This operation needs to remove things from the main AutomationList, so do that now */
+
+ for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
+ i->first->freeze ();
+ }
- AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
+ /* Remove each selected point from its AutomationList */
+ for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
+ boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
+ al->erase ((*i)->model ());
+ }
- if (atv) {
- atv->cut_copy_clear_objects (selection->points, op);
+ /* Thaw the lists and add undo records for them */
+ for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
+ boost::shared_ptr<AutomationList> al = i->first;
+ al->thaw ();
+ _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
}
}
}
begin_reversible_command (_("remove region"));
playlist->clear_changes ();
+ playlist->clear_owned_changes ();
playlist->remove_region (clicked_regionview->region());
+
+ /* We might have removed regions, which alters other regions' layering_index,
+ so we need to do a recursive diff here.
+ */
+ vector<Command*> cmds;
+ playlist->rdiff (cmds);
+ _session->add_commands (cmds);
+
_session->add_command(new StatefulDiffCommand (playlist));
commit_reversible_command ();
}
continue;
}
- vector<boost::shared_ptr<Playlist> >::iterator i;
-
- //only prep history if this is a new playlist.
- for (i = playlists.begin(); i != playlists.end(); ++i) {
- if ((*i) == playlist) {
- break;
- }
- }
-
- if (i == playlists.end()) {
-
- playlist->clear_changes ();
- playlist->freeze ();
+ /* get_regions_from_selection_and_entered() guarantees that
+ the playlists involved are unique, so there is no need
+ to check here.
+ */
- playlists.push_back (playlist);
- }
+ playlists.push_back (playlist);
+ playlist->clear_changes ();
+ playlist->clear_owned_changes ();
+ playlist->freeze ();
playlist->remove_region (*rl);
}
for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
(*pl)->thaw ();
+
+ /* We might have removed regions, which alters other regions' layering_index,
+ so we need to do a recursive diff here.
+ */
+ vector<Command*> cmds;
+ (*pl)->rdiff (cmds);
+ _session->add_commands (cmds);
+
_session->add_command(new StatefulDiffCommand (*pl));
}
first_position = min ((framepos_t) (*x)->region()->position(), first_position);
- if (op == Cut || op == Clear) {
+ if (op == Cut || op == Clear || op == Delete) {
boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
if (pl) {
FreezeList::iterator fl;
- //only take state if this is a new playlist.
+ // only take state if this is a new playlist.
for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
if ((*fl) == pl) {
break;
if (fl == freezelist.end()) {
pl->clear_changes();
+ pl->clear_owned_changes ();
pl->freeze ();
freezelist.insert (pl);
}
tmp = x;
++tmp;
- vector<PlaylistMapping>::iterator z;
+ if (op != Delete) {
- for (z = pmap.begin(); z != pmap.end(); ++z) {
- if ((*z).tv == &tv) {
- break;
+ vector<PlaylistMapping>::iterator z;
+
+ for (z = pmap.begin(); z != pmap.end(); ++z) {
+ if ((*z).tv == &tv) {
+ break;
+ }
+ }
+
+ assert (z != pmap.end());
+
+ if (!(*z).pl) {
+ npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
+ npl->freeze();
+ (*z).pl = npl;
+ } else {
+ npl = (*z).pl;
}
- }
-
- assert (z != pmap.end());
-
- if (!(*z).pl) {
- npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
- npl->freeze();
- (*z).pl = npl;
- } else {
- npl = (*z).pl;
}
boost::shared_ptr<Region> r = (*x)->region();
assert (r != 0);
switch (op) {
+ case Delete:
+ pl->remove_region (r);
+ break;
+
case Cut:
_xx = RegionFactory::create (r);
npl->add_region (_xx, r->position() - first_position);
break;
case Clear:
- pl->remove_region (r);
+ pl->remove_region (r);
break;
}
x = tmp;
}
- list<boost::shared_ptr<Playlist> > foo;
+ if (op != Delete) {
- /* the pmap is in the same order as the tracks in which selected regions occured */
-
- for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
- if ((*i).pl) {
- (*i).pl->thaw();
- foo.push_back ((*i).pl);
+ list<boost::shared_ptr<Playlist> > foo;
+
+ /* the pmap is in the same order as the tracks in which selected regions occured */
+
+ for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
+ if ((*i).pl) {
+ (*i).pl->thaw();
+ foo.push_back ((*i).pl);
+ }
+ }
+
+ if (!foo.empty()) {
+ cut_buffer->set (foo);
+ }
+
+ if (pmap.empty()) {
+ _last_cut_copy_source_track = 0;
+ } else {
+ _last_cut_copy_source_track = pmap.front().tv;
}
}
- if (!foo.empty()) {
- cut_buffer->set (foo);
- }
-
- if (pmap.empty()) {
- _last_cut_copy_source_track = 0;
- } else {
- _last_cut_copy_source_track = pmap.front().tv;
- }
-
for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
(*pl)->thaw ();
+
+ /* We might have removed regions, which alters other regions' layering_index,
+ so we need to do a recursive diff here.
+ */
+ vector<Command*> cmds;
+ (*pl)->rdiff (cmds);
+ _session->add_commands (cmds);
+
_session->add_command (new StatefulDiffCommand (*pl));
}
}
void
Editor::cut_copy_ranges (CutCopyOp op)
{
- TrackViewList* ts;
- TrackViewList entered;
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
- if (selection->tracks.empty()) {
+ /* Sort the track selection now, so that it if is used, the playlists
+ selected by the calls below to cut_copy_clear are in the order that
+ their tracks appear in the editor. This makes things like paste
+ of ranges work properly.
+ */
+
+ sort_track_selection (ts);
+
+ if (ts.empty()) {
if (!entered_track) {
return;
}
- entered.push_back (entered_track);
- ts = &entered;
- } else {
- ts = &selection->tracks;
- }
+ ts.push_back (entered_track);
+ }
- for (TrackSelection::iterator i = ts->begin(); i != ts->end(); ++i) {
+ for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
(*i)->cut_copy_clear (*selection, op);
}
}
void
-Editor::paste (float times)
+Editor::paste (float times, bool from_context)
{
DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
- paste_internal (get_preferred_edit_position(), times);
+
+ paste_internal (get_preferred_edit_position (false, from_context), times);
}
void
/* get everything in the correct order */
- if (!selection->tracks.empty()) {
- /* there are some selected tracks, so paste to them */
- sort_track_selection ();
- ts = selection->tracks;
+ if (_edit_point == Editing::EditAtMouse && entered_track) {
+ /* With the mouse edit point, paste onto the track under the mouse */
+ ts.push_back (entered_track);
+ } else if (!selection->tracks.empty()) {
+ /* Otherwise, if there are some selected tracks, paste to them */
+ ts = selection->tracks.filter_to_unique_playlists ();
+ sort_track_selection (ts);
} else if (_last_cut_copy_source_track) {
- /* otherwise paste to the track that the cut/copy came from;
- see discussion in mants #3333.
+ /* Otherwise paste to the track that the cut/copy came from;
+ see discussion in mantis #3333.
*/
ts.push_back (_last_cut_copy_source_track);
}
if (internal_editing ()) {
/* undo/redo is handled by individual tracks/regions */
-
+
for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
-
+
RegionSelection rs;
RegionSelection::iterator r;
MidiNoteSelection::iterator cb;
-
+
get_regions_at (rs, position, ts);
-
+
for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
}
}
}
-
+
} else {
/* we do redo (do you do voodoo?) */
begin_reversible_command (Operations::paste);
-
+
for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
(*i)->paste (position, times, *cut_buffer, nth);
}
-
+
commit_reversible_command ();
}
}
ri = new_regions.begin();
- for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+
+ for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
if ((playlist = (*i)->playlist()) == 0) {
continue;
}
commit_reversible_command ();
}
+/** Reset all selected points to the relevant default value */
void
Editor::reset_point_selection ()
{
- /* reset all selected points to the relevant default value */
-
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
-
- AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*>((*i).track);
-
- if (atv) {
- atv->reset_objects (selection->points);
- }
+ ARDOUR::AutomationList::iterator j = (*i)->model ();
+ (*j)->value = (*i)->line().the_list()->default_value ();
}
}
void
Editor::center_playhead ()
{
- float page = _canvas_width * frames_per_unit;
- center_screen_internal (playhead_cursor->current_frame, page);
+ float const page = _visible_canvas_width * frames_per_pixel;
+ center_screen_internal (playhead_cursor->current_frame (), page);
}
void
Editor::center_edit_point ()
{
- float page = _canvas_width * frames_per_unit;
+ float const page = _visible_canvas_width * frames_per_pixel;
center_screen_internal (get_preferred_edit_position(), page);
}
begin_reversible_command (_("nudge track"));
- for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+
+ for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
if ((playlist = (*i)->playlist()) == 0) {
continue;
playlist->clear_owned_changes ();
playlist->nudge_after (start, distance, forwards);
-
+
vector<Command*> cmds;
-
+
playlist->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command (new StatefulDiffCommand (playlist));
}
}
RegionSelection rs = get_regions_from_selection_and_entered ();
-
+
if (rs.empty()) {
return;
}
set_canvas_cursor (current_canvas_cursor);
return;
}
-
+
max_amps.push_back (a);
max_amp = max (max_amp, a);
dialog.ascend ();
begin_reversible_command (_("normalize"));
list<double>::const_iterator a = max_amps.begin ();
-
+
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
if (!arv) {
arv->region()->clear_changes ();
double const amp = dialog.normalize_individually() ? *a : max_amp;
-
+
arv->audio_region()->normalize (amp, dialog.target ());
_session->add_command (new StatefulDiffCommand (arv->region()));
int const r = d.run ();
d.drop_rects ();
-
+
if (r == Gtk::RESPONSE_OK) {
ARDOUR::AudioIntervalMap silences;
d.silences (silences);
StripSilence s (*_session, silences, d.fade_length());
apply_filter (s, _("strip silence"), &d);
- }
+ }
}
Command*
vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
v.push_back (selected);
- return op (mrv.midi_region()->model(), v);
+ framepos_t pos_frames = mrv.midi_region()->position();
+ double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
+
+ return op (mrv.midi_region()->model(), pos_beats, v);
}
void
if (mrv) {
boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
-
+
playlist->clear_changes ();
playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
_session->add_command(new StatefulDiffCommand (playlist));
qd->hide ();
if (r == Gtk::RESPONSE_OK) {
- Quantize quant (*_session, Plain,
- qd->snap_start(), qd->snap_end(),
+ Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
qd->start_grid_size(), qd->end_grid_size(),
qd->strength(), qd->swing(), qd->threshold());
}
void
-Editor::insert_patch_change ()
+Editor::insert_patch_change (bool from_context)
{
RegionSelection rs = get_regions_from_selection_and_entered ();
+
if (rs.empty ()) {
return;
}
- framepos_t const p = get_preferred_edit_position (false);
+ const framepos_t p = get_preferred_edit_position (false, from_context);
+
+ /* XXX: bit of a hack; use the MIDNAM from the first selected region;
+ there may be more than one, but the PatchChangeDialog can only offer
+ one set of patch menus.
+ */
+ MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
- PatchChangeDialog d (0, _session, empty, Gtk::Stock::ADD);
-
+ PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
+
if (d.run() == RESPONSE_CANCEL) {
return;
}
int n = 0;
int const N = rs.size ();
-
+
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
RegionSelection::iterator tmp = r;
++tmp;
if (arv->audio_region()->apply (filter, progress) == 0) {
playlist->clear_changes ();
+ playlist->clear_owned_changes ();
if (filter.results.empty ()) {
}
+ /* We might have removed regions, which alters other regions' layering_index,
+ so we need to do a recursive diff here.
+ */
+ vector<Command*> cmds;
+ playlist->rdiff (cmds);
+ _session->add_commands (cmds);
+
_session->add_command(new StatefulDiffCommand (playlist));
} else {
goto out;
}
void
-Editor::toggle_gain_envelope_visibility ()
+Editor::set_region_gain_visibility (RegionView* rv)
{
- if (_ignore_region_action) {
- return;
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
+ if (arv) {
+ arv->update_envelope_visibility();
}
-
- RegionSelection rs = get_regions_from_selection_and_entered ();
+}
- if (!_session || rs.empty()) {
+void
+Editor::set_gain_envelope_visibility ()
+{
+ if (!_session) {
return;
}
- _session->begin_reversible_command (_("region gain envelope visible"));
-
- for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
- AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
- if (arv) {
- arv->region()->clear_changes ();
- arv->set_envelope_visible (!arv->envelope_visible());
- _session->add_command (new StatefulDiffCommand (arv->region()));
- }
- }
-
- _session->commit_reversible_command ();
-}
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
+ if (v) {
+ v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
+ }
+ }
+}
void
Editor::toggle_gain_envelope_active ()
if (_ignore_region_action) {
return;
}
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (!_session || rs.empty()) {
_session->commit_reversible_command ();
}
+#ifdef WITH_VIDEOTIMELINE
+void
+Editor::toggle_region_video_lock ()
+{
+ if (_ignore_region_action) {
+ return;
+ }
+
+ RegionSelection rs = get_regions_from_selection_and_entered ();
+
+ if (!_session || rs.empty()) {
+ return;
+ }
+
+ _session->begin_reversible_command (_("Toggle Video Lock"));
+
+ for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
+ (*i)->region()->clear_changes ();
+ (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
+ _session->add_command (new StatefulDiffCommand ((*i)->region()));
+ }
+
+ _session->commit_reversible_command ();
+}
+#endif
+
+
void
Editor::toggle_region_lock_style ()
{
if (_ignore_region_action) {
return;
}
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (!_session || rs.empty()) {
if (_ignore_region_action) {
return;
}
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (!_session || rs.empty()) {
}
}
+void
+Editor::toggle_solo ()
+{
+ bool new_state = false;
+ bool first = true;
+ boost::shared_ptr<RouteList> rl (new RouteList);
+
+ for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+
+ if (!rtav) {
+ continue;
+ }
+
+ if (first) {
+ new_state = !rtav->route()->soloed ();
+ first = false;
+ }
+
+ rl->push_back (rtav->route());
+ }
+
+ _session->set_solo (rl, new_state, Session::rt_cleanup, true);
+}
+
+void
+Editor::toggle_mute ()
+{
+ bool new_state = false;
+ bool first = true;
+ boost::shared_ptr<RouteList> rl (new RouteList);
+
+ for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+
+ if (!rtav) {
+ continue;
+ }
+
+ if (first) {
+ new_state = !rtav->route()->muted();
+ first = false;
+ }
+
+ rl->push_back (rtav->route());
+ }
+
+ _session->set_mute (rl, new_state, Session::rt_cleanup, true);
+}
+
+void
+Editor::toggle_solo_isolate ()
+{
+}
void
Editor::set_fade_length (bool in)
boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
-
+
ar->clear_changes ();
ar->set_fade_in_active (yn);
_session->add_command (new StatefulDiffCommand (ar));
void
Editor::toggle_region_fades (int dir)
{
+ if (_ignore_region_action) {
+ return;
+ }
+
boost::shared_ptr<AudioRegion> ar;
- bool yn;
+ bool yn = false;
RegionSelection rs = get_regions_from_selection_and_entered ();
return;
}
- RegionSelection::iterator i;
+ RegionSelection::iterator i;
for (i = rs.begin(); i != rs.end(); ++i) {
if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
if (dir == -1) {
}
}
-/** Update crossfade visibility after its configuration has been changed */
-void
-Editor::update_xfade_visibility ()
-{
- _xfade_visibility = _session->config.get_xfades_visible ();
-
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
- if (v) {
- if (_xfade_visibility) {
- v->show_all_xfades ();
- } else {
- v->hide_all_xfades ();
- }
- }
- }
-}
-
void
Editor::set_edit_point ()
{
_session->request_locate (where, _session->transport_rolling());
}
}
+
+ if ( Config->get_always_play_range() )
+ cancel_time_selection();
}
void
Editor::split_region ()
{
- if (((mouse_mode == MouseRange) ||
- (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
- !selection->time.empty()) {
+ if ( !selection->time.empty()) {
separate_regions_between (selection->time);
return;
- }
+ }
RegionSelection rs = get_regions_from_selection_and_edit_point ();
split_regions_at (where, rs);
}
-void
-Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
-{
- if (entered_track && mouse_mode == MouseObject) {
- if (!selection->tracks.empty()) {
- if (!selection->selected (entered_track)) {
- selection->add (entered_track);
- }
- } else {
- /* there is no selection, but this operation requires/prefers selected objects */
-
- if (op_really_wants_one_track_if_none_are_selected) {
- selection->set (entered_track);
- }
- }
- }
-}
-
struct EditorOrderRouteSorter {
bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- /* use of ">" forces the correct sort order */
- return a->order_key ("editor") < b->order_key ("editor");
+ return a->order_key (EditorSort) < b->order_key (EditorSort);
}
};
return;
double const current_view_min_y = vertical_adjustment.get_value();
- double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
+ double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
double const track_min_y = track->y_position ();
double const track_max_y = track->y_position () + track->effective_height ();
new_value = track_min_y;
} else {
// Track is below the current view
- new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
+ new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
}
vertical_adjustment.set_value(new_value);
{
RegionSelection rs = get_regions_from_selection_and_entered ();
- if (rs.empty()) {
+ RegionSelection audio_rs;
+ for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
+ if (dynamic_cast<AudioRegionView*> (*i)) {
+ audio_rs.push_back (*i);
+ }
+ }
+
+ if (audio_rs.empty()) {
return;
}
- pitch_shift (rs, 1.2);
+ pitch_shift (audio_rs, 1.2);
+}
+
+void
+Editor::transpose_region ()
+{
+ RegionSelection rs = get_regions_from_selection_and_entered ();
+
+ list<MidiRegionView*> midi_region_views;
+ for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
+ MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
+ if (mrv) {
+ midi_region_views.push_back (mrv);
+ }
+ }
+
+ TransposeDialog d;
+ int const r = d.run ();
+ if (r != RESPONSE_ACCEPT) {
+ return;
+ }
+
+ for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
+ (*i)->midi_region()->transpose (d.semitones ());
+ }
}
void
we have frames per bar, and beats per bar, so ...
*/
- double frames_per_beat = length / m.beats_per_bar();
+ /* XXXX METER MATH */
+
+ double frames_per_beat = length / m.divisions_per_bar();
/* beats per minute = */
_("Do you want to set the global tempo or add a new tempo marker?"),
options
);
-
+
c.set_default_response (2);
switch (c.run()) {
} else if (t.frame() == start) {
_session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
} else {
- _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
+ Timecode::BBT_Time bbt;
+ _session->tempo_map().bbt_time (start, bbt);
+ _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
}
XMLNode& after (_session->tempo_map().get_state());
Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
{
bool use_rhythmic_rodent = false;
-
+
boost::shared_ptr<Playlist> pl = r->playlist();
-
+
list<boost::shared_ptr<Region> > new_regions;
if (!pl) {
int response = msg.run();
msg.hide ();
-
+
switch (response) {
case RESPONSE_OK:
break;
AnalysisFeatureList::const_iterator x;
pl->clear_changes ();
+ pl->clear_owned_changes ();
x = positions.begin();
framepos_t pos = 0;
while (x != positions.end()) {
-
+
/* deal with positons that are out of scope of present region bounds */
if (*x <= 0 || *x > r->length()) {
++x;
*/
framepos_t len = (*x) - pos;
-
+
/* XXX we do we really want to allow even single-sample regions?
shouldn't we have some kind of lower limit on region size?
*/
/* do NOT announce new regions 1 by one, just wait till they are all done */
- PropertyList plist;
-
+ PropertyList plist;
+
plist.add (ARDOUR::Properties::start, file_start);
plist.add (ARDOUR::Properties::length, len);
plist.add (ARDOUR::Properties::name, new_name);
plist.add (ARDOUR::Properties::layer, 0);
boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
+ /* because we set annouce to false, manually add the new region to the
+ RegionFactory map
+ */
+ RegionFactory::map_add (nr);
pl->add_region (nr, r->position() + pos);
string new_name;
RegionFactory::region_name (new_name, r->name());
-
+
/* Add the final region */
- PropertyList plist;
-
+ PropertyList plist;
+
plist.add (ARDOUR::Properties::start, r->start() + pos);
plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
plist.add (ARDOUR::Properties::name, new_name);
plist.add (ARDOUR::Properties::layer, 0);
boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
+ /* because we set annouce to false, manually add the new region to the
+ RegionFactory map
+ */
+ RegionFactory::map_add (nr);
pl->add_region (nr, r->position() + pos);
-
+
if (select_new) {
new_regions.push_front(nr);
}
pl->thaw ();
- _session->add_command (new StatefulDiffCommand (pl));
+ /* We might have removed regions, which alters other regions' layering_index,
+ so we need to do a recursive diff here.
+ */
+ vector<Command*> cmds;
+ pl->rdiff (cmds);
+ _session->add_commands (cmds);
+ _session->add_command (new StatefulDiffCommand (pl));
+
if (select_new) {
for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
if (rs.empty()) {
return;
}
-
+
framepos_t where = get_preferred_edit_position();
_session->begin_reversible_command (_("place transient"));
-
+
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
framepos_t position = (*r)->region()->position();
(*r)->region()->add_transient(where - position);
}
-
+
_session->commit_reversible_command ();
}
Editor::snap_regions_to_grid ()
{
list <boost::shared_ptr<Playlist > > used_playlists;
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (!_session || rs.empty()) {
return;
}
-
+
_session->begin_reversible_command (_("snap regions to grid"));
-
+
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
-
+
boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
-
+
if (!pl->frozen()) {
/* we haven't seen this playlist before */
framepos_t start_frame = (*r)->region()->first_frame ();
snap_to (start_frame);
- (*r)->region()->set_position (start_frame, this);
+ (*r)->region()->set_position (start_frame);
}
-
+
while (used_playlists.size() > 0) {
list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
(*i)->thaw();
Editor::close_region_gaps ()
{
list <boost::shared_ptr<Playlist > > used_playlists;
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (!_session || rs.empty()) {
Table table (2, 3);
table.set_spacings (12);
table.set_border_width (12);
- Label* l = manage (new Label (_("Crossfade length")));
- l->set_alignment (0, 0.5);
+ Label* l = manage (left_aligned_label (_("Crossfade length")));
table.attach (*l, 0, 1, 0, 1);
-
+
SpinButton spin_crossfade (1, 0);
spin_crossfade.set_range (0, 15);
spin_crossfade.set_increments (1, 1);
table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
- l = manage (new Label (_("Pull-back length")));
- l->set_alignment (0, 0.5);
+ l = manage (left_aligned_label (_("Pull-back length")));
table.attach (*l, 0, 1, 1, 2);
-
+
SpinButton spin_pullback (1, 0);
spin_pullback.set_range (0, 100);
spin_pullback.set_increments (1, 1);
table.attach (spin_pullback, 1, 2, 1, 2);
table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
-
+
dialog.get_vbox()->pack_start (table);
dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
return;
}
- framepos_t crossfade_len = spin_crossfade.get_value();
+ framepos_t crossfade_len = spin_crossfade.get_value();
framepos_t pull_back_frames = spin_pullback.get_value();
crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
/* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
-
+
_session->begin_reversible_command (_("close region gaps"));
int idx = 0;
boost::shared_ptr<Region> last_region;
-
+
rs.sort_by_position_and_track();
-
+
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
-
+
boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
-
+
if (!pl->frozen()) {
/* we haven't seen this playlist before */
}
framepos_t position = (*r)->region()->position();
-
+
if (idx == 0 || position < last_region->position()){
last_region = (*r)->region();
idx++;
continue;
}
-
- (*r)->region()->trim_front( (position - pull_back_frames), this );
- last_region->trim_end( (position - pull_back_frames + crossfade_len), this );
-
+
+ (*r)->region()->trim_front( (position - pull_back_frames));
+ last_region->trim_end( (position - pull_back_frames + crossfade_len));
+
last_region = (*r)->region();
-
+
idx++;
}
-
+
while (used_playlists.size() > 0) {
list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
(*i)->thaw();
if (!selection->tracks.empty()) {
- for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
+ /* don't waste time searching for transients in duplicate playlists.
+ */
+
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+
+ for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
void
Editor::playhead_forward_to_grid ()
{
- if (!_session) return;
- framepos_t pos = playhead_cursor->current_frame;
+ if (!_session) {
+ return;
+ }
+
+ framepos_t pos = playhead_cursor->current_frame ();
if (pos < max_framepos - 1) {
pos += 2;
snap_to_internal (pos, 1, false);
void
Editor::playhead_backward_to_grid ()
{
- if (!_session) return;
- framepos_t pos = playhead_cursor->current_frame;
+ if (!_session) {
+ return;
+ }
+
+ framepos_t pos = playhead_cursor->current_frame ();
if (pos > 2) {
pos -= 2;
snap_to_internal (pos, -1, false);
get_preferred_edit_position(),
d.distance(),
opt,
+ d.all_playlists(),
d.move_glued(),
d.move_markers(),
d.move_glued_markers(),
}
void
-Editor::insert_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
- bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
+Editor::insert_time (
+ framepos_t pos, framecnt_t frames, InsertTimeOption opt,
+ bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
+ )
{
bool commit = false;
begin_reversible_command (_("insert time"));
- for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
+ TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+
+ for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
+
/* regions */
- boost::shared_ptr<Playlist> pl = (*x)->playlist();
- if (pl) {
+ /* don't operate on any playlist more than once, which could
+ * happen if "all playlists" is enabled, but there is more
+ * than 1 track using playlists "from" a given track.
+ */
- pl->clear_changes ();
- pl->clear_owned_changes ();
+ set<boost::shared_ptr<Playlist> > pl;
+
+ if (all_playlists) {
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
+ if (rtav) {
+ vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
+ for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
+ pl.insert (*p);
+ }
+ }
+ } else {
+ if ((*x)->playlist ()) {
+ pl.insert ((*x)->playlist ());
+ }
+ }
+
+ for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
+
+ (*i)->clear_changes ();
+ (*i)->clear_owned_changes ();
if (opt == SplitIntersected) {
- pl->split (pos);
+ (*i)->split (pos);
}
- pl->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
+ (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
vector<Command*> cmds;
- pl->rdiff (cmds);
+ (*i)->rdiff (cmds);
_session->add_commands (cmds);
-
- _session->add_command (new StatefulDiffCommand (pl));
+
+ _session->add_command (new StatefulDiffCommand (*i));
commit = true;
}
}
moved = true;
}
-
+
}
if (was_locked) {
for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
tvl.push_back (&(*r)->get_time_axis_view ());
}
-
+
if (!tvl.empty()) {
fit_tracks (tvl);
}
++visible_tracks;
}
- uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
+ uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
double first_y_pos = DBL_MAX;
if (h < TimeAxisView::preset_height (HeightSmall)) {
return;
}
- undo_visual_stack.push_back (current_visual_state());
+ undo_visual_stack.push_back (current_visual_state (true));
+ no_save_visual = true;
/* build a list of all tracks, including children */
request signal handler will cause the vertical adjustment setting to fail
*/
- controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
+ controls_layout.property_height () = _full_canvas_height;
vertical_adjustment.set_value (first_y_pos);
- redo_visual_stack.push_back (current_visual_state());
+ redo_visual_stack.push_back (current_visual_state (true));
}
void
visual_states.push_back (0);
}
- delete visual_states[n];
+ if (visual_states[n] != 0) {
+ delete visual_states[n];
+ }
visual_states[n] = current_visual_state (true);
gdk_beep ();
void
Editor::start_visual_state_op (uint32_t n)
{
- if (visual_state_op_connection.empty()) {
- visual_state_op_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::end_visual_state_op), n), 1000);
- }
-}
-
-void
-Editor::cancel_visual_state_op (uint32_t n)
-{
- if (!visual_state_op_connection.empty()) {
- visual_state_op_connection.disconnect();
- goto_visual_state (n);
- } else {
- //we land here if called from the menu OR if end_visual_state_op has been called
- //so check if we are already in visual state n
- // XXX not yet checking it at all, but redoing does not hurt
- goto_visual_state (n);
- }
-}
-
-bool
-Editor::end_visual_state_op (uint32_t n)
-{
- visual_state_op_connection.disconnect();
save_visual_state (n);
-
+
PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
char buf[32];
snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
pup->set_text (buf);
pup->touch();
+}
- return false; // do not call again
+void
+Editor::cancel_visual_state_op (uint32_t n)
+{
+ goto_visual_state (n);
}
void
if (_ignore_region_action) {
return;
}
-
+
RegionSelection rs = get_regions_from_selection_and_entered ();
if (rs.empty ()) {
} else {
begin_reversible_command (_("mute region"));
}
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
-
+
(*i)->region()->playlist()->clear_changes ();
(*i)->region()->set_muted (!(*i)->region()->muted ());
_session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
-
+
}
-
+
commit_reversible_command ();
}
+void
+Editor::combine_regions ()
+{
+ /* foreach track with selected regions, take all selected regions
+ and join them into a new region containing the subregions (as a
+ playlist)
+ */
+
+ typedef set<RouteTimeAxisView*> RTVS;
+ RTVS tracks;
+
+ if (selection->regions.empty()) {
+ return;
+ }
+
+ for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
+
+ if (rtv) {
+ tracks.insert (rtv);
+ }
+ }
+
+ begin_reversible_command (_("combine regions"));
+
+ vector<RegionView*> new_selection;
+
+ for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
+ RegionView* rv;
+
+ if ((rv = (*i)->combine_regions ()) != 0) {
+ new_selection.push_back (rv);
+ }
+ }
+
+ selection->clear_regions ();
+ for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
+ selection->add (*i);
+ }
+
+ commit_reversible_command ();
+}
+
+void
+Editor::uncombine_regions ()
+{
+ typedef set<RouteTimeAxisView*> RTVS;
+ RTVS tracks;
+
+ if (selection->regions.empty()) {
+ return;
+ }
+
+ for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
+
+ if (rtv) {
+ tracks.insert (rtv);
+ }
+ }
+
+ begin_reversible_command (_("uncombine regions"));
+
+ for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
+ (*i)->uncombine_regions ();
+ }
+
+ commit_reversible_command ();
+}
+
+void
+Editor::toggle_midi_input_active (bool flip_others)
+{
+ bool onoff;
+ boost::shared_ptr<RouteList> rl (new RouteList);
+
+ for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
+ RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+
+ if (!rtav) {
+ continue;
+ }
+
+ boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
+
+ if (mt) {
+ rl->push_back (rtav->route());
+ onoff = !mt->input_active();
+ }
+ }
+
+ _session->set_exclusive_input_active (rl, onoff, flip_others);
+}