First cut of some Pro-tools inspired editing features; linked play/play range

and linked object/range modes.


git-svn-id: svn://localhost/ardour2/branches/3.0@6431 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2010-01-01 22:11:15 +00:00
parent 6572f421a4
commit f5acf93672
18 changed files with 582 additions and 186 deletions

View file

@ -1558,9 +1558,14 @@ ARDOUR_UI::transport_roll ()
if (_session->get_play_loop()) { if (_session->get_play_loop()) {
_session->request_play_loop (false, true); _session->request_play_loop (false, true);
} else if (_session->get_play_range ()) { } else if (_session->get_play_range () && !join_play_range_button.get_active()) {
_session->request_play_range (false, true); /* stop playing a range if we currently are */
} _session->request_play_range (0, true);
}
if (join_play_range_button.get_active()) {
_session->request_play_range (&editor->get_selection().time, true);
}
if (!rolling) { if (!rolling) {
_session->request_transport_speed (1.0f); _session->request_transport_speed (1.0f);
@ -1619,6 +1624,10 @@ ARDOUR_UI::toggle_roll (bool with_abort, bool roll_out_of_bounded_mode)
if (rolling) { if (rolling) {
_session->request_stop (with_abort, true); _session->request_stop (with_abort, true);
} else { } else {
if (join_play_range_button.get_active()) {
_session->request_play_range (&editor->get_selection().time, true);
}
_session->request_transport_speed (1.0f); _session->request_transport_speed (1.0f);
} }
} }
@ -1761,12 +1770,14 @@ ARDOUR_UI::map_transport_state ()
if (sp != 0.0) { if (sp != 0.0) {
/* we're rolling */
if (_session->get_play_range()) { if (_session->get_play_range()) {
play_selection_button.set_visual_state (1); play_selection_button.set_visual_state (1);
roll_button.set_visual_state (0); roll_button.set_visual_state (0);
auto_loop_button.set_visual_state (0); auto_loop_button.set_visual_state (0);
} else if (_session->get_play_loop ()) { } else if (_session->get_play_loop ()) {
auto_loop_button.set_visual_state (1); auto_loop_button.set_visual_state (1);
@ -1780,6 +1791,12 @@ ARDOUR_UI::map_transport_state ()
auto_loop_button.set_visual_state (0); auto_loop_button.set_visual_state (0);
} }
if (join_play_range_button.get_active()) {
/* light up both roll and play-selection if they are joined */
roll_button.set_visual_state (1);
play_selection_button.set_visual_state (1);
}
stop_button.set_visual_state (0); stop_button.set_visual_state (0);
} else { } else {

View file

@ -328,6 +328,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
Gtkmm2ext::TearOff* transport_tearoff; Gtkmm2ext::TearOff* transport_tearoff;
Gtk::Frame transport_frame; Gtk::Frame transport_frame;
Gtk::HBox transport_tearoff_hbox; Gtk::HBox transport_tearoff_hbox;
Gtk::HBox play_range_hbox;
Gtk::VBox play_range_vbox;
Gtk::HBox transport_hbox; Gtk::HBox transport_hbox;
Gtk::Fixed transport_base; Gtk::Fixed transport_base;
Gtk::Fixed transport_button_base; Gtk::Fixed transport_button_base;
@ -385,6 +387,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
BindableButton auto_loop_button; BindableButton auto_loop_button;
BindableButton play_selection_button; BindableButton play_selection_button;
BindableButton rec_button; BindableButton rec_button;
Gtk::ToggleButton join_play_range_button;
void toggle_external_sync (); void toggle_external_sync ();
void toggle_time_master (); void toggle_time_master ();

View file

@ -178,6 +178,7 @@ ARDOUR_UI::setup_transport ()
play_selection_button.set_name ("TransportButton"); play_selection_button.set_name ("TransportButton");
rec_button.set_name ("TransportRecButton"); rec_button.set_name ("TransportRecButton");
auto_loop_button.set_name ("TransportButton"); auto_loop_button.set_name ("TransportButton");
join_play_range_button.set_name ("TransportButton");
auto_return_button.set_name ("TransportButton"); auto_return_button.set_name ("TransportButton");
auto_play_button.set_name ("TransportButton"); auto_play_button.set_name ("TransportButton");
@ -221,6 +222,9 @@ ARDOUR_UI::setup_transport ()
w = manage (new Image (get_icon (X_("transport_loop")))); w = manage (new Image (get_icon (X_("transport_loop"))));
w->show(); w->show();
auto_loop_button.add (*w); auto_loop_button.add (*w);
w = manage (new Image (get_icon (X_("join_tools"))));
w->show ();
join_play_range_button.add (*w);
RefPtr<Action> act; RefPtr<Action> act;
@ -361,11 +365,17 @@ ARDOUR_UI::setup_transport ()
transport_tearoff_hbox.pack_start (*svbox, false, false, 3); transport_tearoff_hbox.pack_start (*svbox, false, false, 3);
transport_tearoff_hbox.pack_start (auto_loop_button, false, false); if (Profile->get_sae()) {
if (!Profile->get_sae()) { transport_tearoff_hbox.pack_start (auto_loop_button);
transport_tearoff_hbox.pack_start (play_selection_button, false, false); transport_tearoff_hbox.pack_start (roll_button);
} else {
transport_tearoff_hbox.pack_start (auto_loop_button, false, false);
play_range_hbox.pack_start (play_selection_button, false, false);
play_range_hbox.pack_start (roll_button, false, false);
play_range_vbox.pack_start (play_range_hbox, false, false);
play_range_vbox.pack_start (join_play_range_button, false, false);
transport_tearoff_hbox.pack_start (play_range_vbox, false, false);
} }
transport_tearoff_hbox.pack_start (roll_button, false, false);
transport_tearoff_hbox.pack_start (stop_button, false, false); transport_tearoff_hbox.pack_start (stop_button, false, false);
transport_tearoff_hbox.pack_start (rec_button, false, false, 6); transport_tearoff_hbox.pack_start (rec_button, false, false, 6);

View file

@ -239,7 +239,7 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y)
void void
AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool with_push) AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool keep_x, bool with_push)
{ {
double delta = 0.0; double delta = 0.0;
uint32_t last_movable = UINT_MAX; uint32_t last_movable = UINT_MAX;
@ -257,7 +257,7 @@ AutomationLine::modify_view_point (ControlPoint& cp, double x, double y, bool wi
y = min (1.0, y); y = min (1.0, y);
y = _height - (y * _height); y = _height - (y * _height);
if (cp.can_slide()) { if (cp.can_slide() && !keep_x) {
/* x-coord cannot move beyond adjacent points or the start/end, and is /* x-coord cannot move beyond adjacent points or the start/end, and is
already in frames. it needs to be converted to canvas units. already in frames. it needs to be converted to canvas units.
@ -362,15 +362,12 @@ AutomationLine::reset_line_coords (ControlPoint& cp)
} }
void void
AutomationLine::sync_model_with_view_line (uint32_t start, uint32_t end) AutomationLine::sync_model_with_view_points (list<ControlPoint*> cp, bool did_push, int64_t distance)
{ {
ControlPoint *p;
update_pending = true; update_pending = true;
for (uint32_t i = start; i <= end; ++i) { for (list<ControlPoint*>::iterator i = cp.begin(); i != cp.end(); ++i) {
p = nth(i); sync_model_with_view_point (**i, did_push, distance);
sync_model_with_view_point (*p, false, 0);
} }
} }
@ -488,6 +485,14 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
double tx = points[pi].x; double tx = points[pi].x;
double ty = points[pi].y; double ty = points[pi].y;
if (find (_always_in_view.begin(), _always_in_view.end(), (*model)->when) != _always_in_view.end()) {
add_visible_control_point (view_index, pi, tx, ty, model, npoints);
prev_rx = this_rx;
prev_ry = this_ry;
++view_index;
continue;
}
if (isnan (tx) || isnan (ty)) { if (isnan (tx) || isnan (ty)) {
warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""), warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
_name) << endmsg; _name) << endmsg;
@ -667,66 +672,36 @@ AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
p[index].y = DBL_MAX; p[index].y = DBL_MAX;
} }
/** Start dragging a single point.
* @param cp Point to drag.
* @param x Initial x position (frames).
* @param fraction Initial y position (as a fraction of the track height, where 0 is the bottom and 1 the top)
*/
void void
AutomationLine::start_drag (ControlPoint* cp, nframes_t x, float fraction) AutomationLine::start_drag_single (ControlPoint* cp, nframes_t x, float fraction)
{ {
if (trackview.editor().session() == 0) { /* how? */ trackview.editor().session()->begin_reversible_command (_("automation event move"));
return;
}
string str;
if (cp) {
str = _("automation event move");
} else {
str = _("automation range drag");
}
trackview.editor().session()->begin_reversible_command (str);
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0)); trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
drag_x = x; _drag_points.clear ();
drag_distance = 0; _drag_points.push_back (cp);
first_drag_fraction = fraction; start_drag_common (x, fraction);
last_drag_fraction = fraction;
drags = 0;
did_push = false;
} }
/** Start dragging a line vertically (with no change in x)
* @param i1 Control point index of the `left' point on the line.
* @param i2 Control point index of the `right' point on the line.
* @param fraction Initial y position (as a fraction of the track height, where 0 is the bottom and 1 the top)
*/
void void
AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push) AutomationLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
{ {
if (x > drag_x) { trackview.editor().session()->begin_reversible_command (_("automation range move"));
drag_distance += (x - drag_x); trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
} else {
drag_distance -= (drag_x - x);
}
drag_x = x; _drag_points.clear ();
modify_view_point (cp, x, fraction, with_push); // check if one of the control points on the line is in a selected range
if (line_points.size() > 1) {
line->property_points() = line_points;
}
drags++;
did_push = with_push;
}
void
AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
{
double ydelta = fraction - last_drag_fraction;
did_push = with_push;
last_drag_fraction = fraction;
line_drag_cp1 = i1;
line_drag_cp2 = i2;
//check if one of the control points on the line is in a selected range
bool range_found = false; bool range_found = false;
ControlPoint *cp; ControlPoint *cp;
@ -740,38 +715,92 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p
if (range_found) { if (range_found) {
for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) { for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
if ((*i)->selected()) { if ((*i)->selected()) {
modify_view_point (*(*i), trackview.editor().unit_to_frame ((*i)->get_x()), ((_height - (*i)->get_y()) /_height) + ydelta, with_push); _drag_points.push_back (*i);
} }
} }
} else { } else {
ControlPoint *cp;
for (uint32_t i = i1 ; i <= i2; i++) { for (uint32_t i = i1 ; i <= i2; i++) {
cp = nth (i); _drag_points.push_back (nth (i));
modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
} }
} }
start_drag_common (0, fraction);
}
/** Start dragging multiple points (with no change in x)
* @param cp Points to drag.
* @param fraction Initial y position (as a fraction of the track height, where 0 is the bottom and 1 the top)
*/
void
AutomationLine::start_drag_multiple (list<ControlPoint*> cp, float fraction, XMLNode* state)
{
trackview.editor().session()->begin_reversible_command (_("automation range move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), state, 0));
_drag_points = cp;
start_drag_common (0, fraction);
}
/** Common parts of starting a drag.
* @param d Description of the drag.
* @param x Starting x position in frames, or 0 if x is being ignored.
* @param fraction Starting y position (as a fraction of the track height, where 0 is the bottom and 1 the top)
*/
void
AutomationLine::start_drag_common (nframes_t x, float fraction)
{
drag_x = x;
drag_distance = 0;
_last_drag_fraction = fraction;
_drag_had_movement = false;
did_push = false;
}
/** Should be called to indicate motion during a drag.
* @param x New x position of the drag in frames, or 0 if x is being ignored.
* @param fraction New y fraction.
*/
void
AutomationLine::drag_motion (nframes_t x, float fraction, bool with_push)
{
int64_t const dx = x - drag_x;
drag_distance += dx;
drag_x = x;
double const dy = fraction - _last_drag_fraction;
_last_drag_fraction = fraction;
for (list<ControlPoint*>::iterator i = _drag_points.begin(); i != _drag_points.end(); ++i) {
modify_view_point (
**i,
trackview.editor().unit_to_frame ((*i)->get_x()) + dx,
((_height - (*i)->get_y()) / _height) + dy,
(x == 0),
with_push
);
}
if (line_points.size() > 1) { if (line_points.size() > 1) {
line->property_points() = line_points; line->property_points() = line_points;
} }
drags++; _drag_had_movement = true;
did_push = with_push;
} }
/** Should be called to indicate the end of a drag */
void void
AutomationLine::end_drag (ControlPoint* cp) AutomationLine::end_drag ()
{ {
if (!drags) { if (!_drag_had_movement) {
return; return;
} }
alist->freeze (); alist->freeze ();
if (cp) { sync_model_with_view_points (_drag_points, did_push, drag_distance);
sync_model_with_view_point (*cp, did_push, drag_distance);
} else {
sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
}
alist->thaw (); alist->thaw ();
@ -782,7 +811,6 @@ AutomationLine::end_drag (ControlPoint* cp)
trackview.editor().session()->set_dirty (); trackview.editor().session()->set_dirty ();
} }
void void
AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance) AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int64_t distance)
{ {
@ -849,7 +877,6 @@ AutomationLine::sync_model_with_view_point (ControlPoint& cp, bool did_push, int
alist->slide (mr.end, drag_distance); alist->slide (mr.end, drag_distance);
} }
} }
bool bool
@ -1302,7 +1329,7 @@ AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, dou
control_points[view_index]->set_can_slide(true); control_points[view_index]->set_can_slide(true);
shape = ControlPoint::Full; shape = ControlPoint::Full;
} }
control_points[view_index]->reset (tx, ty, model, view_index, shape); control_points[view_index]->reset (tx, ty, model, view_index, shape);
/* finally, control visibility */ /* finally, control visibility */
@ -1316,3 +1343,18 @@ AutomationLine::add_visible_control_point (uint32_t view_index, uint32_t pi, dou
} }
} }
} }
void
AutomationLine::add_always_in_view (double x)
{
_always_in_view.push_back (x);
alist->apply_to_points (*this, &AutomationLine::reset_callback);
}
void
AutomationLine::clear_always_in_view ()
{
_always_in_view.clear ();
alist->apply_to_points (*this, &AutomationLine::reset_callback);
}

