From 48a6e8dfcfae2bf8e6f87fab101beebfe83e5555 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 23 Feb 2024 11:25:07 -0700 Subject: [PATCH] basics of autoscroll for pianoroll (mostly shared with Editor) More work to do moving/testing pianoroll autoscroll variant back into EditingContext and sharing it with Editor. --- gtk2_ardour/cue_editor.cc | 12 +- gtk2_ardour/cue_editor.h | 4 +- gtk2_ardour/editing_context.cc | 90 +++++++++ gtk2_ardour/editing_context.h | 48 ++++- gtk2_ardour/editor.cc | 123 ++--------- gtk2_ardour/editor.h | 41 +--- gtk2_ardour/editor_canvas.cc | 2 +- gtk2_ardour/midi_cue_editor.cc | 359 ++++++++++++++++++++++++++++++++- gtk2_ardour/midi_cue_editor.h | 15 +- gtk2_ardour/midi_view.cc | 3 +- 10 files changed, 526 insertions(+), 171 deletions(-) diff --git a/gtk2_ardour/cue_editor.cc b/gtk2_ardour/cue_editor.cc index a4ccd917c4..a42e45641b 100644 --- a/gtk2_ardour/cue_editor.cc +++ b/gtk2_ardour/cue_editor.cc @@ -141,16 +141,6 @@ CueEditor::get_y_origin () const return 0.; } -void -CueEditor::reset_x_origin (samplepos_t) -{ -} - -void -CueEditor::reset_y_origin (double) -{ -} - void CueEditor::set_zoom_focus (Editing::ZoomFocus) { @@ -163,7 +153,7 @@ CueEditor::get_zoom_focus () const } void -CueEditor::reset_zoom (samplecnt_t n) +CueEditor::set_samples_per_pixel (samplecnt_t n) { samples_per_pixel = n; ZoomChanged(); /* EMIT SIGNAL */ diff --git a/gtk2_ardour/cue_editor.h b/gtk2_ardour/cue_editor.h index c1ef6c5111..233b70fcab 100644 --- a/gtk2_ardour/cue_editor.h +++ b/gtk2_ardour/cue_editor.h @@ -65,13 +65,11 @@ class CueEditor : public EditingContext void redo_selection_op (); double get_y_origin () const; - void reset_x_origin (samplepos_t); - void reset_y_origin (double); void set_zoom_focus (Editing::ZoomFocus); Editing::ZoomFocus get_zoom_focus () const; samplecnt_t get_current_zoom () const; - void reset_zoom (samplecnt_t); + void set_samples_per_pixel (samplecnt_t); void reposition_and_zoom (samplepos_t, double); void set_mouse_mode (Editing::MouseMode, bool force = false); diff --git a/gtk2_ardour/editing_context.cc b/gtk2_ardour/editing_context.cc index f104d34396..abe531ab0e 100644 --- a/gtk2_ardour/editing_context.cc +++ b/gtk2_ardour/editing_context.cc @@ -124,6 +124,10 @@ EditingContext::EditingContext (std::string const & name) , vertical_adjustment (0.0, 0.0, 10.0, 400.0) , horizontal_adjustment (0.0, 0.0, 1e16) , mouse_mode (MouseObject) + , visual_change_queued (false) + , autoscroll_horizontal_allowed (false) + , autoscroll_vertical_allowed (false) + , autoscroll_cnt (0) { if (!button_bindings) { button_bindings = new Bindings ("editor-mouse"); @@ -2362,3 +2366,89 @@ EditingContext::register_grid_actions () ActionManager::register_radio_action (snap_actions, grid_choice_group, X_("grid-type-none"), grid_type_strings[(int)GridTypeNone].c_str(), (sigc::bind (sigc::mem_fun(*this, &EditingContext::grid_type_chosen), Editing::GridTypeNone))); } + +void +EditingContext::ensure_visual_change_idle_handler () +{ + if (pending_visual_change.idle_handler_id < 0) { + /* see comment in add_to_idle_resize above. */ + pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL); + pending_visual_change.being_handled = false; + } +} + +int +EditingContext::_idle_visual_changer (void* arg) +{ + return static_cast(arg)->idle_visual_changer (); +} + +int +EditingContext::idle_visual_changer () +{ + pending_visual_change.idle_handler_id = -1; + + if (pending_visual_change.pending == 0) { + return 0; + } + + /* set_horizontal_position() below (and maybe other calls) call + gtk_main_iteration(), so it's possible that a signal will be handled + half-way through this method. If this signal wants an + idle_visual_changer we must schedule another one after this one, so + mark the idle_handler_id as -1 here to allow that. Also make a note + that we are doing the visual change, so that changes in response to + super-rapid-screen-update can be dropped if we are still processing + the last one. + */ + + if (visual_change_queued) { + return 0; + } + + pending_visual_change.being_handled = true; + + VisualChange vc = pending_visual_change; + + pending_visual_change.pending = (VisualChange::Type) 0; + + visual_changer (vc); + + pending_visual_change.being_handled = false; + + visual_change_queued = true; + + return 0; /* this is always a one-shot call */ +} + + +/** Queue up a change to the viewport x origin. + * @param sample New x origin. + */ +void +EditingContext::reset_x_origin (samplepos_t sample) +{ + pending_visual_change.add (VisualChange::TimeOrigin); + pending_visual_change.time_origin = sample; + ensure_visual_change_idle_handler (); +} + +void +EditingContext::reset_y_origin (double y) +{ + pending_visual_change.add (VisualChange::YOrigin); + pending_visual_change.y_origin = y; + ensure_visual_change_idle_handler (); +} + +void +EditingContext::reset_zoom (samplecnt_t spp) +{ + if (spp == samples_per_pixel) { + return; + } + + pending_visual_change.add (VisualChange::ZoomLevel); + pending_visual_change.samples_per_pixel = spp; + ensure_visual_change_idle_handler (); +} diff --git a/gtk2_ardour/editing_context.h b/gtk2_ardour/editing_context.h index ce5b32a4d3..647c2bb78f 100644 --- a/gtk2_ardour/editing_context.h +++ b/gtk2_ardour/editing_context.h @@ -269,12 +269,15 @@ public: ARDOUR::SnapPref gpref) const; virtual double get_y_origin () const = 0; - virtual void reset_x_origin (samplepos_t) = 0; - virtual void reset_y_origin (double) = 0; + + void reset_x_origin (samplepos_t); + void reset_y_origin (double); + void reset_zoom (samplecnt_t); + void set_samples_per_pixel (samplecnt_t); + virtual void on_samples_per_pixel_changed () {} virtual void set_zoom_focus (Editing::ZoomFocus) = 0; virtual Editing::ZoomFocus get_zoom_focus () const = 0; - virtual void reset_zoom (samplecnt_t) = 0; virtual void reposition_and_zoom (samplepos_t, double) = 0; sigc::signal ZoomChanged; @@ -343,7 +346,7 @@ public: virtual samplecnt_t current_page_samples() const = 0; virtual ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const = 0; - virtual ArdourCanvas::Canvas* get_canvas() const = 0; + virtual ArdourCanvas::GtkCanvas* get_canvas() const = 0; virtual size_t push_canvas_cursor (Gdk::Cursor*); virtual void pop_canvas_cursor (); @@ -563,6 +566,43 @@ public: void set_common_editing_state (XMLNode const & node); void get_common_editing_state (XMLNode& node) const; + struct VisualChange { + enum Type { + TimeOrigin = 0x1, + ZoomLevel = 0x2, + YOrigin = 0x4, + VideoTimeline = 0x8 + }; + + Type pending; + samplepos_t time_origin; + samplecnt_t samples_per_pixel; + double y_origin; + + int idle_handler_id; + /** true if we are currently in the idle handler */ + bool being_handled; + + VisualChange() : pending ((VisualChange::Type) 0), time_origin (0), samples_per_pixel (0), idle_handler_id (-1), being_handled (false) {} + void add (Type t) { + pending = Type (pending | t); + } + }; + + VisualChange pending_visual_change; + bool visual_change_queued; + + static int _idle_visual_changer (void* arg); + int idle_visual_changer (); + void ensure_visual_change_idle_handler (); + virtual void visual_changer (const VisualChange&) = 0; + + sigc::connection autoscroll_connection; + bool autoscroll_horizontal_allowed; + bool autoscroll_vertical_allowed; + uint32_t autoscroll_cnt; + ArdourCanvas::Rect autoscroll_boundary; + private: static std::queue ec_stack; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index d0c1a1dafa..cb31ac5516 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -310,7 +310,6 @@ Editor::Editor () , _full_canvas_height (0) , edit_controls_left_menu (0) , edit_controls_right_menu (0) - , visual_change_queued(false) , _tvl_no_redisplay(false) , _tvl_redisplay_on_resume(false) , _last_update_time (0) @@ -369,10 +368,6 @@ Editor::Editor () , _sections (0) , _snapshots (0) , _locations (0) - , autoscroll_horizontal_allowed (false) - , autoscroll_vertical_allowed (false) - , autoscroll_cnt (0) - , autoscroll_widget (0) , show_gain_after_trim (false) , _no_not_select_reimported_tracks (false) , selection_op_cmd_depth (0) @@ -4169,36 +4164,6 @@ Editor::get_y_origin () const return vertical_adjustment.get_value (); } -/** Queue up a change to the viewport x origin. - * @param sample New x origin. - */ -void -Editor::reset_x_origin (samplepos_t sample) -{ - pending_visual_change.add (VisualChange::TimeOrigin); - pending_visual_change.time_origin = sample; - ensure_visual_change_idle_handler (); -} - -void -Editor::reset_y_origin (double y) -{ - pending_visual_change.add (VisualChange::YOrigin); - pending_visual_change.y_origin = y; - ensure_visual_change_idle_handler (); -} - -void -Editor::reset_zoom (samplecnt_t spp) -{ - if (spp == samples_per_pixel) { - return; - } - - pending_visual_change.add (VisualChange::ZoomLevel); - pending_visual_change.samples_per_pixel = spp; - ensure_visual_change_idle_handler (); -} void Editor::reposition_and_zoom (samplepos_t sample, double fpu) @@ -4369,77 +4334,6 @@ Editor::playhead_cursor_sample () const return _playhead_cursor->current_sample(); } -void -Editor::queue_visual_videotimeline_update () -{ - pending_visual_change.add (VisualChange::VideoTimeline); - ensure_visual_change_idle_handler (); -} - -void -Editor::ensure_visual_change_idle_handler () -{ - if (pending_visual_change.idle_handler_id < 0) { - /* see comment in add_to_idle_resize above. */ - pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL); - pending_visual_change.being_handled = false; - } -} - -int -Editor::_idle_visual_changer (void* arg) -{ - return static_cast(arg)->idle_visual_changer (); -} - -void -Editor::pre_render () -{ - visual_change_queued = false; - - if (pending_visual_change.pending != 0) { - ensure_visual_change_idle_handler(); - } -} - -int -Editor::idle_visual_changer () -{ - pending_visual_change.idle_handler_id = -1; - - if (pending_visual_change.pending == 0) { - return 0; - } - - /* set_horizontal_position() below (and maybe other calls) call - gtk_main_iteration(), so it's possible that a signal will be handled - half-way through this method. If this signal wants an - idle_visual_changer we must schedule another one after this one, so - mark the idle_handler_id as -1 here to allow that. Also make a note - that we are doing the visual change, so that changes in response to - super-rapid-screen-update can be dropped if we are still processing - the last one. - */ - - if (visual_change_queued) { - return 0; - } - - pending_visual_change.being_handled = true; - - VisualChange vc = pending_visual_change; - - pending_visual_change.pending = (VisualChange::Type) 0; - - visual_changer (vc); - - pending_visual_change.being_handled = false; - - visual_change_queued = true; - - return 0; /* this is always a one-shot call */ -} - void Editor::visual_changer (const VisualChange& vc) { @@ -4497,6 +4391,23 @@ Editor::visual_changer (const VisualChange& vc) _summary->set_overlays_dirty (); } +void +Editor::queue_visual_videotimeline_update () +{ + pending_visual_change.add (VisualChange::VideoTimeline); + ensure_visual_change_idle_handler (); +} + +void +Editor::pre_render () +{ + visual_change_queued = false; + + if (pending_visual_change.pending != 0) { + ensure_visual_change_idle_handler(); + } +} + struct EditorOrderTimeAxisSorter { bool operator() (const TimeAxisView* a, const TimeAxisView* b) const { return a->order () < b->order (); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index e9e14decdb..1f7bdfac74 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -380,9 +380,6 @@ public: void restore_editing_space(); double get_y_origin () const; - void reset_x_origin (samplepos_t); - void reset_y_origin (double); - void reset_zoom (samplecnt_t); void reposition_and_zoom (samplepos_t, double); void reset_x_origin_to_follow_playhead (); @@ -476,7 +473,7 @@ public: ArdourCanvas::Container* get_drag_motion_group () const { return _drag_motion_group; } ArdourCanvas::GtkCanvasViewport* get_canvas_viewport () const; - ArdourCanvas::Canvas* get_canvas () const; + ArdourCanvas::GtkCanvas* get_canvas () const; void override_visible_track_count (); @@ -1058,38 +1055,9 @@ private: void tie_vertical_scrolling (); - struct VisualChange { - enum Type { - TimeOrigin = 0x1, - ZoomLevel = 0x2, - YOrigin = 0x4, - VideoTimeline = 0x8 - }; - - Type pending; - samplepos_t time_origin; - samplecnt_t samples_per_pixel; - double y_origin; - - int idle_handler_id; - /** true if we are currently in the idle handler */ - bool being_handled; - - VisualChange() : pending ((VisualChange::Type) 0), time_origin (0), samples_per_pixel (0), idle_handler_id (-1), being_handled (false) {} - void add (Type t) { - pending = Type (pending | t); - } - }; - - VisualChange pending_visual_change; - bool visual_change_queued; - void pre_render (); - static int _idle_visual_changer (void* arg); - int idle_visual_changer (); void visual_changer (const VisualChange&); - void ensure_visual_change_idle_handler (); /* track views */ TrackViewList track_views; @@ -1941,13 +1909,6 @@ private: /* autoscrolling */ - sigc::connection autoscroll_connection; - bool autoscroll_horizontal_allowed; - bool autoscroll_vertical_allowed; - uint32_t autoscroll_cnt; - Gtk::Widget* autoscroll_widget; - ArdourCanvas::Rect autoscroll_boundary; - bool autoscroll_canvas (); void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary); void stop_canvas_autoscroll (); diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index 18d81c2122..70bd8b49e3 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -1075,7 +1075,7 @@ Editor::get_canvas_viewport() const return _track_canvas_viewport; } -ArdourCanvas::Canvas* +ArdourCanvas::GtkCanvas* Editor::get_canvas() const { return _track_canvas_viewport->canvas(); diff --git a/gtk2_ardour/midi_cue_editor.cc b/gtk2_ardour/midi_cue_editor.cc index 8e68f4168d..5c10fe8f21 100644 --- a/gtk2_ardour/midi_cue_editor.cc +++ b/gtk2_ardour/midi_cue_editor.cc @@ -61,6 +61,7 @@ MidiCueEditor::MidiCueEditor() , bbt_metric (*this) { mouse_mode = Editing::MouseContent; + autoscroll_vertical_allowed = false; bindings = Bindings::get_bindings (editor_name()); @@ -103,7 +104,7 @@ MidiCueEditor::get_canvas_viewport() const return _canvas_viewport; } -ArdourCanvas::Canvas* +ArdourCanvas::GtkCanvas* MidiCueEditor::get_canvas() const { return _canvas; @@ -360,9 +361,9 @@ MidiCueEditor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction } void -MidiCueEditor::reset_zoom (samplecnt_t spp) +MidiCueEditor::set_samples_per_pixel (samplecnt_t spp) { - CueEditor::reset_zoom (spp); + CueEditor::set_samples_per_pixel (spp); if (view) { view->set_samples_per_pixel (spp); @@ -1037,3 +1038,355 @@ MidiCueEditor::get_state () const get_common_editing_state (*node); return *node; } + +/** @param allow_horiz true to allow horizontal autoscroll, otherwise false. + * + * @param allow_vert true to allow vertical autoscroll, otherwise false. + * + */ +void +MidiCueEditor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) +{ + if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) { + return; + } + + /* define a rectangular boundary for scrolling. If the mouse moves + * outside of this area and/or continue to be outside of this area, + * then we will continuously auto-scroll the canvas in the appropriate + * direction(s) + * + * the boundary is defined in coordinates relative to canvas' own + * window since that is what we're going to call ::get_pointer() on + * during autoscrolling to determine if we're still outside the + * boundary or not. + */ + + ArdourCanvas::Rect scrolling_boundary; + Gtk::Allocation alloc; + + alloc = get_canvas()->get_allocation (); + + if (allow_vert) { + /* reduce height by the height of the timebars, which happens + to correspond to the position of the hv_scroll_group. + */ + + alloc.set_height (alloc.get_height() - hv_scroll_group->position().y); + alloc.set_y (alloc.get_y() + hv_scroll_group->position().y); + + /* now reduce it again so that we start autoscrolling before we + * move off the top or bottom of the canvas + */ + + alloc.set_height (alloc.get_height() - 20); + alloc.set_y (alloc.get_y() + 10); + } + + /* the effective width of the autoscroll boundary so + that we start scrolling before we hit the edge. + + this helps when the window is slammed up against the + right edge of the screen, making it hard to scroll + effectively. + */ + + if (allow_horiz && (alloc.get_width() > 20)) { + alloc.set_width (alloc.get_width() - 20); + alloc.set_x (alloc.get_x() + 10); + } + + scrolling_boundary = ArdourCanvas::Rect (0., 0., alloc.get_width(), alloc.get_height()); + + int x, y; + Gdk::ModifierType mask; + + get_canvas()->get_window()->get_pointer (x, y, mask); + + if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) || + (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) { + start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary); + } +} + +bool +MidiCueEditor::autoscroll_active () const +{ + return autoscroll_connection.connected (); +} + +bool +MidiCueEditor::autoscroll_canvas () +{ + using std::max; + using std::min; + int x, y; + Gdk::ModifierType mask; + sampleoffset_t dx = 0; + bool no_stop = false; + Gtk::Window* toplevel = dynamic_cast(_canvas_viewport->get_toplevel()); + + if (!toplevel) { + return false; + } + + get_canvas()->get_window()->get_pointer (x, y, mask); + + VisualChange vc; + bool vertical_motion = false; + + if (autoscroll_horizontal_allowed) { + + samplepos_t new_sample = _leftmost_sample; + + /* horizontal */ + + if (x > autoscroll_boundary.x1) { + + /* bring it back into view */ + dx = x - autoscroll_boundary.x1; + dx += 10 + (2 * (autoscroll_cnt/2)); + + dx = pixel_to_sample (dx); + + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample < max_samplepos - dx) { + new_sample = _leftmost_sample + dx; + } else { + new_sample = max_samplepos; + } + + no_stop = true; + + } else if (x < autoscroll_boundary.x0) { + + dx = autoscroll_boundary.x0 - x; + dx += 10 + (2 * (autoscroll_cnt/2)); + + dx = pixel_to_sample (dx); + + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample >= dx) { + new_sample = _leftmost_sample - dx; + } else { + new_sample = 0; + } + + no_stop = true; + } + + if (new_sample != _leftmost_sample) { + vc.time_origin = new_sample; + vc.add (VisualChange::TimeOrigin); + } + } + + if (autoscroll_vertical_allowed) { + + // const double vertical_pos = vertical_adjustment.get_value(); + const int speed_factor = 10; + + /* vertical */ + + if (y < autoscroll_boundary.y0) { + + /* scroll to make higher tracks visible */ + + if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { + // XXX SCROLL UP + vertical_motion = true; + } + no_stop = true; + + } else if (y > autoscroll_boundary.y1) { + + if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { + // XXX SCROLL DOWN + vertical_motion = true; + } + no_stop = true; + } + + } + + if (vc.pending || vertical_motion) { + + /* change horizontal first */ + + if (vc.pending) { + visual_changer (vc); + } + + /* now send a motion event to notify anyone who cares + that we have moved to a new location (because we scrolled) + */ + + GdkEventMotion ev; + + ev.type = GDK_MOTION_NOTIFY; + ev.state = Gdk::BUTTON1_MASK; + + /* the motion handler expects events in canvas coordinate space */ + + /* we asked for the mouse position above (::get_pointer()) via + * our own top level window (we being the Editor). Convert into + * coordinates within the canvas window. + */ + + int cx; + int cy; + + //toplevel->translate_coordinates (*get_canvas(), x, y, cx, + //cy); + cx = x; + cy = y; + + /* clamp x and y to remain within the autoscroll boundary, + * which is defined in window coordinates + */ + + x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1); + y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1); + + /* now convert from Editor window coordinates to canvas + * window coordinates + */ + + ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); + ev.x = d.x; + ev.y = d.y; + ev.state = mask; + + motion_handler (0, (GdkEvent*) &ev, true); + + } else if (no_stop) { + + /* not changing visual state but pointer is outside the scrolling boundary + * so we still need to deliver a fake motion event + */ + + GdkEventMotion ev; + + ev.type = GDK_MOTION_NOTIFY; + ev.state = Gdk::BUTTON1_MASK; + + /* the motion handler expects events in canvas coordinate space */ + + /* first convert from Editor window coordinates to canvas + * window coordinates + */ + + int cx; + int cy; + + /* clamp x and y to remain within the visible area. except + * .. if horizontal scrolling is allowed, always allow us to + * move back to zero + */ + + if (autoscroll_horizontal_allowed) { + x = min (max ((ArdourCanvas::Coord) x, 0.0), autoscroll_boundary.x1); + } else { + x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1); + } + y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1); + + // toplevel->translate_coordinates (*get_canvas_viewport(), x, + // y, cx, cy); + cx = x; + cy = y; + + ArdourCanvas::Duple d = get_canvas()->window_to_canvas (ArdourCanvas::Duple (cx, cy)); + ev.x = d.x; + ev.y = d.y; + ev.state = mask; + + motion_handler (0, (GdkEvent*) &ev, true); + + } else { + stop_canvas_autoscroll (); + return false; + } + + autoscroll_cnt++; + + return true; /* call me again */ +} + +void +MidiCueEditor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary) +{ + if (!_session) { + return; + } + + stop_canvas_autoscroll (); + + autoscroll_horizontal_allowed = allow_horiz; + autoscroll_vertical_allowed = allow_vert; + autoscroll_boundary = boundary; + + /* do the first scroll right now + */ + + autoscroll_canvas (); + + /* scroll again at very very roughly 30FPS */ + + autoscroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &MidiCueEditor::autoscroll_canvas), 30); +} + +void +MidiCueEditor::stop_canvas_autoscroll () +{ + autoscroll_connection.disconnect (); + autoscroll_cnt = 0; +} + +void +MidiCueEditor::visual_changer (const VisualChange& vc) +{ + /** + * Changed first so the correct horizontal canvas position is calculated in + * EditingContext::set_horizontal_position + */ + if (vc.pending & VisualChange::ZoomLevel) { + set_samples_per_pixel (vc.samples_per_pixel); + } + + if (vc.pending & VisualChange::TimeOrigin) { + double new_time_origin = sample_to_pixel_unrounded (vc.time_origin); + set_horizontal_position (new_time_origin); + } + + if (vc.pending & VisualChange::YOrigin) { + vertical_adjustment.set_value (vc.y_origin); + } + + /** + * Now the canvas is in the final state before render the canvas items that + * support the Item::prepare_for_render interface can calculate the correct + * item to visible canvas intersection. + */ + if (vc.pending & VisualChange::ZoomLevel) { + on_samples_per_pixel_changed (); + + // update_tempo_based_rulers (); + } + + if (!(vc.pending & VisualChange::ZoomLevel)) { + /* If the canvas is not being zoomed then the canvas items will not change + * and cause Item::prepare_for_render to be called so do it here manually. + * Not ideal, but I can't think of a better solution atm. + */ + get_canvas()->prepare_for_render(); + } + + /* If we are only scrolling vertically there is no need to update these */ + if (vc.pending != VisualChange::YOrigin) { + // XXX update_fixed_rulers (); + // XXX redisplay_grid (true); + } +} diff --git a/gtk2_ardour/midi_cue_editor.h b/gtk2_ardour/midi_cue_editor.h index 38e5cd0ce4..a62aabd84e 100644 --- a/gtk2_ardour/midi_cue_editor.h +++ b/gtk2_ardour/midi_cue_editor.h @@ -75,7 +75,7 @@ class MidiCueEditor : public CueEditor ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; } ArdourCanvas::ScrollGroup* get_cursor_scroll_group () const { return cursor_scroll_group; } - void reset_zoom (samplecnt_t); + void set_samples_per_pixel (samplecnt_t); void set_mouse_mode (Editing::MouseMode, bool force = false); void step_mouse_mode (bool next); @@ -86,11 +86,14 @@ class MidiCueEditor : public CueEditor size_t n_timebars; ArdourCanvas::GtkCanvasViewport* get_canvas_viewport() const; - ArdourCanvas::Canvas* get_canvas() const; + ArdourCanvas::GtkCanvas* get_canvas() const; int set_state (const XMLNode&, int version); XMLNode& get_state () const; + void maybe_autoscroll (bool, bool, bool); + bool autoscroll_active() const; + protected: void register_actions (); @@ -177,6 +180,14 @@ class MidiCueEditor : public CueEditor bool canvas_pre_event (GdkEvent*); void setup_toolbar (); + + /* autoscrolling */ + + bool autoscroll_canvas (); + void start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary); + void stop_canvas_autoscroll (); + + void visual_changer (const VisualChange&); }; diff --git a/gtk2_ardour/midi_view.cc b/gtk2_ardour/midi_view.cc index 42634082df..ecdb9ddfd6 100644 --- a/gtk2_ardour/midi_view.cc +++ b/gtk2_ardour/midi_view.cc @@ -141,7 +141,8 @@ MidiView::MidiView (std::shared_ptr mt, MidiView::MidiView (MidiView const & other) - : _midi_track (other._midi_track) + : sigc::trackable () + , _midi_track (other._midi_track) , _editing_context (other.editing_context()) , _midi_context (other.midi_context()) , _midi_region (other.midi_region())