From f5acf936728c721bc556b7b3dfe69ebf0c3d63ed Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 1 Jan 2010 22:11:15 +0000 Subject: 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 --- gtk2_ardour/ardour_ui.cc | 25 ++++- gtk2_ardour/ardour_ui.h | 3 + gtk2_ardour/ardour_ui2.cc | 18 +++- gtk2_ardour/automation_line.cc | 188 ++++++++++++++++++++++-------------- gtk2_ardour/automation_line.h | 32 +++--- gtk2_ardour/automation_time_axis.h | 4 + gtk2_ardour/control_point.cc | 9 -- gtk2_ardour/editor.cc | 60 +++++++++--- gtk2_ardour/editor.h | 17 +++- gtk2_ardour/editor_actions.cc | 6 +- gtk2_ardour/editor_canvas_events.cc | 1 - gtk2_ardour/editor_drag.cc | 175 ++++++++++++++++++++++++++++----- gtk2_ardour/editor_drag.h | 27 +++++- gtk2_ardour/editor_mouse.cc | 171 +++++++++++++++++++++++++++----- gtk2_ardour/editor_selection.cc | 13 ++- gtk2_ardour/region_gain_line.cc | 9 +- gtk2_ardour/region_gain_line.h | 4 +- 17 files changed, 579 insertions(+), 183 deletions(-) (limited to 'gtk2_ardour') 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 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 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::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(*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(*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::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 cp, float fraction, XMLNode* state) +{ + trackview.editor().session()->begin_reversible_command (_("automation range move")); + trackview.editor().session()->add_command (new MementoCommand(*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::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, 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, 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 _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 _always_in_view; const Evoral::TimeConverter& _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 control() { return _control; } boost::shared_ptr controller() { return _controller; } + ArdourCanvas::Item* base_item () const { + return _base_rect; + } + protected: boost::shared_ptr _route; ///< Parent route boost::shared_ptr _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 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 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 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::iterator i = _added_time_axes.begin(); + while (i != _added_time_axes.end()) { + + list::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 const & r) + : Drag (e, i) + , _ranges (r) + , _nothing_to_drag (false) +{ + _atav = reinterpret_cast (_item->get_data ("trackview")); + assert (_atav); + + _line = _atav->line (); +} + +void +AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor) +{ + Drag::start_grab (event, cursor); + + list 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 the_list = _line->the_list (); + for (list::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::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 _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 const &); + + void start_grab (GdkEvent *, Gdk::Cursor* c = 0); + void motion (GdkEvent *, bool); + void finished (GdkEvent *, bool); + +private: + std::list _ranges; + AutomationTimeAxisView* _atav; + boost::shared_ptr _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 tvp = trackview_by_y_position (y); + if (tvp.first) { + AutomationTimeAxisView* atv = dynamic_cast (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 tvp = trackview_by_y_position (y); + if (tvp.first) { + AutomationTimeAxisView* atv = dynamic_cast (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 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(*(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(*(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); - 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&); -- cgit v1.2.3