View file

@ -74,10 +74,11 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
bool control_points_adjacent (double xval, uint32_t& before, uint32_t& after); bool control_points_adjacent (double xval, uint32_t& before, uint32_t& after);
/* dragging API */ /* dragging API */
virtual void start_drag (ControlPoint*, nframes_t x, float fraction); virtual void start_drag_single (ControlPoint*, nframes_t x, float);
virtual void point_drag(ControlPoint&, nframes_t x, float, bool with_push); virtual void start_drag_line (uint32_t, uint32_t, float);
virtual void end_drag (ControlPoint*); virtual void start_drag_multiple (std::list<ControlPoint*>, float, XMLNode *);
virtual void line_drag(uint32_t i1, uint32_t i2, float, bool with_push); virtual void drag_motion (nframes_t, float, bool);
virtual void end_drag ();
ControlPoint* nth (uint32_t); ControlPoint* nth (uint32_t);
uint32_t npoints() const { return control_points.size(); } uint32_t npoints() const { return control_points.size(); }
@ -130,6 +131,9 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
void modify_point_y (ControlPoint&, double); void modify_point_y (ControlPoint&, double);
void add_always_in_view (double);
void clear_always_in_view ();
protected: protected:
std::string _name; std::string _name;
@ -164,8 +168,9 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
static bool invalid_point (ALPoints&, uint32_t index); static bool invalid_point (ALPoints&, uint32_t index);
void determine_visible_control_points (ALPoints&); void determine_visible_control_points (ALPoints&);
void sync_model_with_view_point (ControlPoint&, bool did_push, int64_t distance); void sync_model_with_view_point (ControlPoint&, bool, int64_t);
void sync_model_with_view_line (uint32_t, uint32_t); void sync_model_with_view_points (std::list<ControlPoint*>, bool, int64_t);
void start_drag_common (nframes_t, float);
virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y); virtual void change_model (ARDOUR::AutomationList::iterator, double x, double y);
@ -177,18 +182,17 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
virtual void add_model_point (ALPoints& tmp_points, double frame, double yfract); virtual void add_model_point (ALPoints& tmp_points, double frame, double yfract);
private: private:
uint32_t drags; std::list<ControlPoint*> _drag_points; ///< points we are dragging
double first_drag_fraction; bool _drag_had_movement; ///< true if the drag has seen movement, otherwise false
double last_drag_fraction; nframes64_t drag_x; ///< last x position of the drag, in frames
uint32_t line_drag_cp1; nframes64_t drag_distance; ///< total x movement of the drag, in frames
uint32_t line_drag_cp2; double _last_drag_fraction; ///< last y position of the drag, as a fraction
int64_t drag_x; std::list<double> _always_in_view;
int64_t drag_distance;
const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter; const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter;
ARDOUR::AutomationList::InterpolationStyle _interpolation; ARDOUR::AutomationList::InterpolationStyle _interpolation;
void modify_view_point (ControlPoint&, double, double, bool with_push); void modify_view_point (ControlPoint&, double, double, bool, bool with_push);
void reset_line_coords (ControlPoint&); void reset_line_coords (ControlPoint&);
void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t); void add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t);

