From 9f76533f76a544d15f979f4b39997d1bfb63329e Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 9 Feb 2024 11:29:23 -0700 Subject: [PATCH] basically functioning piano roll for midi cue editor --- gtk2_ardour/midi_cue_editor.cc | 22 +++- gtk2_ardour/midi_cue_editor.h | 1 + gtk2_ardour/prh.cc | 184 +++++++++++++++++++++------------ gtk2_ardour/prh.h | 3 + 4 files changed, 140 insertions(+), 70 deletions(-) diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index 116a7344a1..94f96f531d 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -50,8 +50,9 @@ using namespace Temporal; MidiCueEditor::MidiCueEditor() : timebar_height (15.) , n_timebars (3) - , view (nullptr) , prh (nullptr) + , bg (nullptr) + , view (nullptr) , mouse_mode (Editing::MouseContent) , bbt_metric (*this) { @@ -118,6 +119,11 @@ MidiCueEditor::build_canvas () CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll"); _canvas->add_scroller (*h_scroll_group); + + v_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically); + CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll"); + _canvas->add_scroller (*v_scroll_group); + hv_scroll_group = new ArdourCanvas::ScrollGroup (_canvas->root(), ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically| ArdourCanvas::ScrollGroup::ScrollsHorizontally)); @@ -177,7 +183,6 @@ MidiCueEditor::build_canvas () CANVAS_DEBUG_NAME (bbt_ruler, "cue bbt ruler"); data_group = new ArdourCanvas::Container (hv_scroll_group); - data_group->move (ArdourCanvas::Duple (30, timebar_height * n_timebars)); CANVAS_DEBUG_NAME (data_group, "cue data group"); bg = new CueMidiBackground (data_group); @@ -314,7 +319,16 @@ MidiCueEditor::set_region (std::shared_ptr t, std::shared_ptr bg->set_view (view); delete prh; - prh = new ArdourCanvas::PianoRollHeader (data_group, *view); + prh = new ArdourCanvas::PianoRollHeader (v_scroll_group, *view); + + double w, h; + prh->size_request (w, h); + + /* Move stuff around */ + + prh->move (Duple (0., n_timebars * timebar_height)); + data_group->move (ArdourCanvas::Duple (w, timebar_height * n_timebars)); + h_scroll_group->move (Duple (w, 0.)); /* Compute zoom level to show entire source plus some margin if possible */ @@ -337,8 +351,6 @@ MidiCueEditor::set_region (std::shared_ptr t, std::shared_ptr double width = bg->width(); samplecnt_t samples = duration.samples(); - std::cerr << "region is " << samples << " long\n"; - samplecnt_t spp = floor (samples / width); reset_zoom (spp); } diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index dcd2ecf57c..e317ae73df 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -114,6 +114,7 @@ class MidiCueEditor : public CueEditor /* The group containing all other groups that are scrolled horizontally ONLY */ ArdourCanvas::ScrollGroup* h_scroll_group; + ArdourCanvas::ScrollGroup* v_scroll_group; /* Scroll group for cursors, scrolled horizontally, above everything else */ diff --git a/gtk2_ardour/prh.cc b/gtk2_ardour/prh.cc index 3c85f5b4b9..9692d0c81e 100644 --- a/gtk2_ardour/prh.cc +++ b/gtk2_ardour/prh.cc @@ -90,13 +90,69 @@ PianoRollHeader::PianoRollHeader (Item* parent, MidiView& v) _view.midi_context().NoteRangeChanged.connect (sigc::mem_fun (*this, &PianoRollHeader::note_range_changed)); + double w, h; + size_request (w, h); + + set (Rect (0., 0., w, h)); + + /* draw vertical lines on both sides of the rectangle */ + set_outline_color (0x000000ff); /* XXX theme me */ + set_outline_what (Rectangle::What (Rectangle::LEFT|Rectangle::RIGHT)); + + Event.connect (sigc::mem_fun (*this, &PianoRollHeader::event_handler)); +} + +void +PianoRollHeader::size_request (double& w, double& h) const +{ + h = _view.midi_context().contents_height(); + if (show_scroomer()) { - _scroomer_size = 60.f * UIConfiguration::instance().get_ui_scale(); + w = 60.f * UIConfiguration::instance().get_ui_scale(); } else { - _scroomer_size = 20.f * UIConfiguration::instance().get_ui_scale(); + w = 20.f * UIConfiguration::instance().get_ui_scale(); } - set (Rect (0., 0., _scroomer_size + 20., 120.)); + w += 20.; +} + +void +PianoRollHeader::size_allocate (ArdourCanvas::Rect const &r) +{ + _alloc = r; +} + +bool +PianoRollHeader::event_handler (GdkEvent* ev) +{ + /* Remember that ev uses canvas coordinates, not item */ + + switch (ev->type) { + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + return button_press_handler (&ev->button); + + case GDK_BUTTON_RELEASE: + return button_release_handler (&ev->button); + + case GDK_ENTER_NOTIFY: + return enter_handler (&ev->crossing); + + case GDK_LEAVE_NOTIFY: + return leave_handler (&ev->crossing); + + case GDK_SCROLL: + return scroll_handler (&ev->scroll); + + case GDK_MOTION_NOTIFY: + return motion_handler (&ev->motion); + + default: + break; + } + + return false; } inline void @@ -110,26 +166,18 @@ create_path (Cairo::RefPtr cr, double x[], double y[], int start } inline void -render_rect(Cairo::RefPtr cr, int note, double x[], double y[], - Gtkmm2ext::Color& bg) +render_rect(Cairo::RefPtr cr, int note, double x[], double y[], Gtkmm2ext::Color& bg) { - set_source_rgba(cr, bg); - create_path(cr, x, y, 0, 4); - cr->fill(); -} - -void -PianoRollHeader::size_allocate (ArdourCanvas::Rect const &r) -{ - _alloc = r; + set_source_rgba (cr, bg); + create_path (cr, x, y, 0, 4); + cr->fill (); } void PianoRollHeader::render_scroomer(Cairo::RefPtr cr) const { - double scroomer_top = max (1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * _alloc.height () ); - double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * _alloc.height (); - double scroomer_width = _scroomer_size; + double scroomer_top = max (1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * get().height () ); + double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * get().height (); Gtkmm2ext::Color c = UIConfiguration::instance().color_mod (X_("scroomer"), X_("scroomer alpha")); Gtkmm2ext::Color save_color (c); @@ -140,8 +188,8 @@ PianoRollHeader::render_scroomer(Cairo::RefPtr cr) const set_source_rgba (cr, c); cr->move_to (1.f, scroomer_top); - cr->line_to (scroomer_width - 1.f, scroomer_top); - cr->line_to (scroomer_width - 1.f, scroomer_bottom); + cr->line_to (_scroomer_size - 1.f, scroomer_top); + cr->line_to (_scroomer_size - 1.f, scroomer_bottom); cr->line_to (1.f, scroomer_bottom); cr->line_to (1.f, scroomer_top); cr->fill(); @@ -152,9 +200,9 @@ PianoRollHeader::render_scroomer(Cairo::RefPtr cr) const set_source_rgba (cr, c); cr->set_line_width (4.); cr->move_to (1.f, scroomer_top + 2.); - cr->line_to (scroomer_width - 1.f, scroomer_top + 2.); + cr->line_to (_scroomer_size - 1.f, scroomer_top + 2.); cr->stroke (); - cr->line_to (scroomer_width - 1.f, scroomer_bottom - 2.); + cr->line_to (_scroomer_size - 1.f, scroomer_bottom - 2.); cr->line_to (2.f, scroomer_bottom - 2.); cr->stroke (); cr->restore (); @@ -191,7 +239,8 @@ PianoRollHeader::scroll_handler (GdkEventScroll* ev) } } - set_note_highlight (_view.midi_context().y_to_note (ev->y)); + Duple evd (canvas_to_item (Duple (ev->x, ev->y))); + set_note_highlight (_view.midi_context().y_to_note (evd.y)); _adj.value_changed (); redraw (); @@ -202,11 +251,10 @@ PianoRollHeader::scroll_handler (GdkEventScroll* ev) void PianoRollHeader::get_path (int note, double x[], double y[]) const { - double scroomer_size = _scroomer_size; double y_pos = floor(_view.midi_context().note_to_y(note)); double note_height; _raw_note_height = floor(_view.midi_context().note_to_y(note - 1)) - y_pos; - double width = _alloc.width() - 1.0f; + double width = get().width() - 1.0f; if (note == 0) { note_height = floor(_view.midi_context().contents_height()) - y_pos; @@ -214,10 +262,10 @@ PianoRollHeader::get_path (int note, double x[], double y[]) const note_height = _raw_note_height <= 3 ? _raw_note_height : _raw_note_height - 1.f; } - x[0] = scroomer_size; + x[0] = _scroomer_size; y[0] = y_pos + note_height; - x[1] = scroomer_size; + x[1] = _scroomer_size; y[1] = y_pos; x[2] = width; @@ -226,9 +274,8 @@ PianoRollHeader::get_path (int note, double x[], double y[]) const x[3] = width; y[3] = y_pos + note_height; - x[4] = scroomer_size; - y[4] = y_pos + note_height; - return; + x[4] = x[0]; + y[4] = y[0]; } void @@ -239,9 +286,19 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtrsave (); + // cr->translate (origin.x, origin.y); + + Rect self (item_to_window (get())); + + double y1 = max (self.y0, 0.); + double y2 = min (self.y1, (ArdourCanvas::Coord) floor(_view.midi_context().contents_height())); + double av_note_height = _view.midi_context().note_height(); int bc_height, bc_width; //Reduce the frequency of Pango layout resizing @@ -284,21 +341,13 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtrset_source_rgb(0.0f, 0.0f, 0.0f); - cr->move_to(0.f, area.y0); - cr->line_to(0.f, area.y1); - cr->stroke(); - cr->move_to(_alloc.width(),area.y0); - cr->line_to(_alloc.width(), area.y1); - cr->stroke(); // Render the MIDNAM text or its equivalent. First, set up a clip // region so that the text doesn't spill, regardless of its length. cr->save(); - cr->rectangle (0,0,_scroomer_size, _alloc.height () ); + cr->rectangle (0,0,_scroomer_size, get().height () ); cr->clip(); if (show_scroomer()) { @@ -312,8 +361,8 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtrset_text (note.name); - set_source_rgba(cr, textc); - cr->move_to(2.f, y); + set_source_rgba (cr, textc); + cr->move_to (2.f, y); if (!_mini_map_display) { _midnam_layout->show_in_cairo_context (cr); @@ -342,13 +391,13 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtradd_color_stop_rgba (0,r,g,b,0); gradient_ptr->add_color_stop_rgba (1,r,g,b,1); cr->set_source (gradient_ptr); - cr->rectangle (_scroomer_size - fade_width, 0, _scroomer_size, _alloc.height () ); + cr->rectangle (_scroomer_size - fade_width, 0, _scroomer_size, get().height () ); cr->fill(); } /* Now draw the semi-transparent scroomer over the top */ - render_scroomer(cr); + render_scroomer (cr); /* Done with clip region */ @@ -450,6 +499,9 @@ PianoRollHeader::render (ArdourCanvas::Rect const & area, Cairo::RefPtrwindow */ + cr->restore (); } void @@ -558,15 +610,17 @@ PianoRollHeader::get_note_name (int note) bool PianoRollHeader::motion_handler (GdkEventMotion* ev) { + Duple evd (canvas_to_item (Duple (ev->x, ev->y))); + if (!_scroomer_drag && ev->x < _scroomer_size){ Gdk::Cursor m_Cursor; - double scroomer_top = max(1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * _alloc.height()); - double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * _alloc.height(); - if (ev->y > scroomer_top - 5 && ev->y < scroomer_top + 5){ + double scroomer_top = max(1.0, (1.0 - ((_adj.get_value()+_adj.get_page_size()) / 127.0)) * get().height()); + double scroomer_bottom = (1.0 - (_adj.get_value () / 127.0)) * get().height(); + if (evd.y > scroomer_top - 5 && evd.y < scroomer_top + 5){ m_Cursor = Gdk::Cursor (Gdk::TOP_SIDE); // XXX _canvas->set_cursor(m_Cursor); _scroomer_state = TOP; - }else if (ev->y > scroomer_bottom - 5 && ev->y < scroomer_bottom + 5){ + }else if (evd.y > scroomer_bottom - 5 && evd.y < scroomer_bottom + 5){ m_Cursor = Gdk::Cursor (Gdk::BOTTOM_SIDE); // XXXX _canvas->set_cursor(m_Cursor); _scroomer_state = BOTTOM; @@ -577,10 +631,10 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev) } if (_scroomer_drag){ - double pixel2val = 127.0 / _alloc.height(); - double delta = _old_y - ev->y; + double pixel2val = 127.0 / get().height(); + double delta = _old_y - evd.y; double val_at_pointer = (delta * pixel2val); - double real_val_at_pointer = 127.0 - (ev->y * pixel2val); + double real_val_at_pointer = 127.0 - (evd.y * pixel2val); double note_range = _adj.get_page_size (); switch (_scroomer_button_state){ @@ -616,7 +670,7 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev) break; } }else{ - int note = _view.midi_context().y_to_note(ev->y); + int note = _view.midi_context().y_to_note(evd.y); set_note_highlight (note); if (_dragging) { @@ -648,7 +702,7 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev) } _adj.value_changed (); redraw (); - _old_y = ev->y; + _old_y = evd.y; //win->process_updates(false); return true; @@ -657,28 +711,25 @@ PianoRollHeader::motion_handler (GdkEventMotion* ev) bool PianoRollHeader::button_press_handler (GdkEventButton* ev) { + Duple evd (canvas_to_item (Duple (ev->x, ev->y))); + _scroomer_button_state = _scroomer_state; if (ev->button == 1 && ev->x <= _scroomer_size){ if (ev->type == GDK_2BUTTON_PRESS) { -#if 0 - MidiTimeAxisView* mtv = dynamic_cast(&_view.trackview()); - if (mtv) { - mtv->set_note_range (MidiStreamView::ContentsRange, false); - } -#endif + // _view.set_note_range (MidiStreamView::ContentsRange, false); return true; } _scroomer_drag = true; - _old_y = ev->y; + _old_y = evd.y; _fract = _adj.get_value(); _fract_top = _adj.get_value() + _adj.get_page_size(); return true; } else { - int note = _view.midi_context().y_to_note(ev->y); + int note = _view.midi_context().y_to_note(evd.y); bool tertiary = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier); bool primary = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier); @@ -721,10 +772,12 @@ PianoRollHeader::button_press_handler (GdkEventButton* ev) bool PianoRollHeader::button_release_handler (GdkEventButton* ev) { + Duple evd (canvas_to_item (Duple (ev->x, ev->y))); + if (_scroomer_drag){ _scroomer_drag = false; } - int note = _view.midi_context().y_to_note(ev->y); + int note = _view.midi_context().y_to_note(evd.y); if (false /*editor().current_mouse_mode() == Editing::MouseRange*/) { //Todo: this mode is buggy, and of questionable utility anyway @@ -775,7 +828,8 @@ PianoRollHeader::set_note_highlight (uint8_t note) bool PianoRollHeader::enter_handler (GdkEventCrossing* ev) { - set_note_highlight (_view.midi_context().y_to_note (ev->y)); + Duple evd (canvas_to_item (Duple (ev->x, ev->y))); + set_note_highlight (_view.midi_context().y_to_note (evd.y)); entered = true; redraw (); return true; @@ -816,7 +870,7 @@ PianoRollHeader::invalidate_note_range (int lowest, int highest) double y = _view.midi_context().note_to_y (highest); double height = _view.midi_context().note_to_y (lowest - 1) - y; - dynamic_cast(_canvas)->queue_draw_area (0., floor (y), _alloc.width(), floor (height)); + dynamic_cast(_canvas)->queue_draw_area (0., floor (y), get().width(), floor (height)); } bool diff --git a/gtk2_ardour/prh.h b/gtk2_ardour/prh.h index cd3fefa030..f0aef0b12f 100644 --- a/gtk2_ardour/prh.h +++ b/gtk2_ardour/prh.h @@ -41,6 +41,8 @@ class PianoRollHeader : public ArdourCanvas::Rectangle { public: PianoRollHeader (ArdourCanvas::Item* parent, MidiView&); + void size_request (double& w, double& h) const; + void render (ArdourCanvas::Rect const & area, Cairo::RefPtr) const; void size_allocate (ArdourCanvas::Rect const &); @@ -56,6 +58,7 @@ public: private: + bool event_handler (GdkEvent*); bool motion_handler (GdkEventMotion*); bool button_press_handler (GdkEventButton*); bool button_release_handler (GdkEventButton*);