freehand line drawing: automation & velocity share the same basic code

This commit is contained in:
Paul Davis 2023-07-10 17:32:18 -06:00
parent 2c48aabe08
commit df52c39ce0
9 changed files with 235 additions and 107 deletions

View file

@ -1702,6 +1702,7 @@ private:
bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas); bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas);
bool canvas_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*); bool canvas_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*);
bool canvas_velocity_event (GdkEvent* event,ArdourCanvas::Item*); bool canvas_velocity_event (GdkEvent* event,ArdourCanvas::Item*);
bool canvas_velocity_base_event (GdkEvent* event,ArdourCanvas::Item*);
bool canvas_line_event (GdkEvent* event,ArdourCanvas::Item*, AutomationLine*); bool canvas_line_event (GdkEvent* event,ArdourCanvas::Item*, AutomationLine*);
bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*); bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
bool canvas_selection_start_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*); bool canvas_selection_start_trim_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);

View file

@ -693,10 +693,15 @@ Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, C
bool bool
Editor::canvas_velocity_event (GdkEvent *event, ArdourCanvas::Item* item) Editor::canvas_velocity_event (GdkEvent *event, ArdourCanvas::Item* item)
{ {
// std::cerr << "Velocity event: " << Gtkmm2ext::event_type_string (event->type) << std::endl;
return typed_event (item, event, VelocityItem); return typed_event (item, event, VelocityItem);
} }
bool
Editor::canvas_velocity_base_event (GdkEvent *event, ArdourCanvas::Item* item)
{
return typed_event (item, event, VelocityBaseItem);
}
bool bool
Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, AutomationLine* al) Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, AutomationLine* al)
{ {

View file

@ -7199,6 +7199,7 @@ LollipopDrag::LollipopDrag (Editor* ed, ArdourCanvas::Item* l)
: Drag (ed, l, Temporal::BeatTime) : Drag (ed, l, Temporal::BeatTime)
, _primary (dynamic_cast<ArdourCanvas::Lollipop*> (l)) , _primary (dynamic_cast<ArdourCanvas::Lollipop*> (l))
{ {
DEBUG_TRACE (DEBUG::Drags, "New LollipopDrag\n");
_region = reinterpret_cast<VelocityGhostRegion*> (_item->get_data ("ghostregionview")); _region = reinterpret_cast<VelocityGhostRegion*> (_item->get_data ("ghostregionview"));
} }
@ -7257,7 +7258,10 @@ LollipopDrag::setup_pointer_offset ()
_pointer_offset = _region->parent_rv.region()->source_beats_to_absolute_time (note->note()->time ()).distance (raw_grab_time ()); _pointer_offset = _region->parent_rv.region()->source_beats_to_absolute_time (note->note()->time ()).distance (raw_grab_time ());
} }
AutomationDrawDrag::AutomationDrawDrag (Editor* editor, ArdourCanvas::Rectangle& r, Temporal::TimeDomain time_domain) /********/
template<typename OrderedPointList, typename OrderedPoint>
FreehandLineDrag<OrderedPointList,OrderedPoint>::FreehandLineDrag (Editor* editor, ArdourCanvas::Rectangle& r, Temporal::TimeDomain time_domain)
: Drag (editor, &r, time_domain) : Drag (editor, &r, time_domain)
, base_rect (r) , base_rect (r)
, dragging_line (nullptr) , dragging_line (nullptr)
@ -7269,13 +7273,15 @@ AutomationDrawDrag::AutomationDrawDrag (Editor* editor, ArdourCanvas::Rectangle&
DEBUG_TRACE (DEBUG::Drags, "New AutomationDrawDrag\n"); DEBUG_TRACE (DEBUG::Drags, "New AutomationDrawDrag\n");
} }
AutomationDrawDrag::~AutomationDrawDrag () template<typename OrderedPointList, typename OrderedPoint>
FreehandLineDrag<OrderedPointList,OrderedPoint>::~FreehandLineDrag ()
{ {
delete dragging_line; delete dragging_line;
} }
template<typename OrderedPointList, typename OrderedPoint>
void void
AutomationDrawDrag::motion (GdkEvent* ev, bool first_move) FreehandLineDrag<OrderedPointList,OrderedPoint>::motion (GdkEvent* ev, bool first_move)
{ {
if (first_move) { if (first_move) {
dragging_line = new ArdourCanvas::PolyLine (item()); dragging_line = new ArdourCanvas::PolyLine (item());
@ -7293,14 +7299,15 @@ AutomationDrawDrag::motion (GdkEvent* ev, bool first_move)
/* Add a point correspding to the start of the drag */ /* Add a point correspding to the start of the drag */
maybe_add_point (ev, raw_grab_time()); maybe_add_point (ev, raw_grab_time(), true);
} }
maybe_add_point (ev, _drags->current_pointer_time()); maybe_add_point (ev, _drags->current_pointer_time(), first_move);
} }
template<typename OrderedPointList, typename OrderedPoint>
void void
AutomationDrawDrag::maybe_add_point (GdkEvent* ev, timepos_t const & cpos) FreehandLineDrag<OrderedPointList,OrderedPoint>::maybe_add_point (GdkEvent* ev, timepos_t const & cpos, bool first_move)
{ {
timepos_t pos (cpos); timepos_t pos (cpos);
@ -7347,24 +7354,91 @@ AutomationDrawDrag::maybe_add_point (GdkEvent* ev, timepos_t const & cpos)
} }
} }
bool child_call = false;
if (pop_point) { if (pop_point) {
if (line_break_pending) { if (line_break_pending) {
line_break_pending = false; line_break_pending = false;
} else { } else {
dragging_line->pop_back(); dragging_line->pop_back();
drawn_points.pop_back (); drawn_points.pop_back ();
child_call = true;
} }
} }
if (add_point) { if (add_point) {
if (drawn_points.empty() || (pos != drawn_points.back().when)) { if (drawn_points.empty() || (pos != drawn_points.back().when)) {
dragging_line->add_point (ArdourCanvas::Duple (x, y)); dragging_line->add_point (ArdourCanvas::Duple (x, y));
drawn_points.push_back (Evoral::ControlList::OrderedPoint (pos, y)); drawn_points.push_back (OrderedPoint (pos, y));
child_call = true;
} }
}
if (child_call) {
point_added (ArdourCanvas::Duple (pointer_x, y), base_rect, first_move ? -1 : edge_x);
}
if (add_point) {
edge_x = pointer_x; edge_x = pointer_x;
} }
} }
template<typename OrderedPointList, typename OrderedPoint>
void
FreehandLineDrag<OrderedPointList,OrderedPoint>::finished (GdkEvent* event, bool motion_occured)
{
if (!motion_occured) {
/* DragManager will tell editor that no motion happened, and
Editor::button_release_handler() will do the right thing.
*/
return;
}
if (drawn_points.empty()) {
return;
}
/* Points must be in time order, so if the user draw right to left, fix
* that here
*/
if (drawn_points.front().when > drawn_points.back().when) {
std::reverse (drawn_points.begin(), drawn_points.end());
}
}
template<typename OrderedPointList, typename OrderedPoint>
bool
FreehandLineDrag<OrderedPointList,OrderedPoint>::mid_drag_key_event (GdkEventKey* ev)
{
if (ev->type == GDK_KEY_PRESS) {
switch (ev->keyval) {
case GDK_Alt_R:
case GDK_Alt_L:
line_break_pending = true;
return true;
default:
break;
}
}
return false;
}
/**********************/
AutomationDrawDrag::AutomationDrawDrag (Editor* editor, ArdourCanvas::Rectangle& r, Temporal::TimeDomain time_domain)
: FreehandLineDrag<Evoral::ControlList::OrderedPoints,Evoral::ControlList::OrderedPoint> (editor, r, time_domain)
{
DEBUG_TRACE (DEBUG::Drags, "New AutomationDrawDrag\n");
}
AutomationDrawDrag::~AutomationDrawDrag ()
{
}
void void
AutomationDrawDrag::finished (GdkEvent* event, bool motion_occured) AutomationDrawDrag::finished (GdkEvent* event, bool motion_occured)
{ {
@ -7384,37 +7458,58 @@ AutomationDrawDrag::finished (GdkEvent* event, bool motion_occured)
return; return;
} }
/* Points must be in time order, so if the user draw right to left, fix FreehandLineDrag<Evoral::ControlList::OrderedPoints,Evoral::ControlList::OrderedPoint>::finished (event, motion_occured);
* that here
*/
if (drawn_points.front().when > drawn_points.back().when) {
std::reverse (drawn_points.begin(), drawn_points.end());
}
atv->merge_drawn_line (drawn_points, !did_snap); atv->merge_drawn_line (drawn_points, !did_snap);
} }
/*****************/
VelocityLineDrag::VelocityLineDrag (Editor* editor, ArdourCanvas::Rectangle& r, Temporal::TimeDomain time_domain)
: FreehandLineDrag<Evoral::ControlList::OrderedPoints,Evoral::ControlList::OrderedPoint> (editor, r, time_domain)
, grv (static_cast<VelocityGhostRegion*> (r.get_data ("ghostregionview")))
, drag_did_change (false)
{
DEBUG_TRACE (DEBUG::Drags, "New VelocityLineDrag\n");
assert (grv);
}
VelocityLineDrag::~VelocityLineDrag ()
{
}
void void
AutomationDrawDrag::aborted (bool) VelocityLineDrag::start_grab (GdkEvent* ev, Gdk::Cursor* c)
{ {
FreehandLineDrag<Evoral::ControlList::OrderedPoints,Evoral::ControlList::OrderedPoint>::start_grab (ev, c);
grv->start_line_drag ();
} }
bool void
AutomationDrawDrag::mid_drag_key_event (GdkEventKey* ev) VelocityLineDrag::point_added (Duple const & d, Rectangle const & r, double last_x)
{ {
if (ev->type == GDK_KEY_PRESS) { drag_did_change |= grv->line_draw_motion (d, r, last_x);
switch (ev->keyval) {
case GDK_Alt_R:
case GDK_Alt_L:
line_break_pending = true;
return true;
default:
break;
}
} }
return false; void
VelocityLineDrag::finished (GdkEvent* event, bool motion_occured)
{
if (!motion_occured) {
/* DragManager will tell editor that no motion happened, and
Editor::button_release_handler() will do the right thing.
*/
return;
}
/* no need to call FreehandLineDrag::finished(), because we do not use
* drawn_points
*/
grv->end_line_drag (drag_did_change);
}
void
VelocityLineDrag::aborted (bool)
{
grv->end_line_drag (false);
} }