View file

@ -100,6 +100,10 @@ class AutomationTimeAxisView : public TimeAxisView {
boost::shared_ptr<ARDOUR::AutomationControl> control() { return _control; } boost::shared_ptr<ARDOUR::AutomationControl> control() { return _control; }
boost::shared_ptr<AutomationController> controller() { return _controller; } boost::shared_ptr<AutomationController> controller() { return _controller; }
ArdourCanvas::Item* base_item () const {
return _base_rect;
}
protected: protected:
boost::shared_ptr<ARDOUR::Route> _route; ///< Parent route boost::shared_ptr<ARDOUR::Route> _route; ///< Parent route
boost::shared_ptr<ARDOUR::AutomationControl> _control; ///< Control boost::shared_ptr<ARDOUR::AutomationControl> _control; ///< Control

View file

@ -155,15 +155,6 @@ void
ControlPoint::set_size (double sz) ControlPoint::set_size (double sz)
{ {
_size = sz; _size = sz;
#if 0
if (_size > 6.0) {
item->property_fill() = (gboolean) TRUE;
} else {
item->property_fill() = (gboolean) FALSE;
}
#endif
move_to (_x, _y, _shape); move_to (_x, _y, _shape);
} }

View file

@ -220,8 +220,10 @@ show_me_the_size (Requisition* r, const char* what)
} }
Editor::Editor () Editor::Editor ()
: _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
/* time display buttons */ /* time display buttons */
: minsec_label (_("Mins:Secs")) , minsec_label (_("Mins:Secs"))
, bbt_label (_("Bars:Beats")) , bbt_label (_("Bars:Beats"))
, timecode_label (_("Timecode")) , timecode_label (_("Timecode"))
, frame_label (_("Samples")) , frame_label (_("Samples"))
@ -2314,6 +2316,10 @@ Editor::set_state (const XMLNode& node, int /*version*/)
} }
} }
if ((prop = node.property ("join-object-range"))) {
join_object_range_button.set_active (string_is_affirmative (prop->value ()));
}
if ((prop = node.property ("edit-point"))) { if ((prop = node.property ("edit-point"))) {
set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true); set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
} }
@ -2461,6 +2467,7 @@ Editor::get_state ()
node->add_property ("region-list-sort-type", enum2str (_regions->sort_type ())); node->add_property ("region-list-sort-type", enum2str (_regions->sort_type ()));
node->add_property ("mouse-mode", enum2str(mouse_mode)); node->add_property ("mouse-mode", enum2str(mouse_mode));
node->add_property ("internal-edit", _internal_editing ? "yes" : "no"); node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
if (act) { if (act) {
@ -2752,24 +2759,45 @@ Editor::setup_toolbar ()
mouse_timefx_button.set_relief(Gtk::RELIEF_NONE); mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
mouse_audition_button.set_relief(Gtk::RELIEF_NONE); mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
// internal_edit_button.set_relief(Gtk::RELIEF_NONE); // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
join_object_range_button.set_relief(Gtk::RELIEF_NONE);
HBox* mode_box = manage(new HBox); HBox* mode_box = manage(new HBox);
mode_box->set_border_width (2); mode_box->set_border_width (2);
mode_box->set_spacing(4); mode_box->set_spacing(4);
mouse_mode_button_box.set_spacing(1);
mouse_mode_button_box.pack_start(mouse_move_button, true, true);
if (!Profile->get_sae()) {
mouse_mode_button_box.pack_start(mouse_select_button, true, true);
}
mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
if (!Profile->get_sae()) {
mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
}
mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
mouse_mode_button_box.pack_start(internal_edit_button, true, true);
mouse_mode_button_box.set_homogeneous(true);
/* table containing mode buttons */
Table* mouse_mode_button_table = manage (new Table (Profile->get_sae() ? 4 : 6, 2));
int c = 0;
if (Profile->get_sae()) {
mouse_mode_button_table->attach (mouse_move_button, c, c + 1, 0, 1);
++c;
} else {
mouse_mode_button_table->attach (mouse_move_button, c, c + 1, 0, 1);
mouse_mode_button_table->attach (mouse_select_button, c + 1, c + 2, 0, 1);
mouse_mode_button_table->attach (join_object_range_button, c, c + 2, 1, 2);
c += 2;
}
mouse_mode_button_table->attach (mouse_zoom_button, c, c + 1, 0, 1);
++c;
if (!Profile->get_sae()) {
mouse_mode_button_table->attach (mouse_gain_button, c, c + 1, 0, 1);
++c;
}
mouse_mode_button_table->attach (mouse_timefx_button, c, c + 1, 0, 1);
++c;
mouse_mode_button_table->attach (mouse_audition_button, c, c + 1, 0, 1);
++c;
mouse_mode_button_table->attach (internal_edit_button, c, c + 1, 0, 1);
++c;
vector<string> edit_mode_strings; vector<string> edit_mode_strings;
edit_mode_strings.push_back (edit_mode_to_string (Slide)); edit_mode_strings.push_back (edit_mode_to_string (Slide));
if (!Profile->get_sae()) { if (!Profile->get_sae()) {
@ -2781,8 +2809,8 @@ Editor::setup_toolbar ()
set_popdown_strings (edit_mode_selector, edit_mode_strings, true); set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done)); edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
mode_box->pack_start(edit_mode_selector); mode_box->pack_start (edit_mode_selector);
mode_box->pack_start(mouse_mode_button_box); mode_box->pack_start (*mouse_mode_button_table);
mouse_mode_tearoff = manage (new TearOff (*mode_box)); mouse_mode_tearoff = manage (new TearOff (*mode_box));
mouse_mode_tearoff->set_name ("MouseModeBase"); mouse_mode_tearoff->set_name ("MouseModeBase");
@ -2807,6 +2835,7 @@ Editor::setup_toolbar ()
mouse_zoom_button.set_mode (false); mouse_zoom_button.set_mode (false);
mouse_timefx_button.set_mode (false); mouse_timefx_button.set_mode (false);
mouse_audition_button.set_mode (false); mouse_audition_button.set_mode (false);
join_object_range_button.set_mode (false);
mouse_move_button.set_name ("MouseModeButton"); mouse_move_button.set_name ("MouseModeButton");
mouse_select_button.set_name ("MouseModeButton"); mouse_select_button.set_name ("MouseModeButton");
@ -2814,8 +2843,8 @@ Editor::setup_toolbar ()
mouse_zoom_button.set_name ("MouseModeButton"); mouse_zoom_button.set_name ("MouseModeButton");
mouse_timefx_button.set_name ("MouseModeButton"); mouse_timefx_button.set_name ("MouseModeButton");
mouse_audition_button.set_name ("MouseModeButton"); mouse_audition_button.set_name ("MouseModeButton");
internal_edit_button.set_name ("MouseModeButton"); internal_edit_button.set_name ("MouseModeButton");
join_object_range_button.set_name ("MouseModeButton");
mouse_move_button.unset_flags (CAN_FOCUS); mouse_move_button.unset_flags (CAN_FOCUS);
mouse_select_button.unset_flags (CAN_FOCUS); mouse_select_button.unset_flags (CAN_FOCUS);
@ -2824,6 +2853,7 @@ Editor::setup_toolbar ()
mouse_timefx_button.unset_flags (CAN_FOCUS); mouse_timefx_button.unset_flags (CAN_FOCUS);
mouse_audition_button.unset_flags (CAN_FOCUS); mouse_audition_button.unset_flags (CAN_FOCUS);
internal_edit_button.unset_flags (CAN_FOCUS); internal_edit_button.unset_flags (CAN_FOCUS);
join_object_range_button.unset_flags (CAN_FOCUS);
/* Zoom */ /* Zoom */

View file

@ -487,6 +487,19 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
Editing::MouseMode mouse_mode; Editing::MouseMode mouse_mode;
bool _internal_editing; bool _internal_editing;
Editing::MouseMode effective_mouse_mode () const;
enum JoinObjectRangeState {
JOIN_OBJECT_RANGE_NONE,
/** `join object/range' mode is active and the mouse is over a place where object mode should happen */
JOIN_OBJECT_RANGE_OBJECT,
/** `join object/range' mode is active and the mouse is over a place where range mode should happen */
JOIN_OBJECT_RANGE_RANGE
};
JoinObjectRangeState _join_object_range_state;
void update_join_object_range_location (double, double);
int post_maximal_editor_width; int post_maximal_editor_width;
int post_maximal_pane_position; int post_maximal_pane_position;
@ -594,6 +607,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void collect_new_region_view (RegionView *); void collect_new_region_view (RegionView *);
void collect_and_select_new_region_view (RegionView *); void collect_and_select_new_region_view (RegionView *);
void select_range_around_region (RegionView *);
Gtk::Menu track_context_menu; Gtk::Menu track_context_menu;
Gtk::Menu track_region_context_menu; Gtk::Menu track_region_context_menu;
Gtk::Menu track_selection_context_menu; Gtk::Menu track_selection_context_menu;
@ -1492,7 +1507,6 @@ public:
Gtk::Table toolbar_selection_clock_table; Gtk::Table toolbar_selection_clock_table;
Gtk::Label toolbar_selection_cursor_label; Gtk::Label toolbar_selection_cursor_label;
Gtk::HBox mouse_mode_button_box;
Gtkmm2ext::TearOff* mouse_mode_tearoff; Gtkmm2ext::TearOff* mouse_mode_tearoff;
Gtk::ToggleButton mouse_select_button; Gtk::ToggleButton mouse_select_button;
Gtk::ToggleButton mouse_move_button; Gtk::ToggleButton mouse_move_button;
@ -1500,6 +1514,7 @@ public:
Gtk::ToggleButton mouse_zoom_button; Gtk::ToggleButton mouse_zoom_button;
Gtk::ToggleButton mouse_timefx_button; Gtk::ToggleButton mouse_timefx_button;
Gtk::ToggleButton mouse_audition_button; Gtk::ToggleButton mouse_audition_button;
Gtk::ToggleButton join_object_range_button;
void mouse_mode_toggled (Editing::MouseMode m); void mouse_mode_toggled (Editing::MouseMode m);
bool ignore_mouse_mode_toggle; bool ignore_mouse_mode_toggle;

View file

@ -683,7 +683,11 @@ Editor::register_actions ()
mouse_select_button.set_name ("MouseModeButton"); mouse_select_button.set_name ("MouseModeButton");
mouse_select_button.get_image ()->show (); mouse_select_button.get_image ()->show ();
act = ActionManager::register_radio_action (mouse_mode_actions, mouse_mode_group, "set-mouse-mode-gain", _("Gain Tool"), sigc::bind (sigc::mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain)); join_object_range_button.set_image (*(manage (new Image (::get_icon ("join_tools")))));
join_object_range_button.set_name ("MouseModeButton");
join_object_range_button.get_image()->show ();
act = ActionManager::register_radio_action (mouse_mode_actions, mouse_mode_group, "set-mouse-mode-gain", _("Gain Tool"), sigc::bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
act->connect_proxy (mouse_gain_button); act->connect_proxy (mouse_gain_button);
mouse_gain_button.set_image (*(manage (new Image (::get_icon("tool_gain"))))); mouse_gain_button.set_image (*(manage (new Image (::get_icon("tool_gain")))));
mouse_gain_button.set_label (""); mouse_gain_button.set_label ("");

View file

@ -265,7 +265,6 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg
return false; return false;
} }
switch (event->type) { switch (event->type) {
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS: case GDK_2BUTTON_PRESS:

View file

@ -40,6 +40,7 @@
#include "canvas-note.h" #include "canvas-note.h"
#include "selection.h" #include "selection.h"
#include "midi_selection.h" #include "midi_selection.h"
#include "automation_time_axis.h"
using namespace std; using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
@ -2466,15 +2467,12 @@ ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
// the point doesn't 'jump' to the mouse after the first drag // the point doesn't 'jump' to the mouse after the first drag
_time_axis_view_grab_x = _point->get_x(); _time_axis_view_grab_x = _point->get_x();
_time_axis_view_grab_y = _point->get_y(); _time_axis_view_grab_y = _point->get_y();
nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
_point->line().parent_group().i2w (_time_axis_view_grab_x, _time_axis_view_grab_y); float const fraction = 1 - (_point->get_y() / _point->line().height());
_editor->track_canvas->w2c (_time_axis_view_grab_x, _time_axis_view_grab_y, _time_axis_view_grab_x, _time_axis_view_grab_y);
_time_axis_view_grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x); _point->line().start_drag_single (_point, grab_frame, fraction);
_point->line().start_drag (_point, _time_axis_view_grab_frame, 0);
float fraction = 1.0 - (_point->get_y() / _point->line().height());
_editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction), _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
event->button.x + 10, event->button.y + 10); event->button.x + 10, event->button.y + 10);
@ -2492,18 +2490,16 @@ ControlPointDrag::motion (GdkEvent* event, bool)
dy *= 0.1; dy *= 0.1;
} }
/* coordinate in TimeAxisView's space */
double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx; double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy; double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
// calculate zero crossing point. back off by .01 to stay on the // calculate zero crossing point. back off by .01 to stay on the
// positive side of zero // positive side of zero
double _unused = 0; double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
_point->line().parent_group().i2w(_unused, zero_gain_y);
// make sure we hit zero when passing through // make sure we hit zero when passing through
if ((cy < zero_gain_y and (cy - dy) > zero_gain_y) if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
cy = zero_gain_y; cy = zero_gain_y;
} }
@ -2517,13 +2513,10 @@ ControlPointDrag::motion (GdkEvent* event, bool)
_cumulative_x_drag = cx - _time_axis_view_grab_x; _cumulative_x_drag = cx - _time_axis_view_grab_x;
_cumulative_y_drag = cy - _time_axis_view_grab_y; _cumulative_y_drag = cy - _time_axis_view_grab_y;
_point->line().parent_group().w2i (cx, cy);
cx = max (0.0, cx); cx = max (0.0, cx);
cy = max (0.0, cy); cy = max (0.0, cy);
cy = min ((double) _point->line().height(), cy); cy = min ((double) _point->line().height(), cy);
//translate cx to frames
nframes64_t cx_frames = _editor->unit_to_frame (cx); nframes64_t cx_frames = _editor->unit_to_frame (cx);
if (!_x_constrained) { if (!_x_constrained) {
@ -2534,7 +2527,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier); bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
_point->line().point_drag (*_point, cx_frames, fraction, push); _point->line().drag_motion (cx_frames, fraction, push);
_editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction)); _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
} }
@ -2553,7 +2546,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
} else { } else {
motion (event, false); motion (event, false);
} }
_point->line().end_drag (_point); _point->line().end_drag ();
} }
bool bool
@ -2594,7 +2587,10 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit); nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
if (!_line->control_points_adjacent (frame_within_region, _before, _after)) { uint32_t before;
uint32_t after;
if (!_line->control_points_adjacent (frame_within_region, before, after)) {
/* no adjacent points */ /* no adjacent points */
return; return;
} }
@ -2608,7 +2604,7 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
double fraction = 1.0 - (cy / _line->height()); double fraction = 1.0 - (cy / _line->height());
_line->start_drag (0, grab_frame(), fraction); _line->start_drag_line (before, after, fraction);
_editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction), _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
event->button.x + 10, event->button.y + 10); event->button.x + 10, event->button.y + 10);
@ -2642,7 +2638,8 @@ LineDrag::motion (GdkEvent* event, bool)
push = true; push = true;
} }
_line->line_drag (_before, _after, fraction, push); /* we are ignoring x position for this drag, so we can just pass in 0 */
_line->drag_motion (0, fraction, push);
_editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction)); _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
} }
@ -2651,7 +2648,7 @@ void
LineDrag::finished (GdkEvent* event, bool) LineDrag::finished (GdkEvent* event, bool)
{ {
motion (event, false); motion (event, false);
_line->end_drag (0); _line->end_drag ();
} }
void void
@ -2850,6 +2847,8 @@ SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
: Drag (e, i) : Drag (e, i)
, _operation (o) , _operation (o)
, _copy (false) , _copy (false)
, _original_pointer_time_axis (-1)
, _last_pointer_time_axis (-1)
{ {
} }
@ -2909,6 +2908,8 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
} else { } else {
_editor->show_verbose_time_cursor (adjusted_current_frame (event), 10); _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
} }
_original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
} }
void void
@ -2918,13 +2919,16 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
nframes64_t end = 0; nframes64_t end = 0;
nframes64_t length; nframes64_t length;
pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
if (pending_time_axis.first == 0) {
return;
}
nframes64_t const pending_position = adjusted_current_frame (event); nframes64_t const pending_position = adjusted_current_frame (event);
/* only alter selection if the current frame is /* only alter selection if things have changed */
different from the last frame position (adjusted)
*/
if (pending_position == last_pointer_frame()) { if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
return; return;
} }
@ -2969,8 +2973,36 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
_editor->clicked_selection = _editor->selection->set (start, end); _editor->clicked_selection = _editor->selection->set (start, end);
} }
} }
break;
/* select the track that we're in */
if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
_editor->selection->add (pending_time_axis.first);
_added_time_axes.push_back (pending_time_axis.first);
}
/* deselect any tracks that this drag no longer includes, being careful to only deselect
tracks that we selected in the first place.
*/
int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
list<TimeAxisView*>::iterator i = _added_time_axes.begin();
while (i != _added_time_axes.end()) {
list<TimeAxisView*>::iterator tmp = i;
++tmp;
if ((*i)->order() < min_order || (*i)->order() > max_order) {
_editor->selection->remove (*i);
_added_time_axes.remove (*i);
}
i = tmp;
}
} }
break;
case SelectionStartTrim: case SelectionStartTrim:
@ -3490,3 +3522,96 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
region->note_dropped (cnote, drag_delta_x, drag_delta_note); region->note_dropped (cnote, drag_delta_x, drag_delta_note);
} }
} }
AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
: Drag (e, i)
, _ranges (r)
, _nothing_to_drag (false)
{
_atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
assert (_atav);
_line = _atav->line ();
}
void
AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
{
Drag::start_grab (event, cursor);
list<ControlPoint*> points;
XMLNode* state = &_line->get_state ();
if (_ranges.empty()) {
uint32_t const N = _line->npoints ();
for (uint32_t i = 0; i < N; ++i) {
points.push_back (_line->nth (i));
}
} else {
boost::shared_ptr<AutomationList> the_list = _line->the_list ();
for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
the_list->add (j->start, the_list->eval (j->start));
_line->add_always_in_view (j->start);
the_list->add (j->start + 1, the_list->eval (j->start + 1));
_line->add_always_in_view (j->start + 1);
the_list->add (j->end - 1, the_list->eval (j->end - 1));
_line->add_always_in_view (j->end - 1);
the_list->add (j->end, the_list->eval (j->end));
_line->add_always_in_view (j->end);
}
uint32_t const N = _line->npoints ();
AutomationList::const_iterator j = the_list->begin ();
for (uint32_t i = 0; i < N; ++i) {
ControlPoint* p = _line->nth (i);
list<AudioRange>::const_iterator k = _ranges.begin ();
while (k != _ranges.end() && (k->start >= (*j)->when || k->end <= (*j)->when)) {
++k;
}
if (k != _ranges.end()) {
points.push_back (p);
}
++j;
}
}
if (points.empty()) {
_nothing_to_drag = true;
return;
}
_line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
}
void
AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
{
if (_nothing_to_drag) {
return;
}
float const f = 1 - (current_pointer_y() / _line->height());
/* we are ignoring x position for this drag, so we can just pass in 0 */
_line->drag_motion (0, f, false);
}
void
AutomationRangeDrag::finished (GdkEvent* event, bool)
{
if (_nothing_to_drag) {
return;
}
motion (event, false);
_line->end_drag ();
_line->clear_always_in_view ();
}

