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_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*);
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_selection_rect_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
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);
}
bool
Editor::canvas_velocity_base_event (GdkEvent *event, ArdourCanvas::Item* item)
{
return typed_event (item, event, VelocityBaseItem);
}
bool
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)
, _primary (dynamic_cast<ArdourCanvas::Lollipop*> (l))
{
DEBUG_TRACE (DEBUG::Drags, "New LollipopDrag\n");
_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 ());
}
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)
, base_rect (r)
, dragging_line (nullptr)
@ -7269,13 +7273,15 @@ AutomationDrawDrag::AutomationDrawDrag (Editor* editor, ArdourCanvas::Rectangle&
DEBUG_TRACE (DEBUG::Drags, "New AutomationDrawDrag\n");
}
AutomationDrawDrag::~AutomationDrawDrag ()
template<typename OrderedPointList, typename OrderedPoint>
FreehandLineDrag<OrderedPointList,OrderedPoint>::~FreehandLineDrag ()
{
delete dragging_line;
}
template<typename OrderedPointList, typename OrderedPoint>
void
AutomationDrawDrag::motion (GdkEvent* ev, bool first_move)
FreehandLineDrag<OrderedPointList,OrderedPoint>::motion (GdkEvent* ev, bool first_move)
{
if (first_move) {
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 */
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
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);
@ -7347,24 +7354,91 @@ AutomationDrawDrag::maybe_add_point (GdkEvent* ev, timepos_t const & cpos)
}
}
bool child_call = false;
if (pop_point) {
if (line_break_pending) {
line_break_pending = false;
} else {
dragging_line->pop_back();
drawn_points.pop_back ();
child_call = true;
}
}
if (add_point) {
if (drawn_points.empty() || (pos != drawn_points.back().when)) {
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;
}
}
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
AutomationDrawDrag::finished (GdkEvent* event, bool motion_occured)
{
@ -7384,37 +7458,58 @@ AutomationDrawDrag::finished (GdkEvent* event, bool motion_occured)
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());
}
FreehandLineDrag<Evoral::ControlList::OrderedPoints,Evoral::ControlList::OrderedPoint>::finished (event, motion_occured);
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
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
AutomationDrawDrag::mid_drag_key_event (GdkEventKey* ev)
void
VelocityLineDrag::point_added (Duple const & d, Rectangle const & r, double last_x)
{
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;
}
drag_did_change |= grv->line_draw_motion (d, r, last_x);
}
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;
};
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:
AutomationDrawDrag (Editor*, ArdourCanvas::Rectangle&, Temporal::TimeDomain);
~AutomationDrawDrag ();
void motion (GdkEvent*, bool);
void finished (GdkEvent*, 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 &);
void aborted (bool) {}
};
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_ */

View file

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

View file

@ -82,6 +82,7 @@
#include "mouse_cursors.h"
#include "editor_cursors.h"
#include "region_peak_cursor.h"
#include "velocity_ghost_region.h"
#include "verbose_cursor.h"
#include "note.h"
@ -902,6 +903,16 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
return true;
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:
break;
}
@ -1569,6 +1580,7 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT
/* see if we're finishing a drag */
if (_drags->active ()) {
bool const r = _drags->end_grab (event);
if (r) {
/* 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_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) = 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_selection_rect_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)
, selected (false)
{
base_rect->set_data (X_("ghostregionview"), this);
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_outline_color (UIConfiguration::instance().color ("automation track outline"));
@ -70,74 +71,36 @@ VelocityGhostRegion::~VelocityGhostRegion ()
}
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;
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (&parent_rv);
ArdourCanvas::Rect r = base_rect->item_to_canvas (base_rect->get());
switch (ev->type) {
case GDK_MOTION_NOTIFY:
if (dragging) {
if (last_drag_x < 0) {
lollis_close_to_x (ev->motion.x, 20., affected_lollis);
} else if (last_drag_x < ev->motion.x) {
if (last_x < 0) {
lollis_close_to_x (d.x, 20., affected_lollis);
} else if (last_x < d.x) {
/* rightward, "later" motion */
lollis_between (last_drag_x, ev->motion.x, affected_lollis);
lollis_between (last_x, d.x, affected_lollis);
} else {
/* 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()) {
int velocity = y_position_to_velocity (r.height() - (r.y1 - ev->motion.y));
drag_did_change |= mrv->set_velocity_for_notes (affected_lollis, velocity);
int velocity = y_position_to_velocity (r.height() - (r.y1() - d.y));
ret = mrv->set_velocity_for_notes (affected_lollis, velocity);
mrv->mid_drag_edit ();
}
if (dragging) {
dragging_line->add_point (ArdourCanvas::Duple (ev->motion.x - r.x0, ev->motion.y - r.y0));
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;
return ret;
}
return false;
bool
VelocityGhostRegion::base_event (GdkEvent* ev)
{
return trackview.editor().canvas_velocity_base_event (ev, base_rect);
}
void
@ -234,7 +197,6 @@ VelocityGhostRegion::drag_lolli (ArdourCanvas::Lollipop* l, GdkEventMotion* ev)
* (event coordinates use window coordinate space)
*/
ev->y -= r.y0;
/* 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
VelocityGhostRegion::desensitize_lollis ()
{

View file

@ -49,6 +49,12 @@ public:
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:
bool dragging;
ArdourCanvas::PolyLine* dragging_line;