View file

@ -1583,27 +1583,56 @@ class LollipopDrag : public Drag
ArdourCanvas::Lollipop* _primary; ArdourCanvas::Lollipop* _primary;
}; };
class AutomationDrawDrag : public Drag template<typename OrderedPointList, typename OrderedPoint>
class FreehandLineDrag : public Drag
{
public:
FreehandLineDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~FreehandLineDrag ();
void motion (GdkEvent*, bool);
void finished (GdkEvent*, bool);
bool mid_drag_key_event (GdkEventKey*);
virtual void point_added (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x) {}
protected:
ArdourCanvas::Rectangle& base_rect; /* we do not own this */
ArdourCanvas::PolyLine* dragging_line;
int direction;
int edge_x;
bool did_snap;
bool line_break_pending;
OrderedPointList drawn_points;
void maybe_add_point (GdkEvent*, Temporal::timepos_t const &, bool first_move);
};
class AutomationDrawDrag : public FreehandLineDrag<Evoral::ControlList::OrderedPoints, Evoral::ControlList::OrderedPoint>
{ {
public: public:
AutomationDrawDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain); AutomationDrawDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~AutomationDrawDrag (); ~AutomationDrawDrag ();
void motion (GdkEvent*, bool);
void finished (GdkEvent*, bool); void finished (GdkEvent*, bool);
void aborted (bool); void aborted (bool) {}
bool mid_drag_key_event (GdkEventKey*);
private:
ArdourCanvas::Rectangle& base_rect; /* we do not own this */
ArdourCanvas::PolyLine* dragging_line;
int direction;
int edge_x;
Evoral::ControlList::OrderedPoints drawn_points;
bool did_snap;
bool line_break_pending;
void maybe_add_point (GdkEvent*, Temporal::timepos_t const &);
}; };
class VelocityLineDrag : public FreehandLineDrag<Evoral::ControlList::OrderedPoints, Evoral::ControlList::OrderedPoint>
{
public:
VelocityLineDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~VelocityLineDrag ();
void start_grab (GdkEvent *, Gdk::Cursor* c = 0);
void finished (GdkEvent*, bool);
void aborted (bool);
void point_added (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x);
private:
VelocityGhostRegion* grv;
bool drag_did_change;
};
#endif /* __gtk2_ardour_editor_drag_h_ */ #endif /* __gtk2_ardour_editor_drag_h_ */