View file

@ -474,7 +474,6 @@ private:
ControlPoint* _point; ControlPoint* _point;
double _time_axis_view_grab_x; double _time_axis_view_grab_x;
double _time_axis_view_grab_y; double _time_axis_view_grab_y;
nframes64_t _time_axis_view_grab_frame;
double _cumulative_x_drag; double _cumulative_x_drag;
double _cumulative_y_drag; double _cumulative_y_drag;
static double const _zero_gain_fraction; static double const _zero_gain_fraction;
@ -537,7 +536,7 @@ public:
void finished (GdkEvent *, bool); void finished (GdkEvent *, bool);
}; };
/** Drag in range select(gc_owner.get()) moAutomatable */ /** Drag in range select mode */
class SelectionDrag : public Drag class SelectionDrag : public Drag
{ {
public: public:
@ -557,6 +556,9 @@ public:
private: private:
Operation _operation; Operation _operation;
bool _copy; bool _copy;
int _original_pointer_time_axis;
int _last_pointer_time_axis;
std::list<TimeAxisView*> _added_time_axes;
}; };
/** Range marker drag */ /** Range marker drag */
@ -583,16 +585,33 @@ private:
bool _copy; bool _copy;
}; };
/* Drag of rectangle to set zoom */ /** Drag of rectangle to set zoom */
class MouseZoomDrag : public Drag class MouseZoomDrag : public Drag
{ {
public: public:
MouseZoomDrag (Editor *e, ArdourCanvas::Item *i) : Drag (e, i) {} MouseZoomDrag (Editor* e, ArdourCanvas::Item *i) : Drag (e, i) {}
void start_grab (GdkEvent *, Gdk::Cursor* c = 0); void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool); void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool); void finished (GdkEvent *, bool);
}; };
/** Drag of a range of automation data, changing value but not position */
class AutomationRangeDrag : public Drag
{
public:
AutomationRangeDrag (Editor *, ArdourCanvas::Item *, std::list<ARDOUR::AudioRange> const &);
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
private:
std::list<ARDOUR::AudioRange> _ranges;
AutomationTimeAxisView* _atav;
boost::shared_ptr<AutomationLine> _line;
bool _nothing_to_drag;
};
#endif /* __gtk2_ardour_editor_drag_h_ */ #endif /* __gtk2_ardour_editor_drag_h_ */

