X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_canvas.cc;h=56571c3de886e7c36d52c6fd0bd69f784bb3c820;hb=24df2b559464f981e2c6a91719e3cfe2a76a197c;hp=ce20b841ad7b96bc59224bf068022ecaaef20b7a;hpb=0796ccfb652dd31ea1d94526e0e6d92863a972b0;p=ardour.git diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index ce20b841ad..56571c3de8 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -27,6 +27,8 @@ #include "ardour/rc_configuration.h" #include "ardour/smf_source.h" +#include "pbd/error.h" + #include "canvas/canvas.h" #include "canvas/rectangle.h" #include "canvas/pixbuf.h" @@ -37,7 +39,6 @@ #include "ardour_ui.h" #include "automation_time_axis.h" #include "editor.h" -#include "global_signals.h" #include "editing.h" #include "rgb_macros.h" #include "utils.h" @@ -50,12 +51,15 @@ #include "keyboard.h" #include "editor_cursors.h" #include "mouse_cursors.h" +#include "note_base.h" +#include "ui_config.h" #include "verbose_cursor.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; using namespace Glib; @@ -68,40 +72,36 @@ Editor::initialize_canvas () _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment); _track_canvas = _track_canvas_viewport->canvas (); - ArdourCanvas::ScrollGroup* hsg; - ArdourCanvas::ScrollGroup* hg; - ArdourCanvas::ScrollGroup* vg; + _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base")); + _track_canvas->use_nsglview (); - hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), - ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically| - ArdourCanvas::ScrollGroup::ScrollsHorizontally)); - CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll"); - _track_canvas->add_scroller (*hsg); + /* scroll group for items that should not automatically scroll + * (e.g verbose cursor). It shares the canvas coordinate space. + */ + no_scroll_group = new ArdourCanvas::Container (_track_canvas->root()); - v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically); - CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll"); - _track_canvas->add_scroller (*vg); + ArdourCanvas::ScrollGroup* hsg; + ArdourCanvas::ScrollGroup* hg; + ArdourCanvas::ScrollGroup* cg; h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll"); _track_canvas->add_scroller (*hg); - _verbose_cursor = new VerboseCursor (this); + hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), + ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically| + ArdourCanvas::ScrollGroup::ScrollsHorizontally)); + CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll"); + _track_canvas->add_scroller (*hsg); - /* on the bottom, an image */ + cursor_scroll_group = cg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally); + CANVAS_DEBUG_NAME (cursor_scroll_group, "canvas cursor scroll"); + _track_canvas->add_scroller (*cg); - if (Profile->get_sae()) { - Image img (::get_icon (X_("saelogo"))); - // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf()); - // logo_item->property_height_in_pixels() = true; - // logo_item->property_width_in_pixels() = true; - // logo_item->property_height_set() = true; - // logo_item->property_width_set() = true; - // logo_item->show (); - } + _verbose_cursor = new VerboseCursor (this); /*a group to hold global rects like punch/loop indicators */ - global_rect_group = new ArdourCanvas::Layout (hv_scroll_group); + global_rect_group = new ArdourCanvas::Container (hv_scroll_group); CANVAS_DEBUG_NAME (global_rect_group, "global rect group"); transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX)); @@ -113,16 +113,11 @@ Editor::initialize_canvas () transport_punch_range_rect->hide(); /*a group to hold time (measure) lines */ - time_line_group = new ArdourCanvas::Layout (hv_scroll_group); + time_line_group = new ArdourCanvas::Container (h_scroll_group); CANVAS_DEBUG_NAME (time_line_group, "time line group"); - _trackview_group = new ArdourCanvas::Layout (hv_scroll_group); + _trackview_group = new ArdourCanvas::Container (hv_scroll_group); CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews"); - - // used to show zoom mode active zooming - zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0)); - zoom_rect->hide(); - zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0)); // used as rubberband rect rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0)); @@ -131,30 +126,30 @@ Editor::initialize_canvas () /* a group to hold stuff while it gets dragged around. Must be the * uppermost (last) group with hv_scroll_group as a parent */ - _drag_motion_group = new ArdourCanvas::Layout (hv_scroll_group); - CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion"); + _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group); + CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion"); /* TIME BAR CANVAS */ - - _time_markers_group = new ArdourCanvas::Layout (h_scroll_group); + + _time_markers_group = new ArdourCanvas::Container (h_scroll_group); CANVAS_DEBUG_NAME (_time_markers_group, "time bars"); - cd_marker_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0)); + cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0)); CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group"); /* the vide is temporarily placed a the same location as the cd_marker_group, but is moved later. */ - videotl_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0)); + videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0)); CANVAS_DEBUG_NAME (videotl_group, "videotl group"); - marker_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0)); + marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0)); CANVAS_DEBUG_NAME (marker_group, "marker group"); - transport_marker_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0)); + transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0)); CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group"); - range_marker_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0)); + range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0)); CANVAS_DEBUG_NAME (range_marker_group, "range marker group"); - tempo_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0)); + tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0)); CANVAS_DEBUG_NAME (tempo_group, "tempo group"); - meter_group = new ArdourCanvas::Layout (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0)); + meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0)); CANVAS_DEBUG_NAME (meter_group, "meter group"); meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); @@ -179,10 +174,10 @@ Editor::initialize_canvas () cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar"); - cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); + cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height)); - + cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height)); CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag"); cd_marker_bar_drag_rect->set_outline (false); @@ -222,11 +217,6 @@ Editor::initialize_canvas () playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event); - if (logo_item) { - logo_item->lower_to_bottom (); - } - - _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0)); /* this thing is transparent */ _canvas_drop_zone->set_fill (false); @@ -251,6 +241,8 @@ Editor::initialize_canvas () _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false); _track_canvas->set_flags (CAN_FOCUS); + _track_canvas->PreRender.connect (sigc::mem_fun(*this, &Editor::pre_render)); + /* set up drag-n-drop */ vector target_table; @@ -269,7 +261,7 @@ Editor::initialize_canvas () initialize_rulers (); - ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler)); + UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler)); color_handler(); } @@ -319,23 +311,23 @@ Editor::track_canvas_viewport_size_allocated () void Editor::reset_controls_layout_width () { - GtkRequisition req; + GtkRequisition req = { 0, 0 }; gint w; edit_controls_vbox.size_request (req); w = req.width; - if (_group_tabs->is_mapped()) { + if (_group_tabs->is_visible()) { _group_tabs->size_request (req); - w += req.width; - } + w += req.width; + } - /* the controls layout has no horizontal scrolling, its visible - width is always equal to the total width of its contents. - */ + /* the controls layout has no horizontal scrolling, its visible + width is always equal to the total width of its contents. + */ - controls_layout.property_width() = w; - controls_layout.property_width_request() = w; + controls_layout.property_width() = w; + controls_layout.property_width_request() = w; } void @@ -353,19 +345,21 @@ Editor::reset_controls_layout_height (int32_t h) h += _canvas_drop_zone->height (); - /* set the height of the scrollable area (i.e. the sum of all contained widgets) + /* set the height of the scrollable area (i.e. the sum of all contained widgets) * for the controls layout. The size request is set elsewhere. - */ + */ - controls_layout.property_height() = h; + controls_layout.property_height() = h; } bool Editor::track_canvas_map_handler (GdkEventAny* /*ev*/) { - if (current_canvas_cursor) { - set_canvas_cursor (current_canvas_cursor); + if (!_cursor_stack.empty()) { + set_canvas_cursor (get_canvas_cursor()); + } else { + PBD::error << "cursor stack is empty" << endmsg; } return false; } @@ -395,7 +389,7 @@ void Editor::drop_paths_part_two (const vector& paths, framepos_t frame, double ypos, bool copy) { RouteTimeAxisView* tv; - + /* MIDI files must always be imported, because we consider them * writable. So split paths into two vectors, and follow the import * path on the MIDI part. @@ -419,11 +413,12 @@ Editor::drop_paths_part_two (const vector& paths, framepos_t frame, doub /* drop onto canvas background: create new tracks */ frame = 0; + InstrumentSelector is; // instantiation builds instrument-list and sets default. + do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, frame, is.selected_instrument()); - do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame); - - if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) { - do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame); + if (UIConfiguration::instance().get_only_copy_imported_files() || copy) { + do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, + SrcBest, SMFTrackName, SMFTempoIgnore, frame); } else { do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame); } @@ -436,10 +431,12 @@ Editor::drop_paths_part_two (const vector& paths, framepos_t frame, doub /* select the track, then embed/import */ selection->set (tv); - do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame); + do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, + SrcBest, SMFTrackName, SMFTempoIgnore, frame); - if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) { - do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame); + if (UIConfiguration::instance().get_only_copy_imported_files() || copy) { + do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, + SrcBest, SMFTrackName, SMFTempoIgnore, frame); } else { do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame); } @@ -455,7 +452,6 @@ Editor::drop_paths (const RefPtr& context, { vector paths; GdkEvent ev; - framepos_t frame; double cy; if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) { @@ -467,19 +463,18 @@ Editor::drop_paths (const RefPtr& context, ev.button.x = x; ev.button.y = y; - frame = window_event_sample (&ev, 0, &cy); - - snap_to (frame); + MusicFrame when (window_event_sample (&ev, 0, &cy), 0); + snap_to (when); bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY); -#ifdef GTKOSX +#ifdef __APPLE__ /* We are not allowed to call recursive main event loops from within the main event loop with GTK/Quartz. Since import/embed wants to push up a progress dialog, defer all this till we go idle. */ - Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy)); + Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.frame, cy, copy)); #else - drop_paths_part_two (paths, frame, cy, copy); + drop_paths_part_two (paths, when.frame, cy, copy); #endif } @@ -494,7 +489,13 @@ Editor::drop_paths (const RefPtr& context, void Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) { - if (!Config->get_autoscroll_editor () || autoscroll_active ()) { + Gtk::Window* toplevel = dynamic_cast(contents().get_toplevel()); + + if (!toplevel) { + return; + } + + if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) { return; } @@ -511,19 +512,26 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) ArdourCanvas::Rect scrolling_boundary; Gtk::Allocation alloc; - int cx, cy; if (from_headers) { alloc = controls_layout.get_allocation (); - } else { + + int wx, wy; + + controls_layout.get_parent()->translate_coordinates (*toplevel, + alloc.get_x(), alloc.get_y(), + wx, wy); + + scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height()); + + + } else { alloc = _track_canvas_viewport->get_allocation (); - cx = alloc.get_x(); - cy = alloc.get_y(); /* 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); @@ -536,25 +544,30 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) /* 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 (alloc.get_width() > 20) { + + if (alloc.get_width() > 20) { alloc.set_width (alloc.get_width() - 20); alloc.set_x (alloc.get_x() + 10); - } + } + int wx, wy; + + _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel, + alloc.get_x(), alloc.get_y(), + wx, wy); + + scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height()); } - - scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height()); - + int x, y; Gdk::ModifierType mask; - get_window()->get_pointer (x, y, mask); + toplevel->get_window()->get_pointer (x, y, mask); if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) || (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) { @@ -568,6 +581,59 @@ Editor::autoscroll_active () const return autoscroll_connection.connected (); } +std::pair +Editor::session_gui_extents ( bool use_extra ) const +{ + if (!_session) { + return std::pair (max_framepos,0); + } + + framecnt_t session_extent_start = _session->current_start_frame(); + framecnt_t session_extent_end = _session->current_end_frame(); + + //calculate the extents of all regions in every playlist + //NOTE: we should listen to playlists, and cache these values so we don't calculate them every time. + { + boost::shared_ptr rl = _session->get_routes(); + for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*r); + if (tr) { + boost::shared_ptr pl = tr->playlist(); + if ( pl && !pl->all_regions_empty() ) { + pair e; + e = pl->get_extent(); + if (e.first < session_extent_start) { + session_extent_start = e.first; + } + if (e.second > session_extent_end) { + session_extent_end = e.second; + } + } + } + } + } + + //ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like) + + //add additional time to the ui extents ( user-defined in config ) + if (use_extra) { + framecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_frame_rate(); + session_extent_end += extra; + session_extent_start -= extra; + } + + //range-check + if (session_extent_end > max_framepos) { + session_extent_end = max_framepos; + } + if (session_extent_start < 0) { + session_extent_start = 0; + } + + std::pair ret (session_extent_start, session_extent_end); + return ret; +} + bool Editor::autoscroll_canvas () { @@ -575,9 +641,13 @@ Editor::autoscroll_canvas () Gdk::ModifierType mask; frameoffset_t dx = 0; bool no_stop = false; - bool y_motion = false; + Gtk::Window* toplevel = dynamic_cast(contents().get_toplevel()); - get_window()->get_pointer (x, y, mask); + if (!toplevel) { + return false; + } + + toplevel->get_window()->get_pointer (x, y, mask); VisualChange vc; bool vertical_motion = false; @@ -595,6 +665,8 @@ Editor::autoscroll_canvas () dx += 10 + (2 * (autoscroll_cnt/2)); dx = pixel_to_sample (dx); + + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); if (leftmost_frame < max_framepos - dx) { new_frame = leftmost_frame + dx; @@ -605,12 +677,14 @@ Editor::autoscroll_canvas () 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_frame >= dx) { new_frame = leftmost_frame - dx; } else { @@ -619,7 +693,7 @@ Editor::autoscroll_canvas () no_stop = true; } - + if (new_frame != leftmost_frame) { vc.time_origin = new_frame; vc.add (VisualChange::TimeOrigin); @@ -627,30 +701,31 @@ Editor::autoscroll_canvas () } if (autoscroll_vertical_allowed) { - + // const double vertical_pos = vertical_adjustment.get_value(); const int speed_factor = 10; - /* vertical */ - + /* vertical */ + if (y < autoscroll_boundary.y0) { /* scroll to make higher tracks visible */ if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { - y_motion = scroll_up_one_track (); + scroll_up_one_track (); vertical_motion = true; } + no_stop = true; } else if (y > autoscroll_boundary.y1) { if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) { - y_motion = scroll_down_one_track (); + scroll_down_one_track (); vertical_motion = true; } + no_stop = true; } - no_stop = true; } if (vc.pending || vertical_motion) { @@ -669,18 +744,18 @@ Editor::autoscroll_canvas () 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 + * our own top level window (we being the Editor). Convert into * coordinates within the canvas window. */ int cx; int cy; - translate_coordinates (*_track_canvas, x, y, cx, cy); + toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy); /* clamp x and y to remain within the autoscroll boundary, * which is defined in window coordinates @@ -696,20 +771,21 @@ Editor::autoscroll_canvas () ArdourCanvas::Duple d = _track_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 + * 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 @@ -731,14 +807,15 @@ Editor::autoscroll_canvas () } y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1); - translate_coordinates (*_track_canvas_viewport, x, y, cx, cy); + toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy); ArdourCanvas::Duple d = _track_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; @@ -747,7 +824,7 @@ Editor::autoscroll_canvas () autoscroll_cnt++; return true; /* call me again */ -} +} void Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary) @@ -758,7 +835,6 @@ Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const Ardour stop_canvas_autoscroll (); - autoscroll_cnt = 0; autoscroll_horizontal_allowed = allow_horiz; autoscroll_vertical_allowed = allow_vert; autoscroll_boundary = boundary; @@ -777,48 +853,106 @@ void Editor::stop_canvas_autoscroll () { autoscroll_connection.disconnect (); + autoscroll_cnt = 0; +} + +Editor::EnterContext* +Editor::get_enter_context(ItemType type) +{ + for (ssize_t i = _enter_stack.size() - 1; i >= 0; --i) { + if (_enter_stack[i].item_type == type) { + return &_enter_stack[i]; + } + } + return NULL; } bool -Editor::left_track_canvas (GdkEventCrossing */*ev*/) +Editor::left_track_canvas (GdkEventCrossing* ev) { + const bool was_within = within_track_canvas; DropDownKeys (); within_track_canvas = false; set_entered_track (0); set_entered_regionview (0); reset_canvas_action_sensitivity (false); + + if (was_within) { + if (ev->detail == GDK_NOTIFY_NONLINEAR || + ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) { + /* context menu or something similar */ + sensitize_the_right_region_actions (false); + } else { + sensitize_the_right_region_actions (true); + } + } + return false; } bool -Editor::entered_track_canvas (GdkEventCrossing */*ev*/) +Editor::entered_track_canvas (GdkEventCrossing* ev) { + const bool was_within = within_track_canvas; within_track_canvas = true; reset_canvas_action_sensitivity (true); - return FALSE; + + if (!was_within) { + if (ev->detail == GDK_NOTIFY_NONLINEAR || + ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) { + /* context menu or something similar */ + sensitize_the_right_region_actions (false); + } else { + sensitize_the_right_region_actions (true); + } + } + + return false; } void -Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top) +Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top) { - double begin = tav.y_position(); - double v = vertical_adjustment.get_value (); - - if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) { - /* try to put the TimeAxisView roughly central */ - if (begin >= _visible_canvas_height/2.0) { - begin -= _visible_canvas_height/2.0; - } + if (track.hidden()) { + return; } - /* Clamp the y pos so that we do not extend beyond the canvas full - * height. + /* compute visible area of trackview group, as offsets from top of + * trackview group. */ - if (_full_canvas_height - begin < _visible_canvas_height){ - begin = _full_canvas_height - _visible_canvas_height; + + double const current_view_min_y = vertical_adjustment.get_value(); + double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size(); + + double const track_min_y = track.y_position (); + double const track_max_y = track.y_position () + track.effective_height (); + + if (!at_top && + (track_min_y >= current_view_min_y && + track_max_y < current_view_max_y)) { + /* already visible, and caller did not ask to place it at the + * top of the track canvas + */ + return; } - vertical_adjustment.set_value (begin); + double new_value; + + if (at_top) { + new_value = track_min_y; + } else { + if (track_min_y < current_view_min_y) { + // Track is above the current view + new_value = track_min_y; + } else if (track_max_y > current_view_max_y) { + // Track is below the current view + new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size(); + } else { + new_value = track_min_y; + } + } + + vertical_adjustment.set_value(new_value); } /** Called when the main vertical_adjustment has changed */ @@ -836,22 +970,13 @@ Editor::set_horizontal_position (double p) horizontal_adjustment.set_value (p); leftmost_frame = (framepos_t) floor (p * samples_per_pixel); - - update_fixed_rulers (); - redisplay_tempo (true); - - if (pending_visual_change.idle_handler_id < 0) { - _summary->set_overlays_dirty (); - } - - update_video_timeline(); } void Editor::color_handler() { - ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase(); - ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText(); + Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base"); + Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text"); timecode_ruler->set_fill_color (base); timecode_ruler->set_outline_color (text); minsec_ruler->set_fill_color (base); @@ -860,59 +985,62 @@ Editor::color_handler() samples_ruler->set_outline_color (text); bbt_ruler->set_fill_color (base); bbt_ruler->set_outline_color (text); - - playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead()); - _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor()); - meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar()); - meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + playhead_cursor->set_color (UIConfiguration::instance().color ("play head")); - tempo_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TempoBar()); - tempo_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar")); + meter_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MarkerBar()); - marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + tempo_bar->set_fill_color (UIConfiguration::instance().color_mod ("tempo bar", "marker bar")); + tempo_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - cd_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CDMarkerBar()); - cd_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("marker bar", "marker bar")); + marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - range_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeMarkerBar()); - range_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + cd_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("cd marker bar", "marker bar")); + cd_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - transport_marker_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportMarkerBar()); - transport_marker_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator()); + range_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("range marker bar", "marker bar")); + range_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - cd_marker_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect()); - cd_marker_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect()); + transport_marker_bar->set_fill_color (UIConfiguration::instance().color_mod ("transport marker bar", "marker bar")); + transport_marker_bar->set_outline_color (UIConfiguration::instance().color ("marker bar separator")); - range_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect()); - range_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragBarRect()); + cd_marker_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect")); + cd_marker_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect")); - transport_bar_drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect()); - transport_bar_drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportDragRect()); + range_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag bar rect")); + range_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag bar rect")); - transport_loop_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect()); - transport_loop_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportLoopRect()); + transport_bar_drag_rect->set_fill_color (UIConfiguration::instance().color ("transport drag rect")); + transport_bar_drag_rect->set_outline_color (UIConfiguration::instance().color ("transport drag rect")); - transport_punch_range_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect()); - transport_punch_range_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TransportPunchRect()); + transport_loop_range_rect->set_fill_color (UIConfiguration::instance().color_mod ("transport loop rect", "loop rectangle")); + transport_loop_range_rect->set_outline_color (UIConfiguration::instance().color ("transport loop rect")); - transport_punchin_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine()); - transport_punchout_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_PunchLine()); + transport_punch_range_rect->set_fill_color (UIConfiguration::instance().color ("transport punch rect")); + transport_punch_range_rect->set_outline_color (UIConfiguration::instance().color ("transport punch rect")); - zoom_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect()); - zoom_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZoomRect()); + transport_punchin_line->set_outline_color (UIConfiguration::instance().color ("punch line")); + transport_punchout_line->set_outline_color (UIConfiguration::instance().color ("punch line")); - rubberband_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RubberBandRect()); - rubberband_rect->set_fill_color ((guint32) ARDOUR_UI::config()->get_canvasvar_RubberBandRect()); + rubberband_rect->set_outline_color (UIConfiguration::instance().color ("rubber band rect")); + rubberband_rect->set_fill_color (UIConfiguration::instance().color_mod ("rubber band rect", "selection rect")); - location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker(); - location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange(); - location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker(); - location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop(); - location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch(); + location_marker_color = UIConfiguration::instance().color ("location marker"); + location_range_color = UIConfiguration::instance().color ("location range"); + location_cd_marker_color = UIConfiguration::instance().color ("location cd marker"); + location_loop_color = UIConfiguration::instance().color ("location loop"); + location_punch_color = UIConfiguration::instance().color ("location punch"); refresh_location_display (); + + NoteBase::set_colors (); + + /* redraw the whole thing */ + _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base")); + _track_canvas->queue_draw (); + /* redisplay_tempo (true); @@ -930,21 +1058,12 @@ Editor::horizontal_position () const bool Editor::track_canvas_key_press (GdkEventKey*) { - /* XXX: event does not report the modifier key pressed down, AFAICS, so use the Keyboard object instead */ - if (mouse_mode == Editing::MouseZoom && Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { - set_canvas_cursor (_cursors->zoom_out, true); - } - return false; } bool Editor::track_canvas_key_release (GdkEventKey*) { - if (mouse_mode == Editing::MouseZoom && !Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { - set_canvas_cursor (_cursors->zoom_in, true); - } - return false; } @@ -973,36 +1092,57 @@ Editor::get_track_canvas() const return _track_canvas_viewport; } -void -Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save) +Gdk::Cursor* +Editor::get_canvas_cursor () const { - if (save) { - current_canvas_cursor = cursor; - } + /* The top of the cursor stack is always the currently visible cursor. */ + return _cursor_stack.back(); +} +void +Editor::set_canvas_cursor (Gdk::Cursor* cursor) +{ Glib::RefPtr win = _track_canvas->get_window(); - if (win && cursor) { - win->set_cursor (*cursor); + if (win && !_cursors->is_invalid (cursor)) { + /* glibmm 2.4 doesn't allow null cursor pointer because it uses + a Gdk::Cursor& as the argument to Gdk::Window::set_cursor(). + But a null pointer just means "use parent window cursor", + and so should be allowed. Gtkmm 3.x has fixed this API. + + For now, drop down and use C API + */ + gdk_window_set_cursor (win->gobj(), cursor ? cursor->gobj() : 0); } } -void +size_t Editor::push_canvas_cursor (Gdk::Cursor* cursor) { - if (cursor) { - _cursor_stack.push (cursor); - set_canvas_cursor (cursor, false); + if (!_cursors->is_invalid (cursor)) { + _cursor_stack.push_back (cursor); + set_canvas_cursor (cursor); } + return _cursor_stack.size() - 1; } void Editor::pop_canvas_cursor () { - if (!_cursor_stack.empty()) { - Gdk::Cursor* cursor = _cursor_stack.top (); - _cursor_stack.pop (); - set_canvas_cursor (cursor, false); + while (true) { + if (_cursor_stack.size() <= 1) { + PBD::error << "attempt to pop default cursor" << endmsg; + return; + } + + _cursor_stack.pop_back(); + if (_cursor_stack.back()) { + /* Popped to an existing cursor, we're done. Otherwise, the + context that created this cursor has been destroyed, so we need + to skip to the next down the stack. */ + set_canvas_cursor (_cursor_stack.back()); + return; + } } } @@ -1011,41 +1151,16 @@ Editor::which_grabber_cursor () const { Gdk::Cursor* c = _cursors->grabber; - if (_internal_editing) { - switch (mouse_mode) { - case MouseDraw: - c = _cursors->midi_pencil; - break; - - case MouseObject: - c = _cursors->grabber_note; - break; - - case MouseTimeFX: - c = _cursors->midi_resize; - break; - - case MouseRange: - c = _cursors->grabber_note; - break; - - default: - break; - } - - } else { - - switch (_edit_point) { - case EditAtMouse: - c = _cursors->grabber_edit_point; - break; - default: - boost::shared_ptr m = _movable.lock(); - if (m && m->locked()) { - c = _cursors->speaker; - } - break; + switch (_edit_point) { + case EditAtMouse: + c = _cursors->grabber_edit_point; + break; + default: + boost::shared_ptr m = _movable.lock(); + if (m && m->locked()) { + c = _cursors->speaker; } + break; } return c; @@ -1059,9 +1174,9 @@ Editor::which_trim_cursor (bool left) const } Trimmable::CanTrim ct = entered_regionview->region()->can_trim (); - + if (left) { - + if (ct & Trimmable::FrontTrimEarlier) { return _cursors->left_side_trim; } else { @@ -1079,17 +1194,19 @@ Editor::which_trim_cursor (bool left) const Gdk::Cursor* Editor::which_mode_cursor () const { - Gdk::Cursor* mode_cursor = 0; + Gdk::Cursor* mode_cursor = MouseCursors::invalid_cursor (); switch (mouse_mode) { case MouseRange: mode_cursor = _cursors->selector; - if (_internal_editing) { - mode_cursor = which_grabber_cursor(); - } + break; + + case MouseCut: + mode_cursor = _cursors->scissors; break; case MouseObject: + case MouseContent: /* don't use mode cursor, pick a grabber cursor based on the item */ break; @@ -1097,18 +1214,6 @@ Editor::which_mode_cursor () const mode_cursor = _cursors->midi_pencil; break; - case MouseGain: - mode_cursor = _cursors->cross_hair; - break; - - case MouseZoom: - if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) { - mode_cursor = _cursors->zoom_out; - } else { - mode_cursor = _cursors->zoom_in; - } - break; - case MouseTimeFX: mode_cursor = _cursors->time_fx; // just use playhead break; @@ -1119,26 +1224,26 @@ Editor::which_mode_cursor () const } /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */ - if (!_internal_editing && get_smart_mode() ) { + if (get_smart_mode()) { double x, y; get_pointer_position (x, y); if (x >= 0 && y >= 0) { - + vector items; /* Note how we choose a specific scroll group to get * items from. This could be problematic. */ - + hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items); - - // first item will be the upper most - + + // first item will be the upper most + if (!items.empty()) { const ArdourCanvas::Item* i = items.front(); - + if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) { pair tvp = trackview_by_y_position (_last_motion_y); if (dynamic_cast (tvp.first)) { @@ -1155,66 +1260,53 @@ Editor::which_mode_cursor () const Gdk::Cursor* Editor::which_track_cursor () const { - Gdk::Cursor* cursor = 0; - - assert (mouse_mode == MouseObject || get_smart_mode()); + Gdk::Cursor* cursor = MouseCursors::invalid_cursor(); - if (!_internal_editing) { - switch (_join_object_range_state) { - case JOIN_OBJECT_RANGE_NONE: - case JOIN_OBJECT_RANGE_OBJECT: - cursor = which_grabber_cursor (); - cerr << "region use grabber\n"; - break; - case JOIN_OBJECT_RANGE_RANGE: - cursor = _cursors->selector; - cerr << "region use selector\n"; - break; - } + switch (_join_object_range_state) { + case JOIN_OBJECT_RANGE_NONE: + case JOIN_OBJECT_RANGE_OBJECT: + cursor = which_grabber_cursor (); + break; + case JOIN_OBJECT_RANGE_RANGE: + cursor = _cursors->selector; + break; } return cursor; } -bool -Editor::reset_canvas_cursor () +Gdk::Cursor* +Editor::which_canvas_cursor(ItemType type) const { - if (!is_drawable()) { - return false; - } - Gdk::Cursor* cursor = which_mode_cursor (); - if (cursor) { - set_canvas_cursor (cursor); - return true; - } - - return false; -} - -void -Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type) -{ - Gdk::Cursor* cursor = 0; - - cerr << "entered new item type " << enum_2_string (type) << endl; - - if (_drags->active()) { - return; + if (mouse_mode == MouseRange) { + switch (type) { + case StartSelectionTrimItem: + cursor = _cursors->left_side_trim; + break; + case EndSelectionTrimItem: + cursor = _cursors->right_side_trim; + break; + default: + break; + } } - cursor = which_mode_cursor (); - - if (mouse_mode == MouseObject || get_smart_mode ()) { + if ((mouse_mode == MouseObject || get_smart_mode ()) || + mouse_mode == MouseContent) { /* find correct cursor to use in object/smart mode */ switch (type) { case RegionItem: - case RegionViewNameHighlight: - case RegionViewName: - case WaveItem: + /* We don't choose a cursor for these items on top of a region view, + because this would push a new context on the enter stack which + means switching the region context for things like smart mode + won't actualy change the cursor. */ + // case RegionViewNameHighlight: + // case RegionViewName: + // case WaveItem: case StreamItem: case AutomationTrackItem: cursor = which_track_cursor (); @@ -1236,7 +1328,7 @@ Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType typ cursor = _cursors->fader; break; case GainLineItem: - cursor = _cursors->fader; + cursor = _cursors->cross_hair; break; case AutomationLineItem: cursor = _cursors->cross_hair; @@ -1265,17 +1357,20 @@ Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType typ case FadeOutTrimHandleItem: cursor = _cursors->fade_out; break; - case NoteItem: - cursor = which_grabber_cursor(); - break; case FeatureLineItem: cursor = _cursors->cross_hair; break; case LeftFrameHandle: - cursor = which_trim_cursor (true); + if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the btm half, show the trim cursor + cursor = which_trim_cursor (true); + else + cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor break; case RightFrameHandle: - cursor = which_trim_cursor (false); + if ( effective_mouse_mode() == MouseObject ) //see above + cursor = which_trim_cursor (false); + else + cursor = _cursors->selector; break; case StartCrossFadeItem: cursor = _cursors->fade_in; @@ -1286,6 +1381,28 @@ Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType typ case CrossfadeViewItem: cursor = _cursors->cross_hair; break; + case NoteItem: + cursor = _cursors->grabber_note; + default: + break; + } + + } else if (mouse_mode == MouseDraw) { + + /* ControlPointItem is not really specific to region gain mode + but it is the same cursor so don't worry about this for now. + The result is that we'll see the fader cursor if we enter + non-region-gain-line control points while in MouseDraw + mode, even though we can't edit them in this mode. + */ + + switch (type) { + case GainLineItem: + case ControlPointItem: + cursor = _cursors->fader; + break; + case NoteItem: + cursor = _cursors->grabber_note; default: break; } @@ -1311,6 +1428,7 @@ Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType typ case CdMarkerBarItem: case VideoBarItem: case TransportMarkerBarItem: + case DropZoneItem: cursor = which_grabber_cursor(); break; @@ -1318,7 +1436,39 @@ Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType typ break; } - if (cursor) { - set_canvas_cursor (cursor, false); + return cursor; +} + +void +Editor::choose_canvas_cursor_on_entry (ItemType type) +{ + if (_drags->active()) { + return; + } + + Gdk::Cursor* cursor = which_canvas_cursor(type); + + if (!_cursors->is_invalid (cursor)) { + // Push a new enter context + const EnterContext ctx = { type, CursorContext::create(*this, cursor) }; + _enter_stack.push_back(ctx); + } +} + +void +Editor::update_all_enter_cursors () +{ + for (std::vector::iterator i = _enter_stack.begin(); i != _enter_stack.end(); ++i) { + i->cursor_ctx->change(which_canvas_cursor(i->item_type)); + } +} + +double +Editor::trackviews_height() const +{ + if (!_trackview_group) { + return 0; } + + return _visible_canvas_height - _trackview_group->canvas_origin().y; }