summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/ardour_ui.cc25
-rw-r--r--gtk2_ardour/ardour_ui.h3
-rw-r--r--gtk2_ardour/ardour_ui2.cc18
-rw-r--r--gtk2_ardour/automation_line.cc188
-rw-r--r--gtk2_ardour/automation_line.h32
-rw-r--r--gtk2_ardour/automation_time_axis.h4
-rw-r--r--gtk2_ardour/control_point.cc9
-rw-r--r--gtk2_ardour/editor.cc60
-rw-r--r--gtk2_ardour/editor.h17
-rw-r--r--gtk2_ardour/editor_actions.cc6
-rw-r--r--gtk2_ardour/editor_canvas_events.cc1
-rw-r--r--gtk2_ardour/editor_drag.cc175
-rw-r--r--gtk2_ardour/editor_drag.h27
-rw-r--r--gtk2_ardour/editor_mouse.cc171
-rw-r--r--gtk2_ardour/editor_selection.cc13
-rw-r--r--gtk2_ardour/region_gain_line.cc9
-rw-r--r--gtk2_ardour/region_gain_line.h4
-rw-r--r--libs/surfaces/control_protocol/basic_ui.cc2
18 files changed, 580 insertions, 184 deletions
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 9b70f266e4..8ab4cf6577 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -1558,9 +1558,14 @@ ARDOUR_UI::transport_roll ()
if (_session->get_play_loop()) {
_session->request_play_loop (false, true);
- } else if (_session->get_play_range ()) {
- _session->request_play_range (false, true);
- }
+ } else if (_session->get_play_range () && !join_play_range_button.get_active()) {
+ /* 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) {
_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) {
_session->request_stop (with_abort, true);
} else {
+ if (join_play_range_button.get_active()) {
+ _session->request_play_range (&editor->get_selection().time, true);
+ }
+
_session->request_transport_speed (1.0f);
}
}
@@ -1761,12 +1770,14 @@ ARDOUR_UI::map_transport_state ()
if (sp != 0.0) {
+ /* we're rolling */
+
if (_session->get_play_range()) {
play_selection_button.set_visual_state (1);
roll_button.set_visual_state (0);
auto_loop_button.set_visual_state (0);
-
+
} else if (_session->get_play_loop ()) {
auto_loop_button.set_visual_state (1);
@@ -1780,6 +1791,12 @@ ARDOUR_UI::map_transport_state ()
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);
} else {
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 318d189251..9cf245fbeb 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -328,6 +328,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
Gtkmm2ext::TearOff* transport_tearoff;
Gtk::Frame transport_frame;
Gtk::HBox transport_tearoff_hbox;
+ Gtk::HBox play_range_hbox;
+ Gtk::VBox play_range_vbox;
Gtk::HBox transport_hbox;
Gtk::Fixed transport_base;
Gtk::Fixed transport_button_base;
@@ -385,6 +387,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
BindableButton auto_loop_button;
BindableButton play_selection_button;
BindableButton rec_button;
+ Gtk::ToggleButton join_play_range_button;
void toggle_external_sync ();
void toggle_time_master ();
diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc
index ab772389a7..b759e022a1 100644
--- a/gtk2_ardour/ardour_ui2.cc
+++ b/gtk2_ardour/ardour_ui2.cc
@@ -178,6 +178,7 @@ ARDOUR_UI::setup_transport ()
play_selection_button.set_name ("TransportButton");
rec_button.set_name ("TransportRecButton");
auto_loop_button.set_name ("TransportButton");
+ join_play_range_button.set_name ("TransportButton");
auto_return_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->show();
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;
@@ -361,11 +365,17 @@ ARDOUR_UI::setup_transport ()
transport_tearoff_hbox.pack_start (*svbox, false, false, 3);
- transport_tearoff_hbox.pack_start (auto_loop_button, false, false);
- if (!Profile->get_sae()) {
- transport_tearoff_hbox.pack_start (play_selection_button, false, false);
+ if (Profile->get_sae()) {
+ transport_tearoff_hbox.pack_start (auto_loop_button);
+ 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 (rec_button, false, false, 6);
diff --git a/gtk2_ardour/automation_line.cc b/gtk2_ardour/automation_line.cc
index 128cfb5148..0c568ee55a 100644
--- a/gtk2_ardour/automation_line.cc
+++ b/gtk2_ardour/automation_line.cc
@@ -239,7 +239,7 @@ AutomationLine::modify_point_y (ControlPoint& cp, double y)
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;
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 = _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
already in frames. it needs to be converted to canvas units.
@@ -362,15 +362,12 @@ AutomationLine::reset_line_coords (ControlPoint& cp)
}
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;
- for (uint32_t i = start; i <= end; ++i) {
- p = nth(i);
- sync_model_with_view_point (*p, false, 0);
+ for (list<ControlPoint*>::iterator i = cp.begin(); i != cp.end(); ++i) {
+ sync_model_with_view_point (**i, did_push, distance);
}
}
@@ -488,6 +485,14 @@ AutomationLine::determine_visible_control_points (ALPoints& points)
double tx = points[pi].x;
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)) {
warning << string_compose (_("Ignoring illegal points on AutomationLine \"%1\""),
_name) << endmsg;
@@ -667,66 +672,36 @@ AutomationLine::invalidate_point (ALPoints& p, uint32_t index)
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
-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? */
- return;
- }
-
- string str;
-
- if (cp) {
- str = _("automation event move");
- } else {
- str = _("automation range drag");
- }
-
- trackview.editor().session()->begin_reversible_command (str);
+ trackview.editor().session()->begin_reversible_command (_("automation event move"));
trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
- drag_x = x;
- drag_distance = 0;
- first_drag_fraction = fraction;
- last_drag_fraction = fraction;
- drags = 0;
- did_push = false;
-}
-
-void
-AutomationLine::point_drag (ControlPoint& cp, nframes_t x, float fraction, bool with_push)
-{
- if (x > drag_x) {
- drag_distance += (x - drag_x);
- } else {
- drag_distance -= (drag_x - x);
- }
-
- drag_x = x;
-
- modify_view_point (cp, x, fraction, with_push);
-
- if (line_points.size() > 1) {
- line->property_points() = line_points;
- }
-
- drags++;
- did_push = with_push;
+ _drag_points.clear ();
+ _drag_points.push_back (cp);
+ start_drag_common (x, fraction);
}
+/** 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
-AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_push)
+AutomationLine::start_drag_line (uint32_t i1, uint32_t i2, float fraction)
{
- double ydelta = fraction - last_drag_fraction;
-
- did_push = with_push;
-
- last_drag_fraction = fraction;
+ trackview.editor().session()->begin_reversible_command (_("automation range move"));
+ trackview.editor().session()->add_command (new MementoCommand<AutomationList>(*alist.get(), &get_state(), 0));
- line_drag_cp1 = i1;
- line_drag_cp2 = i2;
+ _drag_points.clear ();
- //check if one of the control points on the line is in a selected range
+ // check if one of the control points on the line is in a selected range
bool range_found = false;
ControlPoint *cp;
@@ -740,38 +715,92 @@ AutomationLine::line_drag (uint32_t i1, uint32_t i2, float fraction, bool with_p
if (range_found) {
for (vector<ControlPoint*>::iterator i = control_points.begin(); i != control_points.end(); ++i) {
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 {
- ControlPoint *cp;
for (uint32_t i = i1 ; i <= i2; i++) {
- cp = nth (i);
- modify_view_point (*cp, trackview.editor().unit_to_frame (cp->get_x()), ((_height - cp->get_y()) /_height) + ydelta, with_push);
+ _drag_points.push_back (nth (i));
}
}
+ 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) {
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
-AutomationLine::end_drag (ControlPoint* cp)
+AutomationLine::end_drag ()
{
- if (!drags) {
+ if (!_drag_had_movement) {
return;
}
alist->freeze ();
- if (cp) {
- sync_model_with_view_point (*cp, did_push, drag_distance);
- } else {
- sync_model_with_view_line (line_drag_cp1, line_drag_cp2);
- }
+ sync_model_with_view_points (_drag_points, did_push, drag_distance);
alist->thaw ();
@@ -782,7 +811,6 @@ AutomationLine::end_drag (ControlPoint* cp)
trackview.editor().session()->set_dirty ();
}
-
void
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);
}
-
}
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);
shape = ControlPoint::Full;
}
-
+
control_points[view_index]->reset (tx, ty, model, view_index, shape);
/* 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);
+}
+
diff --git a/gtk2_ardour/automation_line.h b/gtk2_ardour/automation_line.h
index 35923c08b8..436e5cbb20 100644
--- a/gtk2_ardour/automation_line.h
+++ b/gtk2_ardour/automation_line.h
@@ -74,10 +74,11 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
bool control_points_adjacent (double xval, uint32_t& before, uint32_t& after);
/* dragging API */
- virtual void start_drag (ControlPoint*, nframes_t x, float fraction);
- virtual void point_drag(ControlPoint&, nframes_t x, float, bool with_push);
- virtual void end_drag (ControlPoint*);
- virtual void line_drag(uint32_t i1, uint32_t i2, float, bool with_push);
+ virtual void start_drag_single (ControlPoint*, nframes_t x, float);
+ virtual void start_drag_line (uint32_t, uint32_t, float);
+ virtual void start_drag_multiple (std::list<ControlPoint*>, float, XMLNode *);
+ virtual void drag_motion (nframes_t, float, bool);
+ virtual void end_drag ();
ControlPoint* nth (uint32_t);
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 add_always_in_view (double);
+ void clear_always_in_view ();
+
protected:
std::string _name;
@@ -164,8 +168,9 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
static bool invalid_point (ALPoints&, uint32_t index);
void determine_visible_control_points (ALPoints&);
- void sync_model_with_view_point (ControlPoint&, bool did_push, int64_t distance);
- void sync_model_with_view_line (uint32_t, uint32_t);
+ void sync_model_with_view_point (ControlPoint&, bool, int64_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);
@@ -177,18 +182,17 @@ class AutomationLine : public sigc::trackable, public PBD::StatefulDestructible
virtual void add_model_point (ALPoints& tmp_points, double frame, double yfract);
private:
- uint32_t drags;
- double first_drag_fraction;
- double last_drag_fraction;
- uint32_t line_drag_cp1;
- uint32_t line_drag_cp2;
- int64_t drag_x;
- int64_t drag_distance;
+ std::list<ControlPoint*> _drag_points; ///< points we are dragging
+ bool _drag_had_movement; ///< true if the drag has seen movement, otherwise false
+ nframes64_t drag_x; ///< last x position of the drag, in frames
+ nframes64_t drag_distance; ///< total x movement of the drag, in frames
+ double _last_drag_fraction; ///< last y position of the drag, as a fraction
+ std::list<double> _always_in_view;
const Evoral::TimeConverter<double, ARDOUR::sframes_t>& _time_converter;
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 add_visible_control_point (uint32_t, uint32_t, double, double, ARDOUR::AutomationList::iterator, uint32_t);
diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h
index 05e0746bf2..b1500e51d1 100644
--- a/gtk2_ardour/automation_time_axis.h
+++ b/gtk2_ardour/automation_time_axis.h
@@ -100,6 +100,10 @@ class AutomationTimeAxisView : public TimeAxisView {
boost::shared_ptr<ARDOUR::AutomationControl> control() { return _control; }
boost::shared_ptr<AutomationController> controller() { return _controller; }
+ ArdourCanvas::Item* base_item () const {
+ return _base_rect;
+ }
+
protected:
boost::shared_ptr<ARDOUR::Route> _route; ///< Parent route
boost::shared_ptr<ARDOUR::AutomationControl> _control; ///< Control
diff --git a/gtk2_ardour/control_point.cc b/gtk2_ardour/control_point.cc
index 78a3dc57d5..781f9f3b31 100644
--- a/gtk2_ardour/control_point.cc
+++ b/gtk2_ardour/control_point.cc
@@ -155,15 +155,6 @@ void
ControlPoint::set_size (double 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);
}
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 024799bbbf..fd6d8a991f 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -220,8 +220,10 @@ show_me_the_size (Requisition* r, const char* what)
}
Editor::Editor ()
+ : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
+
/* time display buttons */
- : minsec_label (_("Mins:Secs"))
+ , minsec_label (_("Mins:Secs"))
, bbt_label (_("Bars:Beats"))
, timecode_label (_("Timecode"))
, 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"))) {
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 ("mouse-mode", enum2str(mouse_mode));
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"));
if (act) {
@@ -2752,24 +2759,45 @@ Editor::setup_toolbar ()
mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
mouse_audition_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);
mode_box->set_border_width (2);
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);
+
+ /* 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_box.pack_start(mouse_zoom_button, true, true);
+
+ mouse_mode_button_table->attach (mouse_zoom_button, c, c + 1, 0, 1);
+ ++c;
+
if (!Profile->get_sae()) {
- mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
+ mouse_mode_button_table->attach (mouse_gain_button, c, c + 1, 0, 1);
+ ++c;
}
- 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);
-
+
+ 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;
edit_mode_strings.push_back (edit_mode_to_string (Slide));
if (!Profile->get_sae()) {
@@ -2781,8 +2809,8 @@ Editor::setup_toolbar ()
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));
- mode_box->pack_start(edit_mode_selector);
- mode_box->pack_start(mouse_mode_button_box);
+ mode_box->pack_start (edit_mode_selector);
+ mode_box->pack_start (*mouse_mode_button_table);
mouse_mode_tearoff = manage (new TearOff (*mode_box));
mouse_mode_tearoff->set_name ("MouseModeBase");
@@ -2807,6 +2835,7 @@ Editor::setup_toolbar ()
mouse_zoom_button.set_mode (false);
mouse_timefx_button.set_mode (false);
mouse_audition_button.set_mode (false);
+ join_object_range_button.set_mode (false);
mouse_move_button.set_name ("MouseModeButton");
mouse_select_button.set_name ("MouseModeButton");
@@ -2814,8 +2843,8 @@ Editor::setup_toolbar ()
mouse_zoom_button.set_name ("MouseModeButton");
mouse_timefx_button.set_name ("MouseModeButton");
mouse_audition_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_select_button.unset_flags (CAN_FOCUS);
@@ -2824,6 +2853,7 @@ Editor::setup_toolbar ()
mouse_timefx_button.unset_flags (CAN_FOCUS);
mouse_audition_button.unset_flags (CAN_FOCUS);
internal_edit_button.unset_flags (CAN_FOCUS);
+ join_object_range_button.unset_flags (CAN_FOCUS);
/* Zoom */
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index ca1082af78..14e6b1b68d 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -487,6 +487,19 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
Editing::MouseMode mouse_mode;
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_pane_position;
@@ -594,6 +607,8 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void collect_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_region_context_menu;
Gtk::Menu track_selection_context_menu;
@@ -1492,7 +1507,6 @@ public:
Gtk::Table toolbar_selection_clock_table;
Gtk::Label toolbar_selection_cursor_label;
- Gtk::HBox mouse_mode_button_box;
Gtkmm2ext::TearOff* mouse_mode_tearoff;
Gtk::ToggleButton mouse_select_button;
Gtk::ToggleButton mouse_move_button;
@@ -1500,6 +1514,7 @@ public:
Gtk::ToggleButton mouse_zoom_button;
Gtk::ToggleButton mouse_timefx_button;
Gtk::ToggleButton mouse_audition_button;
+ Gtk::ToggleButton join_object_range_button;
void mouse_mode_toggled (Editing::MouseMode m);
bool ignore_mouse_mode_toggle;
diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc
index 6a7e818135..99177601f6 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -683,7 +683,11 @@ Editor::register_actions ()
mouse_select_button.set_name ("MouseModeButton");
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);
mouse_gain_button.set_image (*(manage (new Image (::get_icon("tool_gain")))));
mouse_gain_button.set_label ("");
diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc
index 24b2e646a6..f3131487cd 100644
--- a/gtk2_ardour/editor_canvas_events.cc
+++ b/gtk2_ardour/editor_canvas_events.cc
@@ -265,7 +265,6 @@ Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, Reg
return false;
}
-
switch (event->type) {
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index cd9419cffd..4de1722ed8 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -40,6 +40,7 @@
#include "canvas-note.h"
#include "selection.h"
#include "midi_selection.h"
+#include "automation_time_axis.h"
using namespace std;
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
_time_axis_view_grab_x = _point->get_x();
_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);
- _editor->track_canvas->w2c (_time_axis_view_grab_x, _time_axis_view_grab_y, _time_axis_view_grab_x, _time_axis_view_grab_y);
+ float const fraction = 1 - (_point->get_y() / _point->line().height());
- _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),
event->button.x + 10, event->button.y + 10);
@@ -2492,18 +2490,16 @@ ControlPointDrag::motion (GdkEvent* event, bool)
dy *= 0.1;
}
+ /* coordinate in TimeAxisView's space */
double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
// calculate zero crossing point. back off by .01 to stay on the
// positive side of zero
- double _unused = 0;
- double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
- _point->line().parent_group().i2w(_unused, zero_gain_y);
+ double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
// make sure we hit zero when passing through
- if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
- or (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)) {
cy = zero_gain_y;
}
@@ -2517,13 +2513,10 @@ ControlPointDrag::motion (GdkEvent* event, bool)
_cumulative_x_drag = cx - _time_axis_view_grab_x;
_cumulative_y_drag = cy - _time_axis_view_grab_y;
- _point->line().parent_group().w2i (cx, cy);
-
cx = max (0.0, cx);
cy = max (0.0, cy);
cy = min ((double) _point->line().height(), cy);
- //translate cx to frames
nframes64_t cx_frames = _editor->unit_to_frame (cx);
if (!_x_constrained) {
@@ -2534,7 +2527,7 @@ ControlPointDrag::motion (GdkEvent* event, bool)
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));
}
@@ -2553,7 +2546,7 @@ ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
} else {
motion (event, false);
}
- _point->line().end_drag (_point);
+ _point->line().end_drag ();
}
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);
- 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 */
return;
}
@@ -2608,7 +2604,7 @@ LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
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),
event->button.x + 10, event->button.y + 10);
@@ -2642,7 +2638,8 @@ LineDrag::motion (GdkEvent* event, bool)
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));
}
@@ -2651,7 +2648,7 @@ void
LineDrag::finished (GdkEvent* event, bool)
{
motion (event, false);
- _line->end_drag (0);
+ _line->end_drag ();
}
void
@@ -2850,6 +2847,8 @@ SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
: Drag (e, i)
, _operation (o)
, _copy (false)
+ , _original_pointer_time_axis (-1)
+ , _last_pointer_time_axis (-1)
{
}
@@ -2909,6 +2908,8 @@ SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
} else {
_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
@@ -2918,13 +2919,16 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
nframes64_t end = 0;
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);
- /* only alter selection if the current frame is
- different from the last frame position (adjusted)
- */
+ /* only alter selection if things have changed */
- if (pending_position == last_pointer_frame()) {
+ if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
return;
}
@@ -2969,8 +2973,36 @@ SelectionDrag::motion (GdkEvent* event, bool first_move)
_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:
@@ -3490,3 +3522,96 @@ NoteDrag::finished (GdkEvent* ev, bool moved)
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 ();
+}
diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h
index 1d9ac151d8..9d7215d93f 100644
--- a/gtk2_ardour/editor_drag.h
+++ b/gtk2_ardour/editor_drag.h
@@ -474,7 +474,6 @@ private:
ControlPoint* _point;
double _time_axis_view_grab_x;
double _time_axis_view_grab_y;
- nframes64_t _time_axis_view_grab_frame;
double _cumulative_x_drag;
double _cumulative_y_drag;
static double const _zero_gain_fraction;
@@ -537,7 +536,7 @@ public:
void finished (GdkEvent *, bool);
};
-/** Drag in range select(gc_owner.get()) moAutomatable */
+/** Drag in range select mode */
class SelectionDrag : public Drag
{
public:
@@ -557,6 +556,9 @@ public:
private:
Operation _operation;
bool _copy;
+ int _original_pointer_time_axis;
+ int _last_pointer_time_axis;
+ std::list<TimeAxisView*> _added_time_axes;
};
/** Range marker drag */
@@ -583,16 +585,33 @@ private:
bool _copy;
};
-/* Drag of rectangle to set zoom */
+/** Drag of rectangle to set zoom */
class MouseZoomDrag : public Drag
{
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 motion (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_ */
diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc
index 5fbd76ed49..1b91947d40 100644
--- a/gtk2_ardour/editor_mouse.cc
+++ b/gtk2_ardour/editor_mouse.cc
@@ -134,13 +134,11 @@ Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
-
*pcx = event->button.x;
*pcy = event->button.y;
_trackview_group->w2i(*pcx, *pcy);
break;
case GDK_MOTION_NOTIFY:
-
*pcx = event->motion.x;
*pcy = event->motion.y;
_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()) {
track_canvas->get_window()->set_cursor(*current_canvas_cursor);
}
@@ -315,9 +324,9 @@ Editor::mouse_mode_toggled (MouseMode m)
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.
*/
@@ -331,7 +340,7 @@ Editor::mouse_mode_toggled (MouseMode m)
} 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) {
@@ -416,6 +425,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
*/
if (((mouse_mode != MouseObject) &&
+ (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
(mouse_mode != MouseAudition || item_type != RegionItem) &&
(mouse_mode != MouseTimeFX || item_type != RegionItem) &&
(mouse_mode != MouseGain) &&
@@ -445,16 +455,21 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
switch (item_type) {
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);
} 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;
case RegionViewNameHighlight:
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);
} else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect ();
@@ -466,7 +481,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case FadeInItem:
case FadeOutHandleItem:
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);
} else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect ();
@@ -475,7 +490,7 @@ Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemTyp
case ControlPointItem:
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);
}
break;
@@ -597,7 +612,9 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break;
}
- switch (mouse_mode) {
+ Editing::MouseMode eff = effective_mouse_mode ();
+
+ switch (eff) {
case MouseRange:
switch (item_type) {
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->start_grab (event);
} else {
- /* this was debated, but decided the more common action was to
- make a new selection */
- assert (_drag == 0);
- _drag = new SelectionDrag (this, item, SelectionDrag::CreateSelection);
- _drag->start_grab (event);
+ 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);
+ 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;
@@ -734,14 +761,46 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
_drag = new RegionCreateDrag (this, item, clicked_axisview);
_drag->start_grab (event);
return true;
+ } else {
+ assert (_drag == 0);
+ _drag = new RubberbandSelectDrag (this, item);
+ _drag->start_grab (event);
}
- /* fallthru */
+ break;
+
case AutomationTrackItem:
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);
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
case ImageFrameHandleStartItem:
imageframe_start_handle_op(item, event) ;
@@ -878,7 +937,8 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
bool
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:
switch (item_type) {
case RegionItem:
@@ -1152,6 +1212,8 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
/* delete events get handled here */
+ Editing::MouseMode const eff = effective_mouse_mode ();
+
if (_drag == 0 && Keyboard::is_delete_event (&event->button)) {
switch (item_type) {
@@ -1168,13 +1230,13 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break;
case RegionItem:
- if (mouse_mode == MouseObject) {
+ if (eff == MouseObject) {
remove_clicked_region ();
}
break;
case ControlPointItem:
- if (mouse_mode == MouseGain) {
+ if (eff == MouseGain) {
remove_gain_control_point (item, event);
} else {
remove_control_point (item, event);
@@ -1233,7 +1295,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
break;
}
- switch (mouse_mode) {
+ switch (eff) {
case MouseObject:
switch (item_type) {
case AutomationTrackItem:
@@ -1305,7 +1367,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
case 2:
- switch (mouse_mode) {
+ switch (eff) {
case MouseObject:
switch (item_type) {
@@ -1348,7 +1410,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
}
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;
Marker * marker;
@@ -1544,7 +1606,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType i
}
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;
ControlPoint* cp;
@@ -1661,6 +1723,10 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType i
break;
}
+ if (item_type == RegionItem) {
+ update_join_object_range_location (event->crossing.x, event->crossing.y);
+ }
+
return false;
}
@@ -1782,6 +1848,12 @@ Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from
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;
if (_drag) {
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;
+}
diff --git a/gtk2_ardour/editor_selection.cc b/gtk2_ardour/editor_selection.cc
index 4e66847ba2..59be957138 100644
--- a/gtk2_ardour/editor_selection.cc
+++ b/gtk2_ardour/editor_selection.cc
@@ -171,6 +171,9 @@ Editor::select_all_tracks ()
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
Editor::set_selected_track_as_side_effect (bool force)
{
@@ -1392,4 +1395,12 @@ Editor::deselect_all ()
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());
+}
diff --git a/gtk2_ardour/region_gain_line.cc b/gtk2_ardour/region_gain_line.cc
index 00352c96df..a2c5cffa5a 100644
--- a/gtk2_ardour/region_gain_line.cc
+++ b/gtk2_ardour/region_gain_line.cc
@@ -50,9 +50,10 @@ AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView&
}
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()) {
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), &rv.audio_region()->get_state(), 0));
rv.audio_region()->set_envelope_active(false);
@@ -85,13 +86,13 @@ AudioRegionGainLine::remove_point (ControlPoint& cp)
}
void
-AudioRegionGainLine::end_drag (ControlPoint* cp)
+AudioRegionGainLine::end_drag ()
{
if (!rv.audio_region()->envelope_active()) {
rv.audio_region()->set_envelope_active(true);
trackview.session()->add_command(new MementoCommand<AudioRegion>(*(rv.audio_region().get()), 0, &rv.audio_region()->get_state()));
}
- AutomationLine::end_drag(cp);
+ AutomationLine::end_drag ();
}
diff --git a/gtk2_ardour/region_gain_line.h b/gtk2_ardour/region_gain_line.h
index d54c962294..0f16372287 100644
--- a/gtk2_ardour/region_gain_line.h
+++ b/gtk2_ardour/region_gain_line.h
@@ -38,8 +38,8 @@ class AudioRegionGainLine : public AutomationLine
public:
AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Group& parent, boost::shared_ptr<ARDOUR::AutomationList>);
- void start_drag (ControlPoint*, nframes_t x, float fraction);
- void end_drag (ControlPoint*);
+ void start_drag_single (ControlPoint*, nframes_t x, float fraction);
+ void end_drag ();
void remove_point (ControlPoint&);
diff --git a/libs/surfaces/control_protocol/basic_ui.cc b/libs/surfaces/control_protocol/basic_ui.cc
index a21da95bb1..8bb353edd4 100644
--- a/libs/surfaces/control_protocol/basic_ui.cc
+++ b/libs/surfaces/control_protocol/basic_ui.cc
@@ -126,7 +126,7 @@ BasicUI::transport_play (bool from_last_start)
}
if (session->get_play_range ()) {
- session->request_play_range (false);
+ session->request_play_range (0);
}
if (from_last_start && rolling) {