#include <map>
#include <set>
+#include <gtkmm/messagedialog.h>
+
#include "pbd/error.h"
#include "pbd/basename.h"
#include "pbd/pthread_utils.h"
#include "pbd/whitespace.h"
#include "pbd/stateful_diff_command.h"
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/choice.h>
-#include <gtkmm2ext/popup.h>
+#include "gtkmm2ext/utils.h"
+
+#include "widgets/choice.h"
+#include "widgets/popup.h"
+#include "widgets/prompter.h"
#include "ardour/audio_track.h"
#include "ardour/audioregion.h"
+#include "ardour/boost_debug.h"
#include "ardour/dB.h"
#include "ardour/location.h"
#include "ardour/midi_region.h"
#include "ardour/session_playlists.h"
#include "ardour/strip_silence.h"
#include "ardour/transient_detector.h"
+#include "ardour/transpose.h"
#include "canvas/canvas.h"
#include "actions.h"
-#include "ardour_ui.h"
#include "audio_region_view.h"
#include "audio_streamview.h"
#include "audio_time_axis.h"
#include "item_counts.h"
#include "keyboard.h"
#include "midi_region_view.h"
+#include "mixer_ui.h"
#include "mixer_strip.h"
#include "mouse_cursors.h"
#include "normalize_dialog.h"
#include "streamview.h"
#include "strip_silence_dialog.h"
#include "time_axis_view.h"
+#include "timers.h"
#include "transpose_dialog.h"
#include "transform_dialog.h"
+#include "ui_config.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
+using namespace ArdourWidgets;
using namespace Editing;
using Gtkmm2ext::Keyboard;
void
Editor::undo (uint32_t n)
{
+ if (_session && _session->actively_recording()) {
+ /* no undo allowed while recording. Session will check also,
+ but we don't even want to get to that.
+ */
+ return;
+ }
+
if (_drags->active ()) {
_drags->abort ();
}
-
+
if (_session) {
_session->undo (n);
if (_session->undo_depth() == 0) {
void
Editor::redo (uint32_t n)
{
+ if (_session && _session->actively_recording()) {
+ /* no redo allowed while recording. Session will check also,
+ but we don't even want to get to that.
+ */
+ return;
+ }
+
if (_drags->active ()) {
_drags->abort ();
}
-
+
if (_session) {
- _session->redo (n);
+ _session->redo (n);
if (_session->redo_depth() == 0) {
redo_action->set_sensitive(false);
}
}
void
-Editor::split_regions_at (framepos_t where, RegionSelection& regions)
+Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
{
bool frozen = false;
case SnapToRegionEnd:
break;
default:
- snap_to (where);
+ if (snap_frame) {
+ snap_to (where);
+ }
}
} else {
- snap_to (where);
+ if (snap_frame) {
+ snap_to (where);
+ }
frozen = true;
EditorFreeze(); /* Emit Signal */
have something to split.
*/
- if (!(*a)->region()->covers (where)) {
+ if (!(*a)->region()->covers (where.frame)) {
++a;
continue;
}
for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
}
-
+
while (used_playlists.size() > 0) {
list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
(*i)->thaw();
if (working_on_selection) {
// IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
- _ignore_follow_edits = true; // a split will change the region selection in mysterious ways; it's not practical or wanted to follow this edit
RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
/* There are three classes of regions that we might want selected after
splitting selected regions:
}
for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
- if ((*ri)->region()->position() < where) {
+ if ((*ri)->region()->position() < where.frame) {
// new regions created before the split
if (rsas & NewlyCreatedLeft) {
selection->add (*ri);
}
}
}
- _ignore_follow_edits = false;
} else {
- _ignore_follow_edits = true;
if( working_on_selection ) {
selection->add (latest_regionviews); //these are the new regions created after the split
}
- _ignore_follow_edits = false;
}
commit_reversible_command ();
return;
}
- begin_reversible_command (_("alter selection"));
+ begin_reversible_selection_op (_("alter selection"));
selection->set_preserving_all_ranges (start, end);
- commit_reversible_command ();
+ commit_reversible_selection_op ();
}
bool
} else if (!force_playhead && !selection->markers.empty()) {
bool is_start;
-
- begin_reversible_command (_("nudge location forward"));
+ bool in_command = false;
+ const int32_t divisions = get_grid_music_divisions (0);
for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
distance = next_distance;
}
if (max_framepos - distance > loc->start() + loc->length()) {
- loc->set_start (loc->start() + distance);
+ loc->set_start (loc->start() + distance, false, true, divisions);
} else {
- loc->set_start (max_framepos - loc->length());
+ loc->set_start (max_framepos - loc->length(), false, true, divisions);
}
} else {
distance = get_nudge_distance (loc->end(), next_distance);
distance = next_distance;
}
if (max_framepos - distance > loc->end()) {
- loc->set_end (loc->end() + distance);
+ loc->set_end (loc->end() + distance, false, true, divisions);
} else {
- loc->set_end (max_framepos);
+ loc->set_end (max_framepos, false, true, divisions);
+ }
+ if (loc->is_session_range()) {
+ _session->set_end_is_free (false);
}
}
+ if (!in_command) {
+ begin_reversible_command (_("nudge location forward"));
+ in_command = true;
+ }
XMLNode& after (loc->get_state());
_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
}
}
- commit_reversible_command ();
-
+ if (in_command) {
+ commit_reversible_command ();
+ }
} else {
distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
_session->request_locate (playhead_cursor->current_frame () + distance);
} else if (!force_playhead && !selection->markers.empty()) {
bool is_start;
-
- begin_reversible_command (_("nudge location forward"));
+ bool in_command = false;
for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
distance = next_distance;
}
if (distance < loc->start()) {
- loc->set_start (loc->start() - distance);
+ loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
} else {
- loc->set_start (0);
+ loc->set_start (0, false, true, get_grid_music_divisions(0));
}
} else {
distance = get_nudge_distance (loc->end(), next_distance);
}
if (distance < loc->end() - loc->length()) {
- loc->set_end (loc->end() - distance);
+ loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
} else {
- loc->set_end (loc->length());
+ loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
+ }
+ if (loc->is_session_range()) {
+ _session->set_end_is_free (false);
}
}
-
+ if (!in_command) {
+ begin_reversible_command (_("nudge location forward"));
+ in_command = true;
+ }
XMLNode& after (loc->get_state());
_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
}
}
-
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
} else {
}
struct RegionSelectionPositionSorter {
- bool operator() (RegionView* a, RegionView* b) {
- return a->region()->position() < b->region()->position();
- }
+ bool operator() (RegionView* a, RegionView* b) {
+ return a->region()->position() < b->region()->position();
+ }
};
void
if (!rs.empty()) {
- begin_reversible_command (_("sequence regions"));
+ bool in_command = false;
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
boost::shared_ptr<Region> r ((*i)->region());
r->set_position(r_end_prev);
}
+ if (!in_command) {
+ begin_reversible_command (_("sequence regions"));
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (r));
r_end=r->position() + r->length();
iCount++;
}
- commit_reversible_command ();
- }
-}
+
+ if (in_command) {
+ commit_reversible_command ();
+ }
+ }
+}
/* DISPLAY MOTION */
TimeAxisView *ontrack = 0;
TrackViewList tlist;
-
+
if (!selection->tracks.empty()) {
tlist = selection->tracks.filter_to_unique_playlists ();
} else {
return;
}
- loc->move_to (target);
+ loc->move_to (target, 0);
}
void
pos = track_frame_to_session_frame(pos, speed);
- loc->move_to (pos);
+ loc->move_to (pos, 0);
}
void
return;
}
- loc->move_to (pos);
+ loc->move_to (pos, 0);
}
void
return;
}
- loc->move_to (pos);
+ loc->move_to (pos, 0);
}
void
_session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
} else {
+ const int32_t divisions = get_grid_music_divisions (0);
/* move selected markers to playhead */
for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
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 (), false, true, divisions);
} else {
loc->set (playhead_cursor->current_frame (),
- playhead_cursor->current_frame () + loc->length());
+ playhead_cursor->current_frame () + loc->length(), true, divisions);
}
}
}
reset_y_origin (vertical_adjustment.get_value() - 60);
}
+void
+Editor::select_topmost_track ()
+{
+ const double top_of_trackviews = vertical_adjustment.get_value();
+ for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
+ if ((*t)->hidden()) {
+ continue;
+ }
+ std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
+ if (res.first) {
+ selection->set (*t);
+ break;
+ }
+ }
+}
+
bool
Editor::scroll_down_one_track (bool skip_child_views)
{
}
/* move to the track below the first one that covers the */
-
+
if (next != track_views.rend()) {
ensure_time_axis_view_is_visible (**next, true);
return true;
}
return false;
-}
+}
bool
Editor::scroll_up_one_track (bool skip_child_views)
{
TrackViewList::iterator prev = track_views.end();
double top_of_trackviews = vertical_adjustment.get_value ();
-
+
for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
if ((*t)->hidden()) {
continue;
}
- /* find the trackview at the top of the trackview group
+ /* find the trackview at the top of the trackview group
*
* Note that covers_y_position() is recursive and includes child views
*/
std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
-
+
if (res.first) {
if (skip_child_views) {
break;
/* automation lane (one level, non-recursive)
*
* - if no automation lane exists -> move to prev tack
- * - if no y-axis match is found -> the current track is at the top -> move to prev track
+ * - if no y-axis match is found -> the current track is at the top -> move to prev track
* (actually last automation lane of previous track, see below)
* - if first (top-most) lane is at the top -> move to this track
* - else move up one lane
prev = t;
}
-
+
if (prev != track_views.end()) {
// move to bottom-most automation-lane of the previous track
TimeAxisView::Children kids = (*prev)->get_child_list();
return false;
}
+void
+Editor::scroll_left_step ()
+{
+ framepos_t xdelta = (current_page_samples() / 8);
+
+ if (leftmost_frame > xdelta) {
+ reset_x_origin (leftmost_frame - xdelta);
+ } else {
+ reset_x_origin (0);
+ }
+}
+
+
+void
+Editor::scroll_right_step ()
+{
+ framepos_t xdelta = (current_page_samples() / 8);
+
+ if (max_framepos - xdelta > leftmost_frame) {
+ reset_x_origin (leftmost_frame + xdelta);
+ } else {
+ reset_x_origin (max_framepos - current_page_samples());
+ }
+}
+
+void
+Editor::scroll_left_half_page ()
+{
+ framepos_t xdelta = (current_page_samples() / 2);
+ if (leftmost_frame > xdelta) {
+ reset_x_origin (leftmost_frame - xdelta);
+ } else {
+ reset_x_origin (0);
+ }
+}
+
+void
+Editor::scroll_right_half_page ()
+{
+ framepos_t xdelta = (current_page_samples() / 2);
+ if (max_framepos - xdelta > leftmost_frame) {
+ reset_x_origin (leftmost_frame + xdelta);
+ } else {
+ reset_x_origin (max_framepos - current_page_samples());
+ }
+}
+
/* ZOOM */
void
} else {
ts = &selection->tracks;
}
-
+
for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
tv->step_height (coarser);
} 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 ();
}
}
+void
+Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
+{
+ Editing::ZoomFocus temp_focus = zoom_focus;
+ zoom_focus = Editing::ZoomFocusMouse;
+ temporal_zoom_step_scale (zoom_out, scale);
+ zoom_focus = temp_focus;
+}
+
+void
+Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
+{
+ temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
+}
+
+void
+Editor::temporal_zoom_step (bool zoom_out)
+{
+ temporal_zoom_step_scale (zoom_out, 2.0);
+}
void
-Editor::temporal_zoom_step (bool coarser)
+Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
{
- ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
+ ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
framecnt_t nspp = samples_per_pixel;
- if (coarser) {
- nspp *= 2;
+ if (zoom_out) {
+ nspp *= scale;
+ if (nspp == samples_per_pixel) {
+ nspp *= 2.0;
+ }
} else {
- nspp /= 2;
+ nspp /= scale;
+ if (nspp == samples_per_pixel) {
+ nspp /= 2.0;
+ }
}
-
+
+ // ToDo: encapsulate all of this into something like editor::get_session_extents() or editor::leftmost(), rightmost()
+ {
+ //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like)
+
+ //calculate the extents of all regions in every playlist
+ framecnt_t session_extent_start = 0;
+ framecnt_t session_extent_end = 0;
+ {
+ boost::shared_ptr<RouteList> rl = _session->get_routes();
+ for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
+ if (tr) {
+ boost::shared_ptr<Playlist> pl = tr->playlist();
+ if (pl) {
+ pair<framepos_t, framepos_t> e;
+ e = pl->get_extent();
+ if (e.first < session_extent_start) {
+ session_extent_start = e.first;
+ }
+ if (e.second > session_extent_end) {
+ session_extent_end = e.second;
+ }
+ }
+ }
+ }
+ }
+ framecnt_t session_extents = session_extent_end - session_extent_start;
+
+ //in a session with no regions, use the start/end markers to set max zoom
+ framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
+ if ( session_length > session_extents )
+ session_extents = session_length;
+
+ //in a session with no regions or start/end markers, use 2 minutes to set max zoom
+ framecnt_t const min_length = _session->nominal_frame_rate()*60*2;
+ if ( min_length > session_extents )
+ session_extents = min_length;
+
+ //convert to samples-per-pixel and limit our zoom to this value
+ framecnt_t session_extents_pp = session_extents / _visible_canvas_width;
+ if (nspp > session_extents_pp)
+ nspp = session_extents_pp;
+ }
+
temporal_zoom (nspp);
}
framepos_t leftmost_after_zoom = 0;
framepos_t where;
bool in_track_canvas;
+ bool use_mouse_frame = true;
framecnt_t nfpp;
double l;
return;
}
- // Imposing an arbitrary limit to zoom out as too much zoom out produces
+ // 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.
//
case ZoomFocusMouse:
/* try to keep the mouse over the same point in the display */
- if (!mouse_frame (where, in_track_canvas)) {
- /* use playhead instead */
- where = playhead_cursor->current_frame ();
-
- if (where < half_page_size) {
- leftmost_after_zoom = 0;
- } else {
- leftmost_after_zoom = where - half_page_size;
- }
-
- } else {
+ if (_drags->active()) {
+ where = _drags->current_pointer_frame ();
+ } else if (!mouse_frame (where, in_track_canvas)) {
+ use_mouse_frame = false;
+ }
+ if (use_mouse_frame) {
l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
if (l < 0) {
} else {
leftmost_after_zoom = (framepos_t) l;
}
- }
+ } else {
+ /* use playhead instead */
+ where = playhead_cursor->current_frame ();
+ if (where < half_page_size) {
+ leftmost_after_zoom = 0;
+ } else {
+ leftmost_after_zoom = where - half_page_size;
+ }
+ }
break;
case ZoomFocusEdit:
}
}
-void
-Editor::temporal_zoom_region (bool both_axes)
-{
- framepos_t start = max_framepos;
- framepos_t end = 0;
- set<TimeAxisView*> tracks;
-
- if ( !get_selection_extents(start, end) )
- return;
-
- calc_extra_zoom_edges (start, end);
-
- /* if we're zooming on both axes we need to save track heights etc.
- */
-
- undo_visual_stack.push_back (current_visual_state (both_axes));
-
- 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 ((_visible_canvas_height - 10.0) / tracks.size());
-
- /* set visible track heights appropriately */
-
- for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
- (*t)->set_height (per_track_height);
- }
-
- /* hide irrelevant tracks */
-
- DisplaySuspender ds;
-
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
- hide_track_in_display (*i);
- }
- }
-
- vertical_adjustment.set_value (0.0);
- }
-
- redo_visual_stack.push_back (current_visual_state (both_axes));
-}
-
-
bool
-Editor::get_selection_extents ( framepos_t &start, framepos_t &end )
+Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
{
start = max_framepos;
end = 0;
bool ret = true;
-
+
//ToDo: if notes are selected, set extents to that selection
//ToDo: if control points are selected, set extents to that selection
} else if (!selection->time.empty()) {
start = selection->time.start();
end = selection->time.end_frame();
- } else
+ } else
ret = false; //no selection found
//range check
if ((start == 0 && end == 0) || end < start) {
ret = false;
}
-
- return ret;
+
+ return ret;
}
void
-Editor::temporal_zoom_selection (bool both_axes)
+Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
{
if (!selection) return;
//ToDo: if control points are selected, zoom to that
- //if region(s) are selected, zoom to that
- if ( !selection->regions.empty() )
- temporal_zoom_region (both_axes);
-
- //if a range is selected, zoom to that
- if (!selection->time.empty()) {
+ if (axes == Horizontal || axes == Both) {
- framepos_t start, end;
+ framepos_t start, end;
if (get_selection_extents (start, end)) {
- calc_extra_zoom_edges(start, end);
+ calc_extra_zoom_edges (start, end);
temporal_zoom_by_frame (start, end);
}
-
- if (both_axes)
- fit_selection();
}
+ if (axes == Vertical || axes == Both) {
+ fit_selection ();
+ }
}
void
framepos_t range = end - start;
const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
-
+
framepos_t new_page = range;
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));
/* zoom focus is automatically taken as @param frame when this
method is used.
*/
-
+
framepos_t new_leftmost = frame - (framepos_t)range_before;
if (new_leftmost > frame) {
bool
Editor::choose_new_marker_name(string &name) {
- if (!ARDOUR_UI::config()->get_name_new_markers()) {
+ if (!UIConfiguration::instance().get_name_new_markers()) {
/* don't prompt user for a new name */
return true;
}
- ArdourPrompter dialog (true);
+ Prompter dialog (true);
dialog.set_prompt (_("New Name:"));
framepos_t end = selection->time[clicked_selection].end;
_session->locations()->next_available_name(rangename,"selection");
- Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
+ Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
begin_reversible_command (_("add marker"));
-
+
XMLNode &before = _session->locations()->get_state();
_session->locations()->add (location, true);
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
+
commit_reversible_command ();
}
if (!choose_new_marker_name(markername)) {
return;
}
- Location *location = new Location (*_session, where, where, markername, Location::IsMark);
+ Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
begin_reversible_command (_("add marker"));
-
+
XMLNode &before = _session->locations()->get_state();
_session->locations()->add (location, true);
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
+
commit_reversible_command ();
}
{
if (!_session)
return;
-
+
Location* loc;
if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
_session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
XMLNode &after = loc->get_state();
begin_reversible_command (_("Set session start"));
-
+
_session->add_command (new MementoCommand<Location>(*loc, &before, &after));
commit_reversible_command ();
{
if (!_session)
return;
-
+
Location* loc;
if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
_session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
commit_reversible_command ();
}
+
+ _session->set_end_is_free (false);
+}
+
+
+void
+Editor::toggle_location_at_playhead_cursor ()
+{
+ if (!do_remove_location_at_playhead_cursor())
+ {
+ add_location_from_playhead_cursor();
+ }
}
void
add_location_mark (_session->audible_frame());
}
-void
-Editor::remove_location_at_playhead_cursor ()
+bool
+Editor::do_remove_location_at_playhead_cursor ()
{
+ bool removed = false;
if (_session) {
-
//set up for undo
- begin_reversible_command (_("remove marker"));
-
XMLNode &before = _session->locations()->get_state();
- bool removed = false;
//find location(s) at this time
Locations::LocationList locs;
removed = true;
}
}
-
+
//store undo
if (removed) {
+ begin_reversible_command (_("remove marker"));
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
commit_reversible_command ();
}
}
+ return removed;
+}
+
+void
+Editor::remove_location_at_playhead_cursor ()
+{
+ do_remove_location_at_playhead_cursor ();
}
/** Add a range marker around each selected region */
if (rs.empty()) {
return;
}
+ bool commit = false;
- begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
-
XMLNode &before = _session->locations()->get_state();
for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
boost::shared_ptr<Region> region = (*i)->region ();
- Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
+ Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
_session->locations()->add (location, true);
+ commit = true;
}
- XMLNode &after = _session->locations()->get_state();
- _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
- commit_reversible_command ();
+ if (commit) {
+ begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
+ XMLNode &after = _session->locations()->get_state();
+ _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
+ commit_reversible_command ();
+ }
}
/** Add a single range marker around all selected regions */
return;
}
- begin_reversible_command (_("add marker"));
-
XMLNode &before = _session->locations()->get_state();
string markername;
}
// single range spanning all selected
- Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
+ Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
_session->locations()->add (location, true);
+ begin_reversible_command (_("add marker"));
XMLNode &after = _session->locations()->get_state();
_session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
commit_reversible_command ();
}
if (pos < 0) {
return;
}
-
+
_session->request_locate (pos, _session->transport_rolling());
}
framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
+ //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...
+ if ( _session->transport_rolling() ) {
+ if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
+ framepos_t prior = _session->locations()->first_mark_before ( pos );
+ pos = prior;
+ }
+ }
+
if (pos < 0) {
return;
}
return;
}
- _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
+ _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
}
void
{
if (_session) {
begin_reversible_command (_("clear markers"));
-
+
XMLNode &before = _session->locations()->get_state();
_session->locations()->clear_markers ();
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
+
commit_reversible_command ();
}
}
{
if (_session) {
begin_reversible_command (_("clear ranges"));
-
+
XMLNode &before = _session->locations()->get_state();
_session->locations()->clear_ranges ();
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
+
commit_reversible_command ();
}
}
Editor::clear_locations ()
{
begin_reversible_command (_("clear locations"));
-
+
XMLNode &before = _session->locations()->get_state();
_session->locations()->clear ();
XMLNode &after = _session->locations()->get_state();
_session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
-
+
commit_reversible_command ();
}
void
Editor::play_selection ()
{
- framepos_t start, end;
+ framepos_t start, end;
if (!get_selection_extents ( start, end))
return;
_session->request_play_range (&lar, 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 )
+Editor::maybe_locate_with_edit_preroll (framepos_t location)
{
- if ( _session->transport_rolling() || !ARDOUR_UI::config()->get_follow_edits() || _ignore_follow_edits )
+ if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
return;
- location -= get_preroll();
-
+ location -= _session->preroll_samples (location);
+
//don't try to locate before the beginning of time
- if ( location < 0 )
+ if (location < 0) {
location = 0;
-
+ }
+
//if follow_playhead is on, keep the playhead on the screen
if ( _follow_playhead )
- if ( location < leftmost_frame )
+ if ( location < leftmost_frame )
location = leftmost_frame;
_session->request_locate( location );
void
Editor::play_with_preroll ()
{
- {
- framepos_t preroll = get_preroll();
-
- framepos_t start, end;
- if (!get_selection_extents ( start, end))
- return;
+ framepos_t start, end;
+ if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
+ const framepos_t preroll = _session->preroll_samples (start);
+
+ framepos_t ret = start;
- if (start > preroll)
+ if (start > preroll) {
start = start - preroll;
-
+ }
+
end = end + preroll; //"post-roll"
-
+
AudioRange ar (start, end, 0);
list<AudioRange> lar;
lar.push_back (ar);
_session->request_play_range (&lar, true);
+ _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
+ } else {
+ framepos_t ph = playhead_cursor->current_frame ();
+ const framepos_t preroll = _session->preroll_samples (ph);
+ framepos_t start;
+ if (ph > preroll) {
+ start = ph - preroll;
+ } else {
+ start = 0;
+ }
+ _session->request_locate (start, true);
+ _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
}
}
+void
+Editor::rec_with_preroll ()
+{
+ framepos_t ph = playhead_cursor->current_frame ();
+ framepos_t preroll = _session->preroll_samples (ph);
+ _session->request_preroll_record_trim (ph, preroll);
+}
+
+void
+Editor::rec_with_count_in ()
+{
+ _session->request_count_in_record ();
+}
+
void
Editor::play_location (Location& location)
{
begin_reversible_command (_("raise region to top"));
}
break;
-
+
case Lower:
if (multiple) {
begin_reversible_command (_("lower regions"));
begin_reversible_command (_("lower region"));
}
break;
-
+
case LowerToBottom:
if (multiple) {
begin_reversible_command (_("lower regions to bottom"));
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) {
(*i)->rdiff (cmds);
_session->add_commands (cmds);
}
-
+
commit_reversible_command ();
}
return;
}
- ArdourDialog d (*this, _("Rename Region"), true, false);
+ ArdourDialog d (_("Rename Region"), true, false);
Entry entry;
Label label (_("New name:"));
HBox hbox;
}
}
-void
-Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
-{
- if (_session->is_auditioning()) {
- _session->cancel_audition ();
- }
-
- // note: some potential for creativity here, because region doesn't
- // have to belong to the playlist that Route is handling
-
- // bool was_soloed = route.soloed();
-
- route.set_solo (true, this);
-
- _session->request_bounded_roll (region->position(), region->position() + region->length());
-
- /* XXX how to unset the solo state ? */
-}
-
/** Start an audition of the first selected region */
void
Editor::play_edit_range ()
for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
- RouteTimeAxisView* rtv;
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
- if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
-
- if (rtv->is_track()) {
+ if (!rtv) {
+ continue;
+ }
- /* no edits to destructive tracks */
+ if (!rtv->is_track()) {
+ continue;
+ }
- if (rtv->track()->destructive()) {
- continue;
- }
+ /* no edits to destructive tracks */
- if ((playlist = rtv->playlist()) != 0) {
+ if (rtv->track()->destructive()) {
+ continue;
+ }
- playlist->clear_changes ();
+ if ((playlist = rtv->playlist()) != 0) {
- /* XXX need to consider musical time selections here at some point */
+ playlist->clear_changes ();
- double speed = rtv->track()->speed();
+ /* XXX need to consider musical time selections here at some point */
+ double speed = rtv->track()->speed();
- for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
+ for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
- sigc::connection c = rtv->view()->RegionViewAdded.connect (
- sigc::mem_fun(*this, &Editor::collect_new_region_view));
+ sigc::connection c = rtv->view()->RegionViewAdded.connect (
+ sigc::mem_fun(*this, &Editor::collect_new_region_view));
- latest_regionviews.clear ();
+ latest_regionviews.clear ();
- playlist->partition ((framepos_t)((*t).start * speed),
- (framepos_t)((*t).end * speed), false);
+ playlist->partition ((framepos_t)((*t).start * speed),
+ (framepos_t)((*t).end * speed), false);
- c.disconnect ();
+ c.disconnect ();
- if (!latest_regionviews.empty()) {
+ if (!latest_regionviews.empty()) {
- rtv->view()->foreach_regionview (sigc::bind (
- sigc::ptr_fun (add_if_covered),
- &(*t), &new_selection));
+ 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;
- }
+ if (!in_command) {
+ begin_reversible_command (_("separate"));
+ in_command = true;
+ }
- /* pick up changes to existing regions */
+ /* pick up changes to existing regions */
- vector<Command*> cmds;
- playlist->rdiff (cmds);
- _session->add_commands (cmds);
+ vector<Command*> cmds;
+ playlist->rdiff (cmds);
+ _session->add_commands (cmds);
- /* pick up changes to the playlist itself (adds/removes)
- */
+ /* pick up changes to the playlist itself (adds/removes)
+ */
- _session->add_command(new StatefulDiffCommand (playlist));
- }
- }
+ _session->add_command(new StatefulDiffCommand (playlist));
}
}
}
}
struct PlaylistState {
- boost::shared_ptr<Playlist> playlist;
- XMLNode* before;
+ boost::shared_ptr<Playlist> playlist;
+ XMLNode* before;
};
/** Take tracks from get_tracks_for_range_action and cut any regions
boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
- if (!playlist) {
+ if (!playlist) {
// is this check necessary?
- continue;
- }
+ continue;
+ }
vector<PlaylistState>::iterator i;
for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
- RouteTimeAxisView* rtv;
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
- if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
+ if (!rtv) {
+ continue;
+ }
- boost::shared_ptr<Track> t = rtv->track();
+ boost::shared_ptr<Track> t = rtv->track();
- if (t != 0 && ! t->destructive()) {
+ if (t != 0 && ! t->destructive()) {
- if ((playlist = rtv->playlist()) != 0) {
- playlists.push_back (playlist);
- }
+ if ((playlist = rtv->playlist()) != 0) {
+ playlists.push_back (playlist);
}
}
}
return;
}
- framepos_t the_start;
- framepos_t the_end;
- framepos_t cnt;
-
- begin_reversible_command (_("trim to selection"));
+ framepos_t pos;
+ framepos_t new_start;
+ framepos_t new_end;
+ framecnt_t new_length;
+ bool in_command = false;
for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
- boost::shared_ptr<Region> region;
+ /* Only the top regions at start and end have to be cropped */
+ boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
+ boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
- the_start = start;
+ vector<boost::shared_ptr<Region> > regions;
- if ((region = (*i)->top_region_at(the_start)) == 0) {
- continue;
+ if (region_at_start != 0) {
+ regions.push_back (region_at_start);
+ }
+ if (region_at_end != 0) {
+ regions.push_back (region_at_end);
}
- /* now adjust lengths to that we do the right thing
- if the selection extends beyond the region
- */
+ /* now adjust lengths */
+ for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
- the_start = max (the_start, (framepos_t) region->position());
- if (max_framepos - the_start < region->length()) {
- the_end = the_start + region->length() - 1;
- } else {
- the_end = max_framepos;
- }
- the_end = min (end, the_end);
- cnt = the_end - the_start + 1;
+ pos = (*i)->position();
+ new_start = max (start, pos);
+ if (max_framepos - pos > (*i)->length()) {
+ new_end = pos + (*i)->length() - 1;
+ } else {
+ new_end = max_framepos;
+ }
+ new_end = min (end, new_end);
+ new_length = new_end - new_start + 1;
- region->clear_changes ();
- region->trim_to (the_start, cnt);
- _session->add_command (new StatefulDiffCommand (region));
+ if(!in_command) {
+ begin_reversible_command (_("trim to selection"));
+ in_command = true;
+ }
+ (*i)->clear_changes ();
+ (*i)->trim_to (new_start, new_length);
+ _session->add_command (new StatefulDiffCommand (*i));
+ }
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
Editor::region_fill_track ()
{
- RegionSelection rs = get_regions_from_selection_and_entered ();
-
- if (!_session || rs.empty()) {
- return;
- }
+ boost::shared_ptr<Playlist> playlist;
+ RegionSelection regions = get_regions_from_selection_and_entered ();
+ RegionSelection foo;
framepos_t const end = _session->current_end_frame ();
- begin_reversible_command (Operations::region_fill);
-
- for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
-
- boost::shared_ptr<Region> region ((*i)->region());
-
- boost::shared_ptr<Playlist> pl = region->playlist();
-
- if (end <= region->last_frame()) {
- return;
- }
-
- double times = (double) (end - region->last_frame()) / (double) region->length();
-
- if (times == 0) {
- return;
- }
-
- pl->clear_changes ();
- pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
- _session->add_command (new StatefulDiffCommand (pl));
- }
-
- commit_reversible_command ();
-}
-
-void
-Editor::region_fill_selection ()
-{
- if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
- return;
- }
-
- if (selection->time.empty()) {
+ if (regions.empty () || regions.end_frame () + 1 >= end) {
return;
}
- boost::shared_ptr<Region> region = _regions->get_single_selection ();
- if (region == 0) {
- return;
- }
+ framepos_t const start_frame = regions.start ();
+ framepos_t const end_frame = regions.end_frame ();
+ framecnt_t const gap = end_frame - start_frame + 1;
- framepos_t start = selection->time[clicked_selection].start;
- framepos_t end = selection->time[clicked_selection].end;
+ begin_reversible_command (Operations::region_fill);
- boost::shared_ptr<Playlist> playlist;
+ selection->clear_regions ();
- if (selection->tracks.empty()) {
- return;
- }
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
- framepos_t selection_length = end - start;
- float times = (float)selection_length / region->length();
+ boost::shared_ptr<Region> r ((*i)->region());
- begin_reversible_command (Operations::fill_selection);
+ TimeAxisView& tv = (*i)->get_time_axis_view();
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
+ latest_regionviews.clear ();
+ sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
- TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+ framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
+ playlist = (*i)->region()->playlist();
+ playlist->clear_changes ();
+ playlist->duplicate_until (r, position, gap, end);
+ _session->add_command(new StatefulDiffCommand (playlist));
- for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
+ c.disconnect ();
- if ((playlist = (*i)->playlist()) == 0) {
- continue;
- }
+ foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
+ }
- playlist->clear_changes ();
- playlist->add_region (RegionFactory::create (region, true), start, times);
- _session->add_command (new StatefulDiffCommand (playlist));
+ if (!foo.empty()) {
+ selection->set (foo);
}
commit_reversible_command ();
}
struct RegionSortByTime {
- bool operator() (const RegionView* a, const RegionView* b) {
- return a->region()->position() < b->region()->position();
- }
+ bool operator() (const RegionView* a, const RegionView* b) {
+ return a->region()->position() < b->region()->position();
+ }
};
void
if (front) {
(*i)->region()->trim_front (where);
- maybe_locate_with_edit_preroll ( where );
} else {
(*i)->region()->trim_end (where);
- maybe_locate_with_edit_preroll ( where );
}
_session->add_command (new StatefulDiffCommand ((*i)->region()));
Editor::trim_region_to_location (const Location& loc, const char* str)
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
- begin_reversible_command (str);
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
RegionView* rv = (*x);
rv->region()->clear_changes ();
rv->region()->trim_to (start, (end - start));
+
+ if (!in_command) {
+ begin_reversible_command (str);
+ in_command = true;
+ }
_session->add_command(new StatefulDiffCommand (rv->region()));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
Editor::trim_to_region(bool forward)
{
RegionSelection rs = get_regions_from_selection_and_entered ();
-
- begin_reversible_command (_("trim to region"));
+ bool in_command = false;
boost::shared_ptr<Region> next_region;
AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
if (!atav) {
- return;
+ continue;
}
float speed = 1.0;
if (forward) {
- next_region = playlist->find_next_region (region->first_frame(), Start, 1);
+ next_region = playlist->find_next_region (region->first_frame(), Start, 1);
- if (!next_region) {
- continue;
- }
+ if (!next_region) {
+ continue;
+ }
- region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
- arv->region_changed (PropertyChange (ARDOUR::Properties::length));
+ region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
+ arv->region_changed (PropertyChange (ARDOUR::Properties::length));
}
else {
- next_region = playlist->find_next_region (region->first_frame(), Start, 0);
+ next_region = playlist->find_next_region (region->first_frame(), Start, 0);
- if(!next_region){
- continue;
- }
+ if(!next_region){
+ continue;
+ }
- region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
+ region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
- arv->region_changed (ARDOUR::bounds_change);
+ arv->region_changed (ARDOUR::bounds_change);
}
+ if (!in_command) {
+ begin_reversible_command (_("trim to region"));
+ in_command = true;
+ }
_session->add_command(new StatefulDiffCommand (region));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
/* 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 */
Glib::usleep (250000);
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);
+ "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);
gtk_main_iteration ();
}
+ pthread_join (itt.thread, 0);
current_interthread_info = 0;
}
framepos_t start = selection->time[clicked_selection].start;
framepos_t end = selection->time[clicked_selection].end;
framepos_t cnt = end - start + 1;
-
- begin_reversible_command (_("bounce range"));
+ bool in_command = false;
for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
- RouteTimeAxisView* rtv;
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
- if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
+ if (!rtv) {
continue;
}
boost::shared_ptr<Playlist> playlist;
if ((playlist = rtv->playlist()) == 0) {
- return;
+ continue;
}
InterThreadInfo itt;
playlist->add_region (r, start);
}
+ if (!in_command) {
+ begin_reversible_command (_("bounce range"));
+ in_command = true;
+ }
vector<Command*> cmds;
playlist->rdiff (cmds);
_session->add_commands (cmds);
_session->add_command (new StatefulDiffCommand (playlist));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
/** Delete selected regions, automation points or a time range */
}
}
- if ( op != Delete ) //"Delete" doesn't change copy/paste buf
+ if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
cut_buffer->clear ();
+ }
if (entered_marker) {
Location* loc = find_location_from_marker (entered_marker, ignored);
if (_session && loc) {
+ entered_marker = NULL;
Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
}
if (!selection->regions.empty()) {
cut_copy_regions (op, selection->regions);
-
+
if (op == Cut || op == Delete) {
selection->clear_regions ();
}
}
-
+
if (!selection->points.empty()) {
cut_copy_points (op);
-
+
if (op == Cut || op == Delete) {
selection->clear_points ();
}
did_edit = true;
cut_copy_ranges (op);
-
+
if (op == Cut || op == Delete) {
selection->clear_time ();
}
}
-
+
if (did_edit) {
/* reset repeated paste state */
paste_count = 0;
last_paste_pos = 0;
- commit_reversible_command ();
+ commit_reversible_command ();
}
-
+
if (op == Delete || op == Cut || op == Clear) {
_drags->abort ();
}
}
+
struct AutomationRecord {
AutomationRecord () : state (0) , line(NULL) {}
AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
-
+
XMLNode* state; ///< state before any operation
const AutomationLine* line; ///< line this came from
boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
};
+struct PointsSelectionPositionSorter {
+ bool operator() (ControlPoint* a, ControlPoint* b) {
+ return (*(a->model()))->when < (*(b->model()))->when;
+ }
+};
+
/** Cut, copy or clear selected automation points.
* @param op Operation (Cut, Copy or Clear)
*/
typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
Lists lists;
+ /* user could select points in any order */
+ selection->points.sort(PointsSelectionPositionSorter ());
+
/* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
- for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
- const AutomationLine& line = (*i)->line();
+ for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+ const AutomationLine& line = (*sel_point)->line();
const boost::shared_ptr<AutomationList> al = line.the_list();
if (lists.find (al) == lists.end ()) {
/* We haven't seen this list yet, so make a record for it. This includes
}
/* Add all selected points to the relevant copy ControlLists */
- framepos_t start = std::numeric_limits<framepos_t>::max();
- 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();
+ MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
+ for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+ boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
+ AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
- lists[al].copy->fast_simple_add ((*j)->when, (*j)->value);
+ lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
if (midi) {
/* Update earliest MIDI start time in beats */
- earliest = std::min(earliest, Evoral::Beats((*j)->when));
+ earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
} else {
/* Update earliest session start time in frames */
- start = std::min(start, (*i)->line().session_position(j));
+ start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
}
}
}
earliest.round_down_to_beat();
} else {
- if (start == std::numeric_limits<double>::max()) {
- start = 0; // Weird... don't offset
+ if (start.frame == std::numeric_limits<double>::max()) {
+ start.frame = 0; // Weird... don't offset
}
snap_to(start, RoundDownMaybe);
}
- const double line_offset = midi ? earliest.to_double() : start;
+ const double line_offset = midi ? earliest.to_double() : start.frame;
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
/* Correct this copy list so that it is relative to the earliest
start time, so relative ordering between points is preserved
when copying from several lists and the paste starts at the
earliest copied piece of data. */
- for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
- (*j)->when -= line_offset;
+ boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
+ for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
+ (*ctrl_evt)->when -= line_offset;
}
/* And add it to the cut buffer */
- cut_buffer->add (i->second.copy);
+ cut_buffer->add (al_cpy);
}
}
-
+
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 ();
}
/* 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 ());
+ for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
+ AutomationLine& line = (*sel_point)->line ();
+ boost::shared_ptr<AutomationList> al = line.the_list();
+
+ bool erase = true;
+
+ if (dynamic_cast<AudioRegionGainLine*> (&line)) {
+ /* removing of first and last gain point in region gain lines is prohibited*/
+ if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
+ erase = false;
+ }
+ }
+
+ if(erase) {
+ al->erase ((*sel_point)->model ());
+ }
}
/* Thaw the lists and add undo records for them */
}
struct lt_playlist {
- bool operator () (const PlaylistState& a, const PlaylistState& b) {
- return a.playlist < b.playlist;
- }
+ bool operator () (const PlaylistState& a, const PlaylistState& b) {
+ return a.playlist < b.playlist;
+ }
};
struct PlaylistMapping {
- TimeAxisView* tv;
- boost::shared_ptr<Playlist> pl;
+ TimeAxisView* tv;
+ boost::shared_ptr<Playlist> pl;
- PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
+ PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
};
/** Remove `clicked_regionview' */
vector<Command*> cmds;
playlist->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command(new StatefulDiffCommand (playlist));
commit_reversible_command ();
}
return;
}
- begin_reversible_command (_("remove region"));
-
list<boost::shared_ptr<Region> > regions_to_remove;
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
- if (!playlist) {
+ if (!playlist) {
// is this check necessary?
- continue;
- }
+ continue;
+ }
/* get_regions_from_selection_and_entered() guarantees that
the playlists involved are unique, so there is no need
}
vector<boost::shared_ptr<Playlist> >::iterator pl;
+ bool in_command = false;
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.
*/
+
+ if (!in_command) {
+ begin_reversible_command (_("remove region"));
+ in_command = true;
+ }
vector<Command*> cmds;
(*pl)->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command(new StatefulDiffCommand (*pl));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
/** Cut, copy or clear selected regions.
if (op != Delete) {
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();
if (Config->get_edit_mode() == Ripple)
pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
break;
-
+
case Cut:
_xx = RegionFactory::create (r);
npl->add_region (_xx, r->position() - first_position);
if (op != Delete) {
list<boost::shared_ptr<Playlist> > foo;
-
- /* the pmap is in the same order as the tracks in which selected regions occured */
-
+
+ /* the pmap is in the same order as the tracks in which selected regions occurred */
+
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 {
vector<Command*> cmds;
(*pl)->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command (new StatefulDiffCommand (*pl));
}
}
return;
}
ts.push_back (entered_track);
- }
+ }
for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
(*i)->cut_copy_clear (*selection, op);
void
Editor::paste (float times, bool from_context)
{
- DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
-
- paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times);
+ DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
+ MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
+ paste_internal (where.frame, times, 0);
}
void
Editor::mouse_paste ()
{
- framepos_t where;
+ MusicFrame where (0, 0);
bool ignored;
-
- if (!mouse_frame (where, ignored)) {
+ if (!mouse_frame (where.frame, ignored)) {
return;
}
snap_to (where);
- paste_internal (where, 1);
+ paste_internal (where.frame, 1, where.division);
}
void
-Editor::paste_internal (framepos_t position, float times)
+Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
{
- DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
+ DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
if (cut_buffer->empty(internal_editing())) {
return;
if (position == max_framepos) {
position = get_preferred_edit_position();
- DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
+ DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
}
if (position == last_paste_pos) {
TrackViewList ts;
if (!selection->tracks.empty()) {
/* If there is a track selection, paste into exactly those tracks and
- only those tracks. This allows the user to be explicit and override
- the below "do the reasonable thing" logic. */
+ * only those tracks. This allows the user to be explicit and override
+ * the below "do the reasonable thing" logic. */
ts = selection->tracks.filter_to_unique_playlists ();
sort_track_selection (ts);
} else {
/* Only one line copied, and one automation track selected. Do a
"greedy" paste from one automation type to another. */
- PasteContext ctx(paste_count, times, ItemCounts(), true);
- ts.front()->paste (position, *cut_buffer, ctx);
+ PasteContext ctx(paste_count, times, ItemCounts(), true);
+ ts.front()->paste (position, *cut_buffer, ctx, sub_num);
} else {
PasteContext ctx(paste_count, times, ItemCounts(), false);
for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
- (*i)->paste (position, *cut_buffer, ctx);
+ (*i)->paste (position, *cut_buffer, ctx, sub_num);
}
}
commit_reversible_command ();
}
+void
+Editor::duplicate_regions (float times)
+{
+ RegionSelection rs (get_regions_from_selection_and_entered());
+ duplicate_some_regions (rs, times);
+}
+
void
Editor::duplicate_some_regions (RegionSelection& regions, float times)
{
+ if (regions.empty ()) {
+ return;
+ }
+
boost::shared_ptr<Playlist> playlist;
RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
RegionSelection foo;
framepos_t const start_frame = regions.start ();
framepos_t const end_frame = regions.end_frame ();
+ framecnt_t const gap = end_frame - start_frame + 1;
begin_reversible_command (Operations::duplicate_region);
latest_regionviews.clear ();
sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
- playlist = (*i)->region()->playlist();
+ framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
+ playlist = (*i)->region()->playlist();
playlist->clear_changes ();
- playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
+ playlist->duplicate (r, position, gap, times);
_session->add_command(new StatefulDiffCommand (playlist));
c.disconnect ();
}
boost::shared_ptr<Playlist> playlist;
- vector<boost::shared_ptr<Region> > new_regions;
- vector<boost::shared_ptr<Region> >::iterator ri;
-
- create_region_from_selection (new_regions);
-
- if (new_regions.empty()) {
- return;
- }
-
- begin_reversible_command (_("duplicate selection"));
-
- ri = new_regions.begin();
TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+ bool in_command = false;
+
for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
if ((playlist = (*i)->playlist()) == 0) {
continue;
}
playlist->clear_changes ();
- framepos_t end;
+
if (clicked_selection) {
- end = selection->time[clicked_selection].end;
+ playlist->duplicate_range (selection->time[clicked_selection], times);
} else {
- end = selection->time.end_frame();
+ playlist->duplicate_ranges (selection->time, times);
}
- playlist->duplicate (*ri, end, times);
- _session->add_command (new StatefulDiffCommand (playlist));
- ++ri;
- if (ri == new_regions.end()) {
- --ri;
+ if (!in_command) {
+ begin_reversible_command (_("duplicate range selection"));
+ in_command = true;
}
+ _session->add_command (new StatefulDiffCommand (playlist));
+
}
- commit_reversible_command ();
+ if (in_command) {
+ if (times == 1.0f) {
+ // now "move" range selection to after the current range selection
+ framecnt_t distance = 0;
+
+ if (clicked_selection) {
+ distance =
+ selection->time[clicked_selection].end - selection->time[clicked_selection].start;
+ } else {
+ distance = selection->time.end_frame () - selection->time.start ();
+ }
+
+ selection->move_time (distance);
+ }
+ commit_reversible_command ();
+ }
}
/** Reset all selected points to the relevant default value */
{
for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
ARDOUR::AutomationList::iterator j = (*i)->model ();
- (*j)->value = (*i)->line().the_list()->default_value ();
+ (*j)->value = (*i)->line().the_list()->descriptor ().normal;
}
}
return;
}
- begin_reversible_command (_("nudge track"));
-
TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
+ bool in_command = false;
for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
playlist->nudge_after (start, distance, forwards);
+ if (!in_command) {
+ begin_reversible_command (_("nudge track"));
+ in_command = true;
+ }
vector<Command*> cmds;
playlist->rdiff (cmds);
_session->add_command (new StatefulDiffCommand (playlist));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (Config->get_verify_remove_last_capture()) {
prompt = _("Do you really want to destroy the last capture?"
- "\n(This is destructive and cannot be undone)");
+ "\n(This is destructive and cannot be undone)");
choices.push_back (_("No, do nothing."));
choices.push_back (_("Yes, destroy it."));
- Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
+ Choice prompter (_("Destroy last capture"), prompt, choices);
if (prompter.run () == 1) {
_session->remove_last_capture ();
NormalizeDialog dialog (rs.size() > 1);
- if (dialog.run () == RESPONSE_CANCEL) {
+ if (dialog.run () != RESPONSE_ACCEPT) {
return;
}
obtain the maximum amplitude of them all.
*/
list<double> max_amps;
+ list<double> rms_vals;
double max_amp = 0;
+ double max_rms = 0;
+ bool use_rms = dialog.constrain_rms ();
+
for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
- if (arv) {
- dialog.descend (1.0 / regions);
- double const a = arv->audio_region()->maximum_amplitude (&dialog);
-
- if (a == -1) {
- /* the user cancelled the operation */
- return;
- }
+ if (!arv) {
+ continue;
+ }
+ dialog.descend (1.0 / regions);
+ double const a = arv->audio_region()->maximum_amplitude (&dialog);
+ if (use_rms) {
+ double r = arv->audio_region()->rms (&dialog);
+ max_rms = max (max_rms, r);
+ rms_vals.push_back (r);
+ }
- max_amps.push_back (a);
- max_amp = max (max_amp, a);
- dialog.ascend ();
+ if (a == -1) {
+ /* the user cancelled the operation */
+ return;
}
- }
- begin_reversible_command (_("normalize"));
+ max_amps.push_back (a);
+ max_amp = max (max_amp, a);
+ dialog.ascend ();
+ }
list<double>::const_iterator a = max_amps.begin ();
+ list<double>::const_iterator l = rms_vals.begin ();
+ bool in_command = false;
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
arv->region()->clear_changes ();
- double const amp = dialog.normalize_individually() ? *a : max_amp;
+ double amp = dialog.normalize_individually() ? *a : max_amp;
+ double target = dialog.target_peak (); // dB
+
+ if (use_rms) {
+ double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
+ const double t_rms = dialog.target_rms ();
+ const gain_t c_peak = dB_to_coefficient (target);
+ const gain_t c_rms = dB_to_coefficient (t_rms);
+ if ((amp_rms / c_rms) > (amp / c_peak)) {
+ amp = amp_rms;
+ target = t_rms;
+ }
+ }
- arv->audio_region()->normalize (amp, dialog.target ());
+ arv->audio_region()->normalize (amp, target);
+
+ if (!in_command) {
+ begin_reversible_command (_("normalize"));
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (arv->region()));
++a;
+ ++l;
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
return;
}
- begin_reversible_command ("reset gain");
+ bool in_command = false;
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
continue;
arv->region()->clear_changes ();
arv->audio_region()->set_scale_amplitude (1.0f);
+
+ if(!in_command) {
+ begin_reversible_command ("reset gain");
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (arv->region()));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
return;
}
- begin_reversible_command ("adjust region gain");
+ bool in_command = false;
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
}
arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
+
+ if (!in_command) {
+ begin_reversible_command ("adjust region gain");
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (arv->region()));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
+void
+Editor::reset_region_gain ()
+{
+ RegionSelection rs = get_regions_from_selection_and_entered ();
+
+ if (!_session || rs.empty()) {
+ return;
+ }
+
+ bool in_command = false;
+
+ for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
+ AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
+ if (!arv) {
+ continue;
+ }
+
+ arv->region()->clear_changes ();
+
+ arv->audio_region()->set_scale_amplitude (1.0f);
+
+ if (!in_command) {
+ begin_reversible_command ("reset region gain");
+ in_command = true;
+ }
+ _session->add_command (new StatefulDiffCommand (arv->region()));
+ }
+
+ if (in_command) {
+ commit_reversible_command ();
+ }
+}
void
Editor::reverse_region ()
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
- if (arv) {
+ if (arv) {
audio_only.push_back (arv);
}
}
+ assert (!audio_only.empty());
+
StripSilenceDialog d (_session, audio_only);
int const r = d.run ();
- d.drop_rects ();
+ d.drop_rects ();
- if (r == Gtk::RESPONSE_OK) {
- ARDOUR::AudioIntervalMap silences;
- d.silences (silences);
+ if (r == Gtk::RESPONSE_OK) {
+ ARDOUR::AudioIntervalMap silences;
+ d.silences (silences);
StripSilence s (*_session, silences, d.fade_length());
+
apply_filter (s, _("strip silence"), &d);
}
}
vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
v.push_back (selected);
- framepos_t pos_frames = mrv.midi_region()->position() - mrv.midi_region()->start();
- Evoral::Beats pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
+ Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
return op (mrv.midi_region()->model(), pos_beats, v);
}
return;
}
- begin_reversible_command (op.name ());
+ bool in_command = false;
for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
RegionSelection::const_iterator tmp = r;
if (mrv) {
Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
if (cmd) {
+ if (!in_command) {
+ begin_reversible_command (op.name ());
+ in_command = true;
+ }
(*cmd)();
_session->add_command (cmd);
}
r = tmp;
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
return;
}
- begin_reversible_command (_("Fork Region(s)"));
-
CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
+ bool in_command = false;
+
gdk_flush ();
for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
-
+
+ if (!in_command) {
+ begin_reversible_command (_("Fork Region(s)"));
+ in_command = true;
+ }
playlist->clear_changes ();
playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
_session->add_command(new StatefulDiffCommand (playlist));
r = tmp;
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
return;
}
- QuantizeDialog* qd = new QuantizeDialog (*this);
+ if (!quantize_dialog) {
+ quantize_dialog = new QuantizeDialog (*this);
+ }
- qd->present ();
- const int r = qd->run ();
- qd->hide ();
+ if (quantize_dialog->is_mapped()) {
+ /* in progress already */
+ return;
+ }
+
+ quantize_dialog->present ();
+ const int r = quantize_dialog->run ();
+ quantize_dialog->hide ();
if (r == Gtk::RESPONSE_OK) {
- Quantize quant (qd->snap_start(), qd->snap_end(),
- qd->start_grid_size(), qd->end_grid_size(),
- qd->strength(), qd->swing(), qd->threshold());
+ Quantize quant (quantize_dialog->snap_start(),
+ quantize_dialog->snap_end(),
+ quantize_dialog->start_grid_size(),
+ quantize_dialog->end_grid_size(),
+ quantize_dialog->strength(),
+ quantize_dialog->swing(),
+ quantize_dialog->threshold());
apply_midi_note_edit_op (quant, rs);
}
return;
}
- TransformDialog* td = new TransformDialog();
+ TransformDialog td;
- td->present();
- const int r = td->run();
- td->hide();
+ td.present();
+ const int r = td.run();
+ td.hide();
if (r == Gtk::RESPONSE_OK) {
- Transform transform(td->get());
+ Transform transform(td.get());
apply_midi_note_edit_op(transform, rs);
}
}
+void
+Editor::transpose_region ()
+{
+ if (_session) {
+ transpose_regions(get_regions_from_selection_and_entered ());
+ }
+}
+
+void
+Editor::transpose_regions (const RegionSelection& rs)
+{
+ if (rs.n_midi_regions() == 0) {
+ return;
+ }
+
+ TransposeDialog d;
+ int const r = d.run ();
+
+ if (r == RESPONSE_ACCEPT) {
+ Transpose transpose(d.semitones ());
+ apply_midi_note_edit_op (transpose, rs);
+ }
+}
+
void
Editor::insert_patch_change (bool from_context)
{
MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
- PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
+ PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
if (d.run() == RESPONSE_CANCEL) {
return;
return;
}
- begin_reversible_command (command);
-
CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
+ bool in_command = false;
+
gdk_flush ();
int n = 0;
playlist->clear_changes ();
playlist->clear_owned_changes ();
+ if (!in_command) {
+ begin_reversible_command (command);
+ in_command = true;
+ }
+
if (filter.results.empty ()) {
/* no regions returned; remove the old one */
vector<Command*> cmds;
playlist->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command(new StatefulDiffCommand (playlist));
- } else {
- return;
}
if (progress) {
++n;
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
return;
}
- begin_reversible_command (_("reset region gain"));
-
+ bool in_command = false;
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
if (arv) {
XMLNode& before (alist->get_state());
arv->audio_region()->set_default_envelope ();
+
+ if (!in_command) {
+ begin_reversible_command (_("reset region gain"));
+ in_command = true;
+ }
_session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
}
}
-
- commit_reversible_command ();
+
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
return;
}
- begin_reversible_command (_("region gain envelope active"));
-
+ bool in_command = false;
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
if (arv) {
arv->region()->clear_changes ();
arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
+
+ if (!in_command) {
+ begin_reversible_command (_("region gain envelope active"));
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (arv->region()));
}
}
-
- commit_reversible_command ();
+
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
}
begin_reversible_command (_("toggle region lock"));
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
(*i)->region()->clear_changes ();
(*i)->region()->set_locked (!(*i)->region()->locked());
_session->add_command (new StatefulDiffCommand ((*i)->region()));
}
-
+
commit_reversible_command ();
}
}
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()));
}
-
+
commit_reversible_command ();
}
return;
}
- begin_reversible_command (_("region lock style"));
-
+ Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
+ vector<Widget*> proxies = a->get_proxies();
+ Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
+
+ assert (cmi);
+
+ begin_reversible_command (_("toggle region lock style"));
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
(*i)->region()->clear_changes ();
- PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
+ PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
(*i)->region()->set_position_lock_style (ns);
_session->add_command (new StatefulDiffCommand ((*i)->region()));
}
-
+
commit_reversible_command ();
}
}
begin_reversible_command (_("change region opacity"));
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
(*i)->region()->clear_changes ();
(*i)->region()->set_opaque (!(*i)->region()->opaque());
_session->add_command (new StatefulDiffCommand ((*i)->region()));
}
-
+
commit_reversible_command ();
}
continue;
if (first) {
- new_state = !rtav->track()->record_enabled();
+ new_state = !rtav->track()->rec_enable_control()->get_value();
first = false;
}
- rtav->track()->set_record_enabled (new_state, this);
+ rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
}
}
{
bool new_state = false;
bool first = true;
- boost::shared_ptr<RouteList> rl (new RouteList);
+ boost::shared_ptr<ControlList> cl (new ControlList);
for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
first = false;
}
- rl->push_back (rtav->route());
+ cl->push_back (rtav->route()->solo_control());
}
- _session->set_solo (rl, new_state, Session::rt_cleanup, true);
+ _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
}
void
rl->push_back (rtav->route());
}
- _session->set_mute (rl, new_state, Session::rt_cleanup, true);
+ _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
}
void
cmd = _("set fade out length");
}
- begin_reversible_command (cmd);
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
if (!tmp) {
- return;
+ continue;
}
boost::shared_ptr<AutomationList> alist;
tmp->audio_region()->set_fade_out_active (true);
}
+ if (!in_command) {
+ begin_reversible_command (cmd);
+ in_command = true;
+ }
XMLNode &after = alist->get_state();
_session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (rs.empty()) {
return;
}
-
- begin_reversible_command (_("set fade in shape"));
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
if (!tmp) {
- return;
+ continue;
}
boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
tmp->audio_region()->set_fade_in_shape (shape);
+ if (!in_command) {
+ begin_reversible_command (_("set fade in shape"));
+ in_command = true;
+ }
XMLNode &after = alist->get_state();
_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
}
- commit_reversible_command ();
-
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (rs.empty()) {
return;
}
-
- begin_reversible_command (_("set fade out shape"));
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
if (!tmp) {
- return;
+ continue;
}
boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
tmp->audio_region()->set_fade_out_shape (shape);
+ if(!in_command) {
+ begin_reversible_command (_("set fade out shape"));
+ in_command = true;
+ }
XMLNode &after = alist->get_state();
_session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (rs.empty()) {
return;
}
-
- begin_reversible_command (_("set fade in active"));
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
if (!tmp) {
- return;
+ continue;
}
ar->clear_changes ();
ar->set_fade_in_active (yn);
+
+ if (!in_command) {
+ begin_reversible_command (_("set fade in active"));
+ in_command = true;
+ }
_session->add_command (new StatefulDiffCommand (ar));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (rs.empty()) {
return;
}
-
- begin_reversible_command (_("set fade out active"));
+ bool in_command = false;
for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
if (!tmp) {
- return;
+ continue;
}
boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
ar->clear_changes ();
ar->set_fade_out_active (yn);
+
+ if (!in_command) {
+ begin_reversible_command (_("set fade out active"));
+ in_command = true;
+ }
_session->add_command(new StatefulDiffCommand (ar));
}
- commit_reversible_command ();
+ if (in_command) {
+ commit_reversible_command ();
+ }
}
void
if (_ignore_region_action) {
return;
}
-
+
boost::shared_ptr<AudioRegion> ar;
bool yn = false;
}
/* XXX should this undo-able? */
+ bool in_command = false;
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
continue;
}
+ ar->clear_changes ();
+
if (dir == 1 || dir == 0) {
ar->set_fade_in_active (!yn);
}
if (dir == -1 || dir == 0) {
ar->set_fade_out_active (!yn);
}
+ if (!in_command) {
+ begin_reversible_command (_("toggle fade active"));
+ in_command = true;
+ }
+ _session->add_command(new StatefulDiffCommand (ar));
+ }
+
+ if (in_command) {
+ commit_reversible_command ();
}
}
void
Editor::set_edit_point ()
{
- framepos_t where;
bool ignored;
+ MusicFrame where (0, 0);
- if (!mouse_frame (where, ignored)) {
+ if (!mouse_frame (where.frame, ignored)) {
return;
}
if (selection->markers.empty()) {
- mouse_add_new_marker (where);
+ mouse_add_new_marker (where.frame);
} else {
bool ignored;
Location* loc = find_location_from_marker (selection->markers.front(), ignored);
if (loc) {
- loc->move_to (where);
+ loc->move_to (where.frame, where.division);
}
}
}
if (entered_marker) {
_session->request_locate (entered_marker->position(), _session->transport_rolling());
} else {
- framepos_t where;
+ MusicFrame where (0, 0);
bool ignored;
- if (!mouse_frame (where, ignored)) {
+ if (!mouse_frame (where.frame, ignored)) {
return;
}
snap_to (where);
if (_session) {
- _session->request_locate (where, _session->transport_rolling());
+ _session->request_locate (where.frame, _session->transport_rolling());
}
}
- if (ARDOUR_UI::config()->get_follow_edits()) {
- cancel_time_selection();
- }
+//not sure what this was for; remove it for now.
+// if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
+// cancel_time_selection();
+// }
+
}
void
Editor::split_region ()
{
+ if (_drags->active ()) {
+ return;
+ }
+
//if a range is selected, separate it
if ( !selection->time.empty()) {
separate_regions_between (selection->time);
//if no range was selected, try to find some regions to split
if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
-
- RegionSelection rs = get_regions_from_selection_and_edit_point ();
- framepos_t where = get_preferred_edit_position ();
+ RegionSelection rs = get_regions_from_selection_and_edit_point ();
+ const framepos_t pos = get_preferred_edit_position();
+ const int32_t division = get_grid_music_divisions (0);
+ MusicFrame where (pos, division);
if (rs.empty()) {
return;
}
split_regions_at (where, rs);
+
}
}
-struct EditorOrderRouteSorter {
- bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- return a->order_key () < b->order_key ();
- }
-};
-
void
Editor::select_next_route()
{
RouteUI *rui;
do {
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+
if (*i == current) {
++i;
if (i != track_views.end()) {
break;
}
}
+
rui = dynamic_cast<RouteUI *>(current);
- } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
- selection->set(current);
+ } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+
+ selection->set (current);
ensure_time_axis_view_is_visible (*current, false);
}
RouteUI *rui;
do {
for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
+
if (*i == current) {
++i;
if (i != track_views.rend()) {
}
}
rui = dynamic_cast<RouteUI *>(current);
- } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
+
+ } while (current->hidden() || (rui == NULL) || !rui->route()->active());
selection->set (current);
set_punch_range (start, end, _("set punch range from selection"));
}
+void
+Editor::set_auto_punch_range ()
+{
+ // auto punch in/out button from a single button
+ // If Punch In is unset, set punch range from playhead to end, enable punch in
+ // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
+ // rewound beyond the Punch In marker, in which case that marker will be moved back
+ // to the current playhead position.
+ // If punch out is set, it clears the punch range and Punch In/Out buttons
+
+ if (_session == 0) {
+ return;
+ }
+
+ Location* tpl = transport_punch_location();
+ framepos_t now = playhead_cursor->current_frame();
+ framepos_t begin = now;
+ framepos_t end = _session->current_end_frame();
+
+ if (!_session->config.get_punch_in()) {
+ // First Press - set punch in and create range from here to eternity
+ set_punch_range (begin, end, _("Auto Punch In"));
+ _session->config.set_punch_in(true);
+ } else if (tpl && !_session->config.get_punch_out()) {
+ // Second press - update end range marker and set punch_out
+ if (now < tpl->start()) {
+ // playhead has been rewound - move start back and pretend nothing happened
+ begin = now;
+ set_punch_range (begin, end, _("Auto Punch In/Out"));
+ } else {
+ // normal case for 2nd press - set the punch out
+ end = playhead_cursor->current_frame ();
+ set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
+ _session->config.set_punch_out(true);
+ }
+ } else {
+ if (_session->config.get_punch_out()) {
+ _session->config.set_punch_out(false);
+ }
+
+ if (_session->config.get_punch_in()) {
+ _session->config.set_punch_in(false);
+ }
+
+ if (tpl)
+ {
+ // third press - unset punch in/out and remove range
+ _session->locations()->remove(tpl);
+ }
+ }
+
+}
+
void
Editor::set_session_extents_from_selection ()
{
if (_session == 0) {
return;
}
-
+
framepos_t start, end;
if (!get_selection_extents ( start, end))
return;
Location* loc;
if ((loc = _session->locations()->session_range_location()) == 0) {
- _session->set_session_extents ( start, end ); // this will create a new session range; no need for UNDO
+ _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
} else {
XMLNode &before = loc->get_state();
- _session->set_session_extents ( start, end );
+ _session->set_session_extents (start, end);
XMLNode &after = loc->get_state();
commit_reversible_command ();
}
+
+ _session->set_end_is_free (false);
+}
+
+void
+Editor::set_punch_start_from_edit_point ()
+{
+ if (_session) {
+
+ MusicFrame start (0, 0);
+ framepos_t end = max_framepos;
+
+ //use the existing punch end, if any
+ Location* tpl = transport_punch_location();
+ if (tpl) {
+ end = tpl->end();
+ }
+
+ if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
+ start.frame = _session->audible_frame();
+ } else {
+ start.frame = get_preferred_edit_position();
+ }
+
+ //snap the selection start/end
+ snap_to(start);
+
+ //if there's not already a sensible selection endpoint, go "forever"
+ if (start.frame > end ) {
+ end = max_framepos;
+ }
+
+ set_punch_range (start.frame, end, _("set punch start from EP"));
+ }
+
+}
+
+void
+Editor::set_punch_end_from_edit_point ()
+{
+ if (_session) {
+
+ framepos_t start = 0;
+ MusicFrame end (max_framepos, 0);
+
+ //use the existing punch start, if any
+ Location* tpl = transport_punch_location();
+ if (tpl) {
+ start = tpl->start();
+ }
+
+ if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
+ end.frame = _session->audible_frame();
+ } else {
+ end.frame = get_preferred_edit_position();
+ }
+
+ //snap the selection start/end
+ snap_to (end);
+
+ set_punch_range (start, end.frame, _("set punch end from EP"));
+
+ }
+}
+
+void
+Editor::set_loop_start_from_edit_point ()
+{
+ if (_session) {
+
+ MusicFrame start (0, 0);
+ framepos_t end = max_framepos;
+
+ //use the existing loop end, if any
+ Location* tpl = transport_loop_location();
+ if (tpl) {
+ end = tpl->end();
+ }
+
+ if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
+ start.frame = _session->audible_frame();
+ } else {
+ start.frame = get_preferred_edit_position();
+ }
+
+ //snap the selection start/end
+ snap_to (start);
+
+ //if there's not already a sensible selection endpoint, go "forever"
+ if (start.frame > end ) {
+ end = max_framepos;
+ }
+
+ set_loop_range (start.frame, end, _("set loop start from EP"));
+ }
+
+}
+
+void
+Editor::set_loop_end_from_edit_point ()
+{
+ if (_session) {
+
+ framepos_t start = 0;
+ MusicFrame end (max_framepos, 0);
+
+ //use the existing loop start, if any
+ Location* tpl = transport_loop_location();
+ if (tpl) {
+ start = tpl->start();
+ }
+
+ if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
+ end.frame = _session->audible_frame();
+ } else {
+ end.frame = get_preferred_edit_position();
+ }
+
+ //snap the selection start/end
+ snap_to(end);
+
+ set_loop_range (start, end.frame, _("set loop end from EP"));
+ }
}
void
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
Editor::set_tempo_from_region ()
{
{
framepos_t length = end - start;
- const Meter& m (_session->tempo_map().meter_at (start));
+ const Meter& m (_session->tempo_map().meter_at_frame (start));
/* length = 1 bar */
- /* now we want frames per beat.
+ /* We're going to deliver a constant tempo here,
+ so we can use frames per beat to determine length.
+ now we want frames per beat.
we have frames per bar, and beats per bar, so ...
*/
*/
- const TempoSection& t (_session->tempo_map().tempo_section_at (start));
+ const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
bool do_global = false;
XMLNode& before (_session->tempo_map().get_state());
if (do_global) {
- _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
+ _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
} else if (t.frame() == start) {
- _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
+ _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
} else {
- Timecode::BBT_Time bbt;
- _session->tempo_map().bbt_time (start, bbt);
- _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), bbt);
+ /* constant tempo */
+ const Tempo tempo (beats_per_minute, t.note_type());
+ _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
}
XMLNode& after (_session->tempo_map().get_state());
}
begin_reversible_command (_("split regions"));
-
+
for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
RegionSelection::iterator tmp;
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
- if (ar && (ar->get_transients (positions) == 0)) {
+ if (ar) {
+ ar->transients (positions);
split_region_at_points ((*i)->region(), positions, true);
positions.clear ();
}
i = tmp;
}
-
+
commit_reversible_command ();
}
return;
}
-
if (positions.size() > 20 && can_ferret) {
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);
MessageDialog msg (msgstr,
framepos_t pos = 0;
+ framepos_t rstart = r->first_frame ();
+ framepos_t rend = r->last_frame ();
+
while (x != positions.end()) {
/* deal with positons that are out of scope of present region bounds */
- if (*x <= 0 || *x > r->length()) {
+ if (*x <= rstart || *x > rend) {
++x;
continue;
}
- /* file start = original start + how far we from the initial position ?
- */
+ /* file start = original start + how far we from the initial position ? */
framepos_t file_start = r->start() + pos;
- /* length = next position - current position
- */
+ /* length = next position - current position */
- framepos_t len = (*x) - pos;
+ framepos_t len = (*x) - pos - rstart;
/* XXX we do we really want to allow even single-sample regions?
- shouldn't we have some kind of lower limit on region size?
- */
+ * shouldn't we have some kind of lower limit on region size?
+ */
if (len <= 0) {
break;
plist.add (ARDOUR::Properties::length, len);
plist.add (ARDOUR::Properties::name, new_name);
plist.add (ARDOUR::Properties::layer, 0);
+ // TODO set transients_offset
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
+ */
RegionFactory::map_add (nr);
- pl->add_region (nr, r->position() + pos);
+ pl->add_region (nr, rstart + pos);
if (select_new) {
new_regions.push_front(nr);
vector<Command*> cmds;
pl->rdiff (cmds);
_session->add_commands (cmds);
-
+
_session->add_command (new StatefulDiffCommand (pl));
if (select_new) {
framepos_t where = get_preferred_edit_position();
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);
+ (*r)->region()->add_transient(where);
}
-
+
commit_reversible_command ();
}
}
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();
used_playlists.push_back(pl);
pl->freeze();
}
+ (*r)->region()->clear_changes ();
- framepos_t start_frame = (*r)->region()->first_frame ();
- snap_to (start_frame);
- (*r)->region()->set_position (start_frame);
+ MusicFrame start ((*r)->region()->first_frame (), 0);
+ snap_to (start);
+ (*r)->region()->set_position (start.frame, start.division);
+ _session->add_command(new StatefulDiffCommand ((*r)->region()));
}
while (used_playlists.size() > 0) {
(*i)->thaw();
used_playlists.pop_front();
}
-
+
commit_reversible_command ();
}
/* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
begin_reversible_command (_("close region gaps"));
-
+
int idx = 0;
boost::shared_ptr<Region> last_region;
continue;
}
+ (*r)->region()->clear_changes ();
(*r)->region()->trim_front( (position - pull_back_frames));
+
+ last_region->clear_changes ();
last_region->trim_end( (position - pull_back_frames + crossfade_len));
- last_region = (*r)->region();
+ _session->add_command (new StatefulDiffCommand ((*r)->region()));
+ _session->add_command (new StatefulDiffCommand (last_region));
+ last_region = (*r)->region();
idx++;
}
(*i)->thaw();
used_playlists.pop_front();
}
-
+
commit_reversible_command ();
}
if (!_session) {
return;
}
-
- framepos_t pos = playhead_cursor->current_frame ();
- if (pos < max_framepos - 1) {
- pos += 2;
- snap_to_internal (pos, RoundUpAlways, false);
- _session->request_locate (pos);
+
+ MusicFrame pos (playhead_cursor->current_frame (), 0);
+
+ if (pos.frame < max_framepos - 1) {
+ pos.frame += 2;
+ snap_to_internal (pos, RoundUpAlways, false, true);
+ _session->request_locate (pos.frame);
}
}
if (!_session) {
return;
}
-
- framepos_t pos = playhead_cursor->current_frame ();
- if (pos > 2) {
- pos -= 2;
- snap_to_internal (pos, RoundDownAlways, false);
- _session->request_locate (pos);
+
+ MusicFrame pos (playhead_cursor->current_frame (), 0);
+
+ if (pos.frame > 2) {
+ pos.frame -= 2;
+ snap_to_internal (pos, RoundDownAlways, false, true);
+ _session->request_locate (pos.frame);
}
}
bool
Editor::idle_remove_tracks ()
{
+ Session::StateProtector sp (_session);
_remove_tracks ();
return false; /* do not call again */
}
trackstr = P_("track", "tracks", ntracks);
busstr = P_("bus", "busses", nbusses);
-
+
if (ntracks) {
if (nbusses) {
prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
return;
}
+ if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
+ /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
+ * route). If the deleted route is currently displayed in the Editor-Mixer (highly
+ * likely because deletion requires selection) this will call
+ * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
+ * It's likewise likely that the route that has just been displayed in the
+ * Editor-Mixer will be next in line for deletion.
+ *
+ * So simply switch to the master-bus (if present)
+ */
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ if ((*i)->stripable ()->is_master ()) {
+ set_selected_mixer_strip (*(*i));
+ break;
+ }
+ }
+ }
+
{
- Session::StateProtector sp (_session);
+ PresentationInfo::ChangeSuspender cs;
DisplaySuspender ds;
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
- _session->remove_route (*x);
+ rl->push_back (*x);
}
+ _session->remove_routes (rl);
}
+ /* TrackSelection and RouteList leave scope,
+ * destructors are called,
+ * diskstream drops references, save_state is called (again for every track)
+ */
}
void
return;
}
- InsertTimeOption opt = d.intersected_region_action ();
-
insert_time (
- get_preferred_edit_position(),
+ d.position(),
d.distance(),
- opt,
+ d.intersected_region_action (),
d.all_playlists(),
d.move_glued(),
d.move_markers(),
bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
)
{
- bool commit = false;
if (Config->get_edit_mode() == Lock) {
return;
}
-
- begin_reversible_command (_("insert time"));
+ bool in_command = false;
TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
if (all_playlists) {
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
- if (rtav) {
+ if (rtav && rtav->track ()) {
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);
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 (!in_command) {
+ begin_reversible_command (_("insert time"));
+ in_command = true;
+ }
+
if (opt == SplitIntersected) {
- (*i)->split (pos);
+ /* non musical split */
+ (*i)->split (MusicFrame (pos, 0));
}
(*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
_session->add_commands (cmds);
_session->add_command (new StatefulDiffCommand (*i));
- commit = true;
}
/* automation */
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
if (rtav) {
+ if (!in_command) {
+ begin_reversible_command (_("insert time"));
+ in_command = true;
+ }
rtav->route ()->shift (pos, frames);
- commit = true;
}
}
/* markers */
if (markers_too) {
bool moved = false;
+ const int32_t divisions = get_grid_music_divisions (0);
XMLNode& before (_session->locations()->get_state());
Locations::LocationList copy (_session->locations()->list());
if ((*i)->start() >= pos) {
// move end first, in case we're moving by more than the length of the range
if (!(*i)->is_mark()) {
- (*i)->set_end ((*i)->end() + frames);
+ (*i)->set_end ((*i)->end() + frames, false, true, divisions);
}
- (*i)->set_start ((*i)->start() + frames);
+ (*i)->set_start ((*i)->start() + frames, false, true, divisions);
moved = true;
}
}
if (moved) {
+ if (!in_command) {
+ begin_reversible_command (_("insert time"));
+ in_command = true;
+ }
XMLNode& after (_session->locations()->get_state());
_session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
}
}
if (tempo_too) {
+ if (!in_command) {
+ begin_reversible_command (_("insert time"));
+ in_command = true;
+ }
+ XMLNode& before (_session->tempo_map().get_state());
_session->tempo_map().insert_time (pos, frames);
+ XMLNode& after (_session->tempo_map().get_state());
+ _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
}
- if (commit) {
+ if (in_command) {
commit_reversible_command ();
}
}
return;
}
- framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
InsertRemoveTimeDialog d (*this, true);
int response = d.run ();
if (response != RESPONSE_OK) {
return;
}
-
+
framecnt_t distance = d.distance();
if (distance == 0) {
}
remove_time (
- pos,
+ d.position(),
distance,
SplitIntersected,
d.move_glued(),
}
void
-Editor::remove_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::remove_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)
{
- bool commit = false;
-
if (Config->get_edit_mode() == Lock) {
error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
return;
}
-
- begin_reversible_command (_("cut time"));
+ bool in_command = false;
for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
/* regions */
boost::shared_ptr<Playlist> pl = (*x)->playlist();
-
+
if (pl) {
XMLNode &before = pl->get_state();
-
+
+ if (!in_command) {
+ begin_reversible_command (_("remove time"));
+ in_command = true;
+ }
+
std::list<AudioRange> rl;
AudioRange ar(pos, pos+frames, 0);
rl.push_back(ar);
pl->cut (rl);
pl->shift (pos, -frames, true, ignore_music_glue);
-
+
XMLNode &after = pl->get_state();
-
+
_session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
- commit = true;
}
-
+
/* automation */
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
if (rtav) {
+ if (!in_command) {
+ begin_reversible_command (_("remove time"));
+ in_command = true;
+ }
rtav->route ()->shift (pos, -frames);
- commit = true;
}
}
+ const int32_t divisions = get_grid_music_divisions (0);
std::list<Location*> loc_kill_list;
-
+
/* markers */
if (markers_too) {
bool moved = false;
// if we're removing more time than the length of the range
if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
// start is within cut
- (*i)->set_start (pos); // bring the start marker to the beginning of the cut
+ (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
moved = true;
} else if ((*i)->start() >= pos+frames) {
// start (and thus entire range) lies beyond end of cut
- (*i)->set_start ((*i)->start() - frames); // slip the start marker back
+ (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
moved = true;
}
if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
// end is inside cut
- (*i)->set_end (pos); // bring the end to the cut
+ (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
moved = true;
} else if ((*i)->end() >= pos+frames) {
// end is beyond end of cut
- (*i)->set_end ((*i)->end() - frames); // slip the end marker back
+ (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
moved = true;
}
loc_kill_list.push_back(*i);
moved = true;
} else if ((*i)->start() >= pos) {
- (*i)->set_start ((*i)->start() -frames);
+ (*i)->set_start ((*i)->start() -frames, false, true, divisions);
moved = true;
}
for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
_session->locations()->remove( *i );
}
-
+
if (moved) {
+ if (!in_command) {
+ begin_reversible_command (_("remove time"));
+ in_command = true;
+ }
XMLNode& after (_session->locations()->get_state());
_session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
- commit = true;
}
}
-
+
if (tempo_too) {
XMLNode& before (_session->tempo_map().get_state());
- if (_session->tempo_map().cut_time (pos, frames) ) {
+ if (_session->tempo_map().remove_time (pos, frames) ) {
+ if (!in_command) {
+ begin_reversible_command (_("remove time"));
+ in_command = true;
+ }
XMLNode& after (_session->tempo_map().get_state());
_session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
- commit = true;
}
}
-
- if (commit) {
+
+ if (in_command) {
commit_reversible_command ();
}
}
void
Editor::fit_selection ()
{
- if (!selection->tracks.empty()) {
- fit_tracks (selection->tracks);
- } else {
- TrackViewList tvl;
-
- /* no selected tracks - use tracks with selected regions */
+ if (!selection->tracks.empty()) {
+ fit_tracks (selection->tracks);
+ } else {
+ TrackViewList tvl;
- if (!selection->regions.empty()) {
- for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
- tvl.push_back (&(*r)->get_time_axis_view ());
- }
+ /* no selected tracks - use tracks with selected regions */
- if (!tvl.empty()) {
- fit_tracks (tvl);
- }
- } else if (internal_editing()) {
- /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
- the entered track
- */
- if (entered_track) {
- tvl.push_back (entered_track);
- fit_tracks (tvl);
- }
- }
- }
+ if (!selection->regions.empty()) {
+ 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);
+ }
+ } else if (internal_editing()) {
+ /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
+ * the entered track
+ */
+ if (entered_track) {
+ tvl.push_back (entered_track);
+ fit_tracks (tvl);
+ }
+ }
+ }
}
void
}
/* compute the per-track height from:
-
- total canvas visible height -
- height that will be taken by visible children of selected
- tracks - height of the ruler/hscroll area
- */
+ *
+ * total canvas visible height
+ * - height that will be taken by visible children of selected tracks
+ * - height of the ruler/hscroll area
+ */
uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
double first_y_pos = DBL_MAX;
if (h < TimeAxisView::preset_height (HeightSmall)) {
- MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
+ MessageDialog msg (_("There are too many tracks to fit in the current window"));
/* too small to be displayed */
return;
}
Editor::start_visual_state_op (uint32_t n)
{
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);
void
Editor::cancel_visual_state_op (uint32_t n)
{
- goto_visual_state (n);
+ goto_visual_state (n);
}
void
onoff = !mt->input_active();
}
}
-
+
_session->set_exclusive_input_active (rl, onoff, flip_others);
}
+static bool ok_fine (GdkEventAny*) { return true; }
+
void
Editor::lock ()
{
Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
lock_dialog->get_vbox()->pack_start (*padlock);
+ lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
ArdourButton* b = manage (new ArdourButton);
b->set_name ("lock button");
b->set_text (_("Click to unlock"));
b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
lock_dialog->get_vbox()->pack_start (*b);
-
+
lock_dialog->get_vbox()->show_all ();
lock_dialog->set_size_request (200, 200);
}
-
-#ifdef __APPLE__
- /* The global menu bar continues to be accessible to applications
- with modal dialogs, which means that we need to desensitize
- all items in the menu bar. Since those items are really just
- proxies for actions, that means disabling all actions.
- */
- ActionManager::disable_all_actions ();
-#endif
+
+ delete _main_menu_disabler;
+ _main_menu_disabler = new MainMenuDisabler;
+
lock_dialog->present ();
+
+ lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
}
void
Editor::unlock ()
{
lock_dialog->hide ();
-
-#ifdef __APPLE__
- ActionManager::pop_action_state ();
-#endif
- if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+ delete _main_menu_disabler;
+ _main_menu_disabler = 0;
+
+ if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
start_lock_event_timing ();
}
}
void
Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
{
+ Timers::TimerSuspender t;
label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
- Gtkmm2ext::UI::instance()->flush_pending ();
+ Gtkmm2ext::UI::instance()->flush_pending (1);
}
void
ArdourDialog w (_("Moving embedded files into session folder"));
w.get_vbox()->pack_start (msg);
w.present ();
-
+
/* flush all pending GUI events because we're about to start copying
* files
*/
-
- Gtkmm2ext::UI::instance()->flush_pending ();
+
+ Timers::TimerSuspender t;
+ Gtkmm2ext::UI::instance()->flush_pending (3);
cerr << " Do it\n";