View file

@ -134,13 +134,11 @@ Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
case GDK_BUTTON_PRESS: case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS: case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS: case GDK_3BUTTON_PRESS:
*pcx = event->button.x; *pcx = event->button.x;
*pcy = event->button.y; *pcy = event->button.y;
_trackview_group->w2i(*pcx, *pcy); _trackview_group->w2i(*pcx, *pcy);
break; break;
case GDK_MOTION_NOTIFY: case GDK_MOTION_NOTIFY:
*pcx = event->motion.x; *pcx = event->motion.x;
*pcy = event->motion.y; *pcy = event->motion.y;
_trackview_group->w2i(*pcx, *pcy); _trackview_group->w2i(*pcx, *pcy);
@ -254,6 +252,17 @@ Editor::set_canvas_cursor ()
} }
} }
switch (_join_object_range_state) {
case JOIN_OBJECT_RANGE_NONE:
break;
case JOIN_OBJECT_RANGE_OBJECT:
current_canvas_cursor = which_grabber_cursor ();
break;
case JOIN_OBJECT_RANGE_RANGE:
current_canvas_cursor = selector_cursor;
break;
}
if (is_drawable()) { if (is_drawable()) {
track_canvas->get_window()->set_cursor(*current_canvas_cursor); track_canvas->get_window()->set_cursor(*current_canvas_cursor);
} }
@ -315,9 +324,9 @@ Editor::mouse_mode_toggled (MouseMode m)
instant_save (); instant_save ();
if (mouse_mode != MouseRange) { if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
/* in all modes except range, hide the range selection, /* in all modes except range and joined object/range, hide the range selection,
show the object (region) selection. show the object (region) selection.
*/ */
@ -331,7 +340,7 @@ Editor::mouse_mode_toggled (MouseMode m)
} else { } else {
/* /*
in range mode, show the range selection. in range or object/range mode, show the range selection.
*/ */
for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
@ -416,6 +425,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
*/ */
if (((mouse_mode != MouseObject) && if (((mouse_mode != MouseObject) &&
(_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
(mouse_mode != MouseAudition || item_type != RegionItem) && (mouse_mode != MouseAudition || item_type != RegionItem) &&
(mouse_mode != MouseTimeFX || item_type != RegionItem) && (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
(mouse_mode != MouseGain) && (mouse_mode != MouseGain) &&
@ -445,16 +455,21 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
switch (item_type) { switch (item_type) {
case RegionItem: case RegionItem:
if (mouse_mode != MouseRange) { if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
set_selected_regionview_from_click (press, op, true); set_selected_regionview_from_click (press, op, true);
} else if (event->type == GDK_BUTTON_PRESS) { } else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect (); selection->clear_tracks ();
set_selected_track_as_side_effect (true);
} }
if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
select_range_around_region (selection->regions.front());
}
break; break;
case RegionViewNameHighlight: case RegionViewNameHighlight:
case RegionViewName: case RegionViewName:
if (mouse_mode != MouseRange) { if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
set_selected_regionview_from_click (press, op, true); set_selected_regionview_from_click (press, op, true);
} else if (event->type == GDK_BUTTON_PRESS) { } else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect (); set_selected_track_as_side_effect ();
@ -466,7 +481,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case FadeInItem: case FadeInItem:
case FadeOutHandleItem: case FadeOutHandleItem:
case FadeOutItem: case FadeOutItem:
if (mouse_mode != MouseRange) { if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
set_selected_regionview_from_click (press, op, true); set_selected_regionview_from_click (press, op, true);
} else if (event->type == GDK_BUTTON_PRESS) { } else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect (); set_selected_track_as_side_effect ();
@ -475,7 +490,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case ControlPointItem: case ControlPointItem:
set_selected_track_as_side_effect (); set_selected_track_as_side_effect ();
if (mouse_mode != MouseRange) { if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
set_selected_control_point_from_click (op, false); set_selected_control_point_from_click (op, false);
} }
break; break;
@ -597,7 +612,9 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break; break;
} }
switch (mouse_mode) { Editing::MouseMode eff = effective_mouse_mode ();
switch (eff) {
case MouseRange: case MouseRange:
switch (item_type) { switch (item_type) {
case StartSelectionTrimItem: case StartSelectionTrimItem:
@ -623,11 +640,21 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
_drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove); _drag = new SelectionDrag (this, item, SelectionDrag::SelectionMove);
_drag->start_grab (event); _drag->start_grab (event);
} else { } else {
/* this was debated, but decided the more common action was to double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
make a new selection */ pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
assert (_drag == 0); if (tvp.first) {
_drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection); AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
_drag->start_grab (event); assert (_drag == 0);
if (join_object_range_button.get_active() && atv) {
/* smart "join" mode: drag automation */
_drag = new AutomationRangeDrag (this, atv->base_item(), selection->time);
} else {
/* this was debated, but decided the more common action was to
make a new selection */
_drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
}
_drag->start_grab (event);
}
} }
break; break;
@ -734,14 +761,46 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
_drag = new RegionCreateDrag (this, item, clicked_axisview); _drag = new RegionCreateDrag (this, item, clicked_axisview);
_drag->start_grab (event); _drag->start_grab (event);
return true; return true;
} else {
assert (_drag == 0);
_drag = new RubberbandSelectDrag (this, item);
_drag->start_grab (event);
} }
/* fallthru */ break;
case AutomationTrackItem: case AutomationTrackItem:
assert (_drag == 0); assert (_drag == 0);
_drag = new RubberbandSelectDrag (this, item);
if (join_object_range_button.get_active()) {
/* smart "join" mode: drag automation */
_drag = new AutomationRangeDrag (this, item, selection->time);
} else {
/* otherwise: rubberband drag to select automation points */
_drag = new RubberbandSelectDrag (this, item);
}
_drag->start_grab (event); _drag->start_grab (event);
break; break;
case SelectionItem:
{
if (join_object_range_button.get_active()) {
/* we're in "smart" joined mode, and we've clicked on a Selection; if we're
* over an automation track, start a drag of its data */
double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
if (tvp.first) {
AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
if (atv) {
assert (_drag == 0);
_drag = new AutomationRangeDrag (this, atv->base_item(), selection->time);
_drag->start_grab (event);
}
}
}
break;
}
#ifdef WITH_CMT #ifdef WITH_CMT
case ImageFrameHandleStartItem: case ImageFrameHandleStartItem:
imageframe_start_handle_op(item, event) ; imageframe_start_handle_op(item, event) ;
@ -878,7 +937,8 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
bool bool
Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type) Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
switch (mouse_mode) { Editing::MouseMode const eff = effective_mouse_mode ();
switch (eff) {
case MouseObject: case MouseObject:
switch (item_type) { switch (item_type) {
case RegionItem: case RegionItem:
@ -1152,6 +1212,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
/* delete events get handled here */ /* delete events get handled here */
Editing::MouseMode const eff = effective_mouse_mode ();
if (_drag == 0 && Keyboard::is_delete_event (&event->button)) { if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
switch (item_type) { switch (item_type) {
@ -1168,13 +1230,13 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break; break;
case RegionItem: case RegionItem:
if (mouse_mode == MouseObject) { if (eff == MouseObject) {
remove_clicked_region (); remove_clicked_region ();
} }
break; break;
case ControlPointItem: case ControlPointItem:
if (mouse_mode == MouseGain) { if (eff == MouseGain) {
remove_gain_control_point (item, event); remove_gain_control_point (item, event);
} else { } else {
remove_control_point (item, event); remove_control_point (item, event);
@ -1233,7 +1295,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break; break;
} }
switch (mouse_mode) { switch (eff) {
case MouseObject: case MouseObject:
switch (item_type) { switch (item_type) {
case AutomationTrackItem: case AutomationTrackItem:
@ -1305,7 +1367,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
case 2: case 2:
switch (mouse_mode) { switch (eff) {
case MouseObject: case MouseObject:
switch (item_type) { switch (item_type) {
@ -1348,7 +1410,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
} }
bool bool
Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type) Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
ControlPoint* cp; ControlPoint* cp;
Marker * marker; Marker * marker;
@ -1544,7 +1606,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType i
} }
bool bool
Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type) Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{ {
AutomationLine* al; AutomationLine* al;
ControlPoint* cp; ControlPoint* cp;
@ -1661,6 +1723,10 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType i
break; break;
} }
if (item_type == RegionItem) {
update_join_object_range_location (event->crossing.x, event->crossing.y);
}
return false; return false;
} }
@ -1782,6 +1848,12 @@ Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from
return true; return true;
} }
JoinObjectRangeState const old = _join_object_range_state;
update_join_object_range_location (event->motion.x, event->motion.y);
if (_join_object_range_state != old) {
set_canvas_cursor ();
}
bool handled = false; bool handled = false;
if (_drag) { if (_drag) {
handled = _drag->motion_handler (event, from_autoscroll); handled = _drag->motion_handler (event, from_autoscroll);
@ -2598,3 +2670,52 @@ Editor::set_internal_edit (bool yn)
} }
/** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
* used by the `join object/range' tool mode.
*/
void
Editor::update_join_object_range_location (double x, double y)
{
if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
_join_object_range_state = JOIN_OBJECT_RANGE_NONE;
return;
}
if (entered_regionview) {
double cx = x;
double cy = y;
entered_regionview->get_canvas_group()->w2i (cx, cy);
double x1;
double y1;
double x2;
double y2;
entered_regionview->get_canvas_group()->get_bounds (x1, y1, x2, y2);
bool const top_half = cy < (y2 - y1) / 2;
_join_object_range_state = top_half ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
} else {
if (mouse_mode == MouseObject) {
_join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
} else if (mouse_mode == MouseRange) {
_join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
}
}
}
Editing::MouseMode
Editor::effective_mouse_mode () const
{
if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
return MouseObject;
} else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
return MouseRange;
}
return mouse_mode;
}

View file

@ -171,6 +171,9 @@ Editor::select_all_tracks ()
selection->set (visible_views); selection->set (visible_views);
} }
/** Select clicked_routeview, unless there are no currently selected
* tracks, in which case nothing will happen unless `force' is true.
*/
void void
Editor::set_selected_track_as_side_effect (bool force) Editor::set_selected_track_as_side_effect (bool force)
{ {
@ -1392,4 +1395,12 @@ Editor::deselect_all ()
selection->clear (); selection->clear ();
} }
void
Editor::select_range_around_region (RegionView* rv)
{
selection->set (&rv->get_time_axis_view());
selection->time.clear ();
boost::shared_ptr<Region> r = rv->region ();
selection->set (r->position(), r->position() + r->length());
}