View file

@ -69,6 +69,7 @@ enum ItemType {
SelectionMarkerItem, SelectionMarkerItem,
DropZoneItem, DropZoneItem,
VelocityItem, VelocityItem,
VelocityBaseItem,
/* don't remove this */ /* don't remove this */

View file

@ -82,6 +82,7 @@
#include "mouse_cursors.h" #include "mouse_cursors.h"
#include "editor_cursors.h" #include "editor_cursors.h"
#include "region_peak_cursor.h" #include "region_peak_cursor.h"
#include "velocity_ghost_region.h"
#include "verbose_cursor.h" #include "verbose_cursor.h"
#include "note.h" #include "note.h"
@ -902,6 +903,16 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
return true; return true;
break; break;
case VelocityBaseItem:
{
VelocityGhostRegion* grv = static_cast<VelocityGhostRegion*> (item->get_data ("ghostregionview"));
if (grv) {
_drags->set (new VelocityLineDrag (this, grv->base_item(), Temporal::BeatTime), event);
}
}
return true;
break;
default: default:
break; break;
} }
@ -1569,6 +1580,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
/* see if we're finishing a drag */ /* see if we're finishing a drag */
if (_drags->active ()) { if (_drags->active ()) {
bool const r = _drags->end_grab (event); bool const r = _drags->end_grab (event);
if (r) { if (r) {
/* grab dragged, so do nothing else */ /* grab dragged, so do nothing else */

View file

@ -451,6 +451,7 @@ public:
virtual bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas) = 0; virtual bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas) = 0;
virtual bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) = 0; virtual bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) = 0;
virtual bool canvas_velocity_event (GdkEvent* event, ArdourCanvas::Item*) = 0; virtual bool canvas_velocity_event (GdkEvent* event, ArdourCanvas::Item*) = 0;
virtual bool canvas_velocity_base_event (GdkEvent* event, ArdourCanvas::Item*) = 0;
virtual bool canvas_line_event (GdkEvent* event, ArdourCanvas::Item*, AutomationLine*) = 0; virtual bool canvas_line_event (GdkEvent* event, ArdourCanvas::Item*, AutomationLine*) = 0;
virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0; virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
virtual bool canvas_selection_start_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0; virtual bool canvas_selection_start_trim_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;

