X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_canvas.cc;h=3d763cbe568cdc7be7ddf85fcce6b9aee3d0abf3;hb=4e411dfd7bab6793426979fe005faf2435c0df0d;hp=1736da96cb4a8e45ec4ae270095f71a52ec881dd;hpb=0de7c8920b57e9af99597affb1b18e3435fc1f67;p=ardour.git diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index 1736da96cb..3d763cbe56 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -30,10 +30,12 @@ #include "canvas/canvas.h" #include "canvas/rectangle.h" #include "canvas/pixbuf.h" +#include "canvas/scroll_group.h" #include "canvas/text.h" #include "canvas/debug.h" #include "ardour_ui.h" +#include "automation_time_axis.h" #include "editor.h" #include "global_signals.h" #include "editing.h" @@ -54,6 +56,7 @@ using namespace std; using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtk; using namespace Glib; @@ -66,9 +69,29 @@ Editor::initialize_canvas () _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment); _track_canvas = _track_canvas_viewport->canvas (); - _time_bars_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, unused_adjustment); - _time_bars_canvas = _time_bars_canvas_viewport->canvas (); - + /* 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()); + + ArdourCanvas::ScrollGroup* hsg; + ArdourCanvas::ScrollGroup* hg; + ArdourCanvas::ScrollGroup* vg; + + 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); + + 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); + + 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); /* on the bottom, an image */ @@ -82,9 +105,9 @@ Editor::initialize_canvas () // logo_item->property_width_set() = true; // logo_item->show (); } - + /*a group to hold global rects like punch/loop indicators */ - global_rect_group = new ArdourCanvas::Group (_track_canvas->root()); + 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)); @@ -96,66 +119,76 @@ Editor::initialize_canvas () transport_punch_range_rect->hide(); /*a group to hold time (measure) lines */ - time_line_group = new ArdourCanvas::Group (_track_canvas->root()); + time_line_group = new ArdourCanvas::Container (hv_scroll_group); CANVAS_DEBUG_NAME (time_line_group, "time line group"); - _trackview_group = new ArdourCanvas::Group (_track_canvas->root()); + _trackview_group = new ArdourCanvas::Container (hv_scroll_group); CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews"); - _region_motion_group = new ArdourCanvas::Group (_trackview_group); - CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion"); + + // 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)); + rubberband_rect->hide(); + + /* 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::Container (hv_scroll_group); + CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion"); + + /* TIME BAR CANVAS */ + + _time_markers_group = new ArdourCanvas::Container (h_scroll_group); + CANVAS_DEBUG_NAME (_time_markers_group, "time bars"); + + 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::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0)); + CANVAS_DEBUG_NAME (videotl_group, "videotl group"); + 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::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::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::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::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0)); + CANVAS_DEBUG_NAME (meter_group, "meter group"); - meter_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - meter_bar = new ArdourCanvas::Rectangle (meter_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (meter_bar, "meter Bar"); meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); - tempo_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - tempo_bar = new ArdourCanvas::Rectangle (tempo_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar"); tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); - range_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - range_marker_bar = new ArdourCanvas::Rectangle (range_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar"); range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); - transport_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar"); transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); - marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - marker_bar = new ArdourCanvas::Rectangle (marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); CANVAS_DEBUG_NAME (marker_bar, "Marker Bar"); marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM); - cd_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ()); - cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height)); + 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); - - _time_markers_group = new ArdourCanvas::Group (_time_bars_canvas->root()); - - cd_marker_group = new ArdourCanvas::Group (_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::Group (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0)); - CANVAS_DEBUG_NAME (videotl_group, "videotl group"); - marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0)); - CANVAS_DEBUG_NAME (marker_group, "marker group"); - transport_marker_group = new ArdourCanvas::Group (_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::Group (_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::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0)); - CANVAS_DEBUG_NAME (tempo_group, "tempo group"); - meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0)); - CANVAS_DEBUG_NAME (meter_group, "meter group"); 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); @@ -171,29 +204,20 @@ Editor::initialize_canvas () transport_bar_drag_rect->set_outline (false); transport_bar_drag_rect->hide (); - transport_punchin_line = new ArdourCanvas::Line (_track_canvas->root()); + transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group); transport_punchin_line->set_x0 (0); transport_punchin_line->set_y0 (0); transport_punchin_line->set_x1 (0); transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX); transport_punchin_line->hide (); - transport_punchout_line = new ArdourCanvas::Line (_track_canvas->root()); + transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group); transport_punchout_line->set_x0 (0); transport_punchout_line->set_y0 (0); transport_punchout_line->set_x1 (0); transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX); transport_punchout_line->hide(); - // used to show zoom mode active zooming - zoom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), 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 (_trackview_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0)); - rubberband_rect->hide(); - tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar)); meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar)); marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar)); @@ -209,17 +233,17 @@ Editor::initialize_canvas () } - _canvas_bottom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 20)); + _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_bottom_rect->set_fill (false); - _canvas_bottom_rect->set_outline (false); - _canvas_bottom_rect->Event.connect (sigc::mem_fun (*this, &Editor::canvas_bottom_rect_event)); + _canvas_drop_zone->set_fill (false); + _canvas_drop_zone->set_outline (false); + _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event)); /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level handlers. */ - _track_canvas->signal_scroll_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_scroll_event)); + _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true)); _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event)); _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event)); _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event)); @@ -249,6 +273,8 @@ Editor::initialize_canvas () _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate)); + initialize_rulers (); + ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler)); color_handler(); @@ -269,6 +295,8 @@ Editor::track_canvas_viewport_size_allocated () _visible_canvas_width = _canvas_viewport_allocation.get_width (); _visible_canvas_height = _canvas_viewport_allocation.get_height (); + _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0)); + // SHOWTRACKS if (height_changed) { @@ -323,13 +351,13 @@ Editor::reset_controls_layout_height (int32_t h) * (the drag-n-drop zone) is, in fact, at the bottom. */ - _canvas_bottom_rect->set_position (ArdourCanvas::Duple (0, h)); + _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h)); /* track controls layout must span the full height of "h" (all tracks) * plus the bottom rect. */ - h += _canvas_bottom_rect->height (); + h += _canvas_drop_zone->height (); /* 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. @@ -391,7 +419,7 @@ Editor::drop_paths_part_two (const vector& paths, framepos_t frame, doub } - std::pair const tvp = trackview_by_y_position (ypos); + std::pair const tvp = trackview_by_y_position (ypos, false); if (tvp.first == 0) { /* drop onto canvas background: create new tracks */ @@ -438,7 +466,7 @@ Editor::drop_paths (const RefPtr& context, if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) { - /* D-n-D coordinates are window-relative, so convert to "world" coordinates + /* D-n-D coordinates are window-relative, so convert to canvas coordinates */ ev.type = GDK_BUTTON_RELEASE; @@ -472,19 +500,43 @@ Editor::drop_paths (const RefPtr& context, void Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) { - if (!Config->get_autoscroll_editor ()) { + if (!Config->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 the toplevel + * 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; - + if (from_headers) { alloc = controls_layout.get_allocation (); - } else { + } else { alloc = _track_canvas_viewport->get_allocation (); + + /* 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. @@ -497,19 +549,18 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) alloc.set_width (alloc.get_width() - 20); alloc.set_x (alloc.get_x() + 10); } + } - scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), - alloc.get_x() + alloc.get_width(), - alloc.get_y() + 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); - if ((allow_horiz && (x < scrolling_boundary.x0 || x >= scrolling_boundary.x1)) || - (allow_vert && (y < scrolling_boundary.y0 || y >= scrolling_boundary.y1))) { + 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))) { start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary); } } @@ -527,11 +578,12 @@ Editor::autoscroll_canvas () Gdk::ModifierType mask; frameoffset_t dx = 0; bool no_stop = false; - bool y_motion = false; get_window()->get_pointer (x, y, mask); VisualChange vc; + bool vertical_motion = false; + bool y_motion = false; if (autoscroll_horizontal_allowed) { @@ -579,34 +631,32 @@ Editor::autoscroll_canvas () if (autoscroll_vertical_allowed) { - const double vertical_pos = vertical_adjustment.get_value(); - double new_pixel = vertical_pos; - const int speed_factor = 20; + // const double vertical_pos = vertical_adjustment.get_value(); + const int speed_factor = 10; /* vertical */ - new_pixel = vertical_pos; - 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; } } 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; } - if (vc.pending) { + if (vc.pending || vertical_motion) { /* change horizontal first */ @@ -625,26 +675,33 @@ Editor::autoscroll_canvas () /* the motion handler expects events in canvas coordinate space */ - /* first convert from Editor window coordinates to canvas - * window coordinates + /* 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; - /* clamp x and y to remain within the visible area */ + translate_coordinates (*_track_canvas, x, y, cx, cy); - x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1); - y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1); + /* clamp x and y to remain within the autoscroll boundary, + * which is defined in window coordinates + */ - translate_coordinates (*_track_canvas_viewport, x, y, cx, cy); + 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 = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy)); ev.x = d.x; ev.y = d.y; motion_handler (0, (GdkEvent*) &ev, true); - + } else if (no_stop) { /* not changing visual state but pointer is outside the scrolling boundary @@ -684,7 +741,7 @@ Editor::autoscroll_canvas () ev.y = d.y; motion_handler (0, (GdkEvent*) &ev, true); - + } else { stop_canvas_autoscroll (); return false; @@ -745,26 +802,48 @@ Editor::entered_track_canvas (GdkEventCrossing */*ev*/) } 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; + } + + 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 (begin); + vertical_adjustment.set_value(new_value); } /** Called when the main vertical_adjustment has changed */ @@ -796,8 +875,18 @@ Editor::set_horizontal_position (double p) void Editor::color_handler() { + ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase(); + ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText(); + timecode_ruler->set_fill_color (base); + timecode_ruler->set_outline_color (text); + minsec_ruler->set_fill_color (base); + minsec_ruler->set_outline_color (text); + samples_ruler->set_fill_color (base); + 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()); @@ -862,20 +951,6 @@ Editor::horizontal_position () const return sample_to_pixel (leftmost_frame); } -void -Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save) -{ - if (save) { - current_canvas_cursor = cursor; - } - - Glib::RefPtr win = _track_canvas->get_window(); - - if (win) { - _track_canvas->get_window()->set_cursor (*cursor); - } -} - bool Editor::track_canvas_key_press (GdkEventKey*) { @@ -916,26 +991,371 @@ Editor::clamp_verbose_cursor_y (double y) return y; } -ArdourCanvas::Group* -Editor::get_time_bars_group () const +ArdourCanvas::GtkCanvasViewport* +Editor::get_track_canvas() const { - return _time_bars_canvas->root(); + return _track_canvas_viewport; } -ArdourCanvas::Group* -Editor::get_track_canvas_group() const +void +Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save) { - return _track_canvas->root(); + if (save) { + current_canvas_cursor = cursor; + } + + Glib::RefPtr win = _track_canvas->get_window(); + + if (win && cursor) { + win->set_cursor (*cursor); + } } -ArdourCanvas::GtkCanvasViewport* -Editor::get_time_bars_canvas() const +void +Editor::push_canvas_cursor (Gdk::Cursor* cursor) { - return _time_bars_canvas_viewport; + if (cursor) { + _cursor_stack.push (cursor); + set_canvas_cursor (cursor, false); + } } -ArdourCanvas::GtkCanvasViewport* -Editor::get_track_canvas() const +void +Editor::pop_canvas_cursor () { - return _track_canvas_viewport; + if (!_cursor_stack.empty()) { + Gdk::Cursor* cursor = _cursor_stack.top (); + _cursor_stack.pop (); + set_canvas_cursor (cursor, false); + } +} + +Gdk::Cursor* +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; + } + } + + return c; +} + +Gdk::Cursor* +Editor::which_trim_cursor (bool left) const +{ + if (!entered_regionview) { + return 0; + } + + Trimmable::CanTrim ct = entered_regionview->region()->can_trim (); + + if (left) { + + if (ct & Trimmable::FrontTrimEarlier) { + return _cursors->left_side_trim; + } else { + return _cursors->left_side_trim_right_only; + } + } else { + if (ct & Trimmable::EndTrimLater) { + return _cursors->right_side_trim; + } else { + return _cursors->right_side_trim_left_only; + } + } +} + +Gdk::Cursor* +Editor::which_mode_cursor () const +{ + Gdk::Cursor* mode_cursor = 0; + + 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: + /* don't use mode cursor, pick a grabber cursor based on the item */ + break; + + case MouseDraw: + 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; + + case MouseAudition: + mode_cursor = _cursors->speaker; + break; + } + + /* 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() ) { + + 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 + + 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)) { + mode_cursor = _cursors->up_down; + } + } + } + } + } + + return mode_cursor; +} + +Gdk::Cursor* +Editor::which_track_cursor () const +{ + Gdk::Cursor* cursor = 0; + + assert (mouse_mode == MouseObject || get_smart_mode()); + + if (!_internal_editing) { + 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 () +{ + 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; + + if (_drags->active()) { + return; + } + + cursor = which_mode_cursor (); + + if (mouse_mode == MouseObject || get_smart_mode ()) { + + /* find correct cursor to use in object/smart mode */ + + switch (type) { + case RegionItem: + case RegionViewNameHighlight: + case RegionViewName: + case WaveItem: + case StreamItem: + case AutomationTrackItem: + cursor = which_track_cursor (); + break; + case PlayheadCursorItem: + switch (_edit_point) { + case EditAtMouse: + cursor = _cursors->grabber_edit_point; + break; + default: + cursor = _cursors->grabber; + break; + } + break; + case SelectionItem: + cursor = _cursors->selector; + break; + case ControlPointItem: + cursor = _cursors->fader; + break; + case GainLineItem: + cursor = _cursors->fader; + break; + case AutomationLineItem: + cursor = _cursors->cross_hair; + break; + case StartSelectionTrimItem: + cursor = _cursors->left_side_trim; + break; + case EndSelectionTrimItem: + cursor = _cursors->right_side_trim; + break; + case FadeInItem: + cursor = _cursors->fade_in; + break; + case FadeInHandleItem: + cursor = _cursors->fade_in; + break; + case FadeInTrimHandleItem: + cursor = _cursors->fade_in; + break; + case FadeOutItem: + cursor = _cursors->fade_out; + break; + case FadeOutHandleItem: + cursor = _cursors->fade_out; + break; + case FadeOutTrimHandleItem: + cursor = _cursors->fade_out; + break; + case NoteItem: + cursor = which_grabber_cursor(); + break; + case FeatureLineItem: + cursor = _cursors->cross_hair; + break; + case LeftFrameHandle: + if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the top half, override the trim cursor, since they are in the range zone + cursor = which_trim_cursor (true); //alternatively, one could argue that we _should_ allow trims here, and disallow range selection + break; + case RightFrameHandle: + if ( effective_mouse_mode() == MouseObject ) //see above + cursor = which_trim_cursor (false); + break; + case StartCrossFadeItem: + cursor = _cursors->fade_in; + break; + case EndCrossFadeItem: + cursor = _cursors->fade_out; + break; + case CrossfadeViewItem: + cursor = _cursors->cross_hair; + break; + default: + break; + } + } + + switch (type) { + /* These items use the timebar cursor at all times */ + case TimecodeRulerItem: + case MinsecRulerItem: + case BBTRulerItem: + case SamplesRulerItem: + cursor = _cursors->timebar; + break; + + /* These items use the grabber cursor at all times */ + case MeterMarkerItem: + case TempoMarkerItem: + case MeterBarItem: + case TempoBarItem: + case MarkerItem: + case MarkerBarItem: + case RangeMarkerBarItem: + case CdMarkerBarItem: + case VideoBarItem: + case TransportMarkerBarItem: + case DropZoneItem: + cursor = which_grabber_cursor(); + break; + + default: + break; + } + + if (cursor) { + set_canvas_cursor (cursor, false); + } +} + +double +Editor::trackviews_height() const +{ + if (!_trackview_group) { + return 0; + } + + return _visible_canvas_height - _trackview_group->canvas_origin().y; }