View file

@ -50,9 +50,10 @@ AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView&
} }
void void
AudioRegionGainLine::start_drag (ControlPoint* cp, nframes_t x, float fraction) AudioRegionGainLine::start_drag_single (ControlPoint* cp, nframes_t x, float fraction)
{ {
AutomationLine::start_drag (cp, x, fraction); AutomationLine::start_drag_single (cp, x, fraction);
if (!rv.audio_region()->envelope_active()) { if (!rv.audio_region()->envelope_active()) {
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0)); trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0));
rv.audio_region()->set_envelope_active(false); rv.audio_region()->set_envelope_active(false);
@ -85,13 +86,13 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
} }
void void
AudioRegionGainLine::end_drag (ControlPoint* cp) AudioRegionGainLine::end_drag ()
{ {
if (!rv.audio_region()->envelope_active()) { if (!rv.audio_region()->envelope_active()) {
rv.audio_region()->set_envelope_active(true); rv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state())); trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state()));
} }
AutomationLine::end_drag(cp); AutomationLine::end_drag ();
} }

View file

@ -38,8 +38,8 @@ class AudioRegionGainLine : public AutomationLine
public: public:
AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Group& parent, boost::shared_ptr<ARDOUR::AutomationList>); AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Group& parent, boost::shared_ptr<ARDOUR::AutomationList>);
void start_drag (ControlPoint*, nframes_t x, float fraction); void start_drag_single (ControlPoint*, nframes_t x, float fraction);
void end_drag (ControlPoint*); void end_drag ();
void remove_point (ControlPoint&); void remove_point (ControlPoint&);

View file

@ -126,7 +126,7 @@ BasicUI::transport_play (bool from_last_start)
} }
if (session->get_play_range ()) { if (session->get_play_range ()) {
session->request_play_range (false); session->request_play_range (0);
} }
if (from_last_start && rolling) { if (from_last_start && rolling) {