View file

@ -58,6 +58,7 @@ VelocityGhostRegion::VelocityGhostRegion (MidiRegionView& mrv, TimeAxisView& tv,
, drag_did_change (false) , drag_did_change (false)
, selected (false) , selected (false)
{ {
base_rect->set_data (X_("ghostregionview"), this);
base_rect->Event.connect (sigc::mem_fun (*this, &VelocityGhostRegion::base_event)); base_rect->Event.connect (sigc::mem_fun (*this, &VelocityGhostRegion::base_event));
base_rect->set_fill_color (UIConfiguration::instance().color_mod ("ghost track base", "ghost track midi fill")); base_rect->set_fill_color (UIConfiguration::instance().color_mod ("ghost track base", "ghost track midi fill"));
base_rect->set_outline_color (UIConfiguration::instance().color ("automation track outline")); base_rect->set_outline_color (UIConfiguration::instance().color ("automation track outline"));
@ -70,74 +71,36 @@ VelocityGhostRegion::~VelocityGhostRegion ()
} }
bool bool
VelocityGhostRegion::base_event (GdkEvent* ev) VelocityGhostRegion::line_draw_motion (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x)
{ {
std::vector<NoteBase*> affected_lollis; std::vector<NoteBase*> affected_lollis;
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (&parent_rv); MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (&parent_rv);
ArdourCanvas::Rect r = base_rect->item_to_canvas (base_rect->get());
switch (ev->type) { if (last_x < 0) {
case GDK_MOTION_NOTIFY: lollis_close_to_x (d.x, 20., affected_lollis);
if (dragging) { } else if (last_x < d.x) {
if (last_drag_x < 0) {
lollis_close_to_x (ev->motion.x, 20., affected_lollis);
} else if (last_drag_x < ev->motion.x) {
/* rightward, "later" motion */ /* rightward, "later" motion */
lollis_between (last_drag_x, ev->motion.x, affected_lollis); lollis_between (last_x, d.x, affected_lollis);
} else { } else {
/* leftward, "earlier" motion */ /* leftward, "earlier" motion */
lollis_between (ev->motion.x, last_drag_x, affected_lollis); lollis_between (d.x, last_x, affected_lollis);
} }
bool ret = false;
if (!affected_lollis.empty()) { if (!affected_lollis.empty()) {
int velocity = y_position_to_velocity (r.height() - (r.y1 - ev->motion.y)); int velocity = y_position_to_velocity (r.height() - (r.y1() - d.y));
drag_did_change |= mrv->set_velocity_for_notes (affected_lollis, velocity); ret = mrv->set_velocity_for_notes (affected_lollis, velocity);
mrv->mid_drag_edit (); mrv->mid_drag_edit ();
} }
if (dragging) {
dragging_line->add_point (ArdourCanvas::Duple (ev->motion.x - r.x0, ev->motion.y - r.y0)); return ret;
last_drag_x = ev->motion.x;
}
return true;
}
break;
case GDK_BUTTON_PRESS:
if (ev->button.button == 1) {
assert (!dragging);
desensitize_lollis ();
dragging = true;
drag_did_change = false;
last_drag_x = -1;
if (!dragging_line) {
dragging_line = new ArdourCanvas::PolyLine (_note_group);
dragging_line->set_ignore_events (true);
dragging_line->set_outline_color (UIConfiguration::instance().color ("midi note selected outline"));
}
dragging_line->set (ArdourCanvas::Points());
dragging_line->show();
dragging_line->raise_to_top();
base_rect->grab();
mrv->begin_drag_edit (_("draw velocities"));
return true;
}
break;
case GDK_BUTTON_RELEASE:
if (ev->button.button == 1 && dragging) {
mrv->end_drag_edit (drag_did_change);
base_rect->ungrab();
dragging_line->hide ();
dragging = false;
sensitize_lollis ();
return true;
}
break;
default:
// std::cerr << "vgr event type " << Gtkmm2ext::event_type_string (ev->type) << std::endl;
break;
} }
bool
return false; VelocityGhostRegion::base_event (GdkEvent* ev)
{
return trackview.editor().canvas_velocity_base_event (ev, base_rect);
} }
void void
@ -234,7 +197,6 @@ VelocityGhostRegion::drag_lolli (ArdourCanvas::Lollipop* l, GdkEventMotion* ev)
* (event coordinates use window coordinate space) * (event coordinates use window coordinate space)
*/ */
ev->y -= r.y0; ev->y -= r.y0;
/* clamp y to be within the range defined by the base_rect height minus /* clamp y to be within the range defined by the base_rect height minus
@ -357,6 +319,22 @@ VelocityGhostRegion::lollis_close_to_x (int x, double distance, std::vector<Note
} }
} }
void
VelocityGhostRegion::start_line_drag ()
{
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (&parent_rv);
mrv->begin_drag_edit (_("draw velocities"));
desensitize_lollis ();
}
void
VelocityGhostRegion::end_line_drag (bool did_change)
{
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (&parent_rv);
mrv->end_drag_edit (did_change);
sensitize_lollis ();
}
void void
VelocityGhostRegion::desensitize_lollis () VelocityGhostRegion::desensitize_lollis ()
{ {

View file

@ -49,6 +49,12 @@ public:
void set_selected (bool); void set_selected (bool);
bool line_draw_motion (ArdourCanvas::Duple const & d, ArdourCanvas::Rectangle const & r, double last_x);
void start_line_drag ();
void end_line_drag (bool did_change);
ArdourCanvas::Rectangle& base_item() { return *base_rect; }
private: private:
bool dragging; bool dragging;
ArdourCanvas::PolyLine* dragging_line; ArdourCanvas::PolyLine* dragging_line;