X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_canvas.cc;h=72a86808d0aac88ae02c2f4c323206cc170e7922;hb=cc83e1a9bfd7339c4ebfcde4a57d511eec51923e;hp=bc457453bc3149c717313dc8db53ab0778d7296b;hpb=ebc7eabea2f513ad6050c8fbd735371d553f68cf;p=ardour.git diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index bc457453bc..72a86808d0 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -73,6 +73,7 @@ Editor::initialize_canvas () _track_canvas = _track_canvas_viewport->canvas (); _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base")); + _track_canvas->use_nsglview (); /* scroll group for items that should not automatically scroll * (e.g verbose cursor). It shares the canvas coordinate space. @@ -126,7 +127,7 @@ Editor::initialize_canvas () * 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"); + CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion"); /* TIME BAR CANVAS */ @@ -173,7 +174,7 @@ 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)); @@ -216,6 +217,8 @@ Editor::initialize_canvas () playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event); + snapped_cursor = new EditorCursor (*this); + _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); @@ -240,6 +243,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; @@ -301,7 +306,7 @@ Editor::track_canvas_viewport_size_allocated () } update_fixed_rulers(); - redisplay_tempo (false); + redisplay_grid (false); _summary->set_overlays_dirty (); } @@ -314,17 +319,17 @@ Editor::reset_controls_layout_width () edit_controls_vbox.size_request (req); w = req.width; - if (_group_tabs->is_visible()) { + 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 @@ -342,11 +347,11 @@ 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; } @@ -376,14 +381,14 @@ Editor::track_canvas_drag_data_received (const RefPtr& context } bool -Editor::idle_drop_paths (vector paths, framepos_t frame, double ypos, bool copy) +Editor::idle_drop_paths (vector paths, samplepos_t sample, double ypos, bool copy) { - drop_paths_part_two (paths, frame, ypos, copy); + drop_paths_part_two (paths, sample, ypos, copy); return false; } void -Editor::drop_paths_part_two (const vector& paths, framepos_t frame, double ypos, bool copy) +Editor::drop_paths_part_two (const vector& paths, samplepos_t sample, double ypos, bool copy) { RouteTimeAxisView* tv; @@ -409,15 +414,15 @@ Editor::drop_paths_part_two (const vector& paths, framepos_t frame, doub /* drop onto canvas background: create new tracks */ - frame = 0; + sample = 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, SMFTrackName, SMFTempoIgnore, sample, is.selected_instrument()); if (UIConfiguration::instance().get_only_copy_imported_files() || copy) { - do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, - SrcBest, SMFTrackName, SMFTempoIgnore, frame); + do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, + SrcBest, SMFTrackName, SMFTempoIgnore, sample); } else { - do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame); + do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, sample); } } else if ((tv = dynamic_cast (tvp.first)) != 0) { @@ -429,13 +434,13 @@ Editor::drop_paths_part_two (const vector& paths, framepos_t frame, doub selection->set (tv); do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, - SrcBest, SMFTrackName, SMFTempoIgnore, frame); + SrcBest, SMFTrackName, SMFTempoIgnore, sample); if (UIConfiguration::instance().get_only_copy_imported_files() || copy) { do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, - SrcBest, SMFTrackName, SMFTempoIgnore, frame); + SrcBest, SMFTrackName, SMFTempoIgnore, sample); } else { - do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame); + do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, sample); } } } @@ -449,7 +454,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) { @@ -461,9 +465,8 @@ Editor::drop_paths (const RefPtr& context, ev.button.x = x; ev.button.y = y; - frame = window_event_sample (&ev, 0, &cy); - - snap_to (frame); + MusicSample 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 __APPLE__ @@ -471,9 +474,9 @@ Editor::drop_paths (const RefPtr& context, 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.sample, cy, copy)); #else - drop_paths_part_two (paths, frame, cy, copy); + drop_paths_part_two (paths, when.sample, cy, copy); #endif } @@ -514,6 +517,16 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) if (from_headers) { alloc = controls_layout.get_allocation (); + + 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 (); @@ -544,16 +557,21 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers) 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 (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height()); + scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height()); + } int x, y; Gdk::ModifierType mask; toplevel->get_window()->get_pointer (x, y, mask); - if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) || + 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); } @@ -565,12 +583,66 @@ Editor::autoscroll_active () const return autoscroll_connection.connected (); } +std::pair +Editor::session_gui_extents (bool use_extra) const +{ + if (!_session) { + return std::pair (max_samplepos,0); + } + + samplecnt_t session_extent_start = _session->current_start_sample(); + samplecnt_t session_extent_end = _session->current_end_sample(); + + /* 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) { + samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate(); + session_extent_end += extra; + session_extent_start -= extra; + } + + /* range-check */ + if (session_extent_end > max_samplepos) { + session_extent_end = max_samplepos; + } + if (session_extent_start < 0) { + session_extent_start = 0; + } + + std::pair ret (session_extent_start, session_extent_end); + return ret; +} + bool Editor::autoscroll_canvas () { int x, y; Gdk::ModifierType mask; - frameoffset_t dx = 0; + sampleoffset_t dx = 0; bool no_stop = false; Gtk::Window* toplevel = dynamic_cast(contents().get_toplevel()); @@ -585,7 +657,7 @@ Editor::autoscroll_canvas () if (autoscroll_horizontal_allowed) { - framepos_t new_frame = leftmost_frame; + samplepos_t new_sample = _leftmost_sample; /* horizontal */ @@ -597,10 +669,12 @@ Editor::autoscroll_canvas () dx = pixel_to_sample (dx); - if (leftmost_frame < max_framepos - dx) { - new_frame = leftmost_frame + dx; + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample < max_samplepos - dx) { + new_sample = _leftmost_sample + dx; } else { - new_frame = max_framepos; + new_sample = max_samplepos; } no_stop = true; @@ -612,17 +686,19 @@ Editor::autoscroll_canvas () dx = pixel_to_sample (dx); - if (leftmost_frame >= dx) { - new_frame = leftmost_frame - dx; + dx *= UIConfiguration::instance().get_draggable_playhead_speed(); + + if (_leftmost_sample >= dx) { + new_sample = _leftmost_sample - dx; } else { - new_frame = 0; + new_sample = 0; } no_stop = true; } - if (new_frame != leftmost_frame) { - vc.time_origin = new_frame; + if (new_sample != _leftmost_sample) { + vc.time_origin = new_sample; vc.add (VisualChange::TimeOrigin); } } @@ -795,22 +871,46 @@ Editor::get_enter_context(ItemType type) } 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 @@ -872,23 +972,14 @@ 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(); + _leftmost_sample = (samplepos_t) floor (p * samples_per_pixel); } void Editor::color_handler() { - ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base"); - ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text"); + 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); @@ -954,7 +1045,7 @@ Editor::color_handler() _track_canvas->queue_draw (); /* - redisplay_tempo (true); + redisplay_grid (true); if (_session) _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers @@ -964,7 +1055,7 @@ Editor::color_handler() double Editor::horizontal_position () const { - return sample_to_pixel (leftmost_frame); + return sample_to_pixel (_leftmost_sample); } bool @@ -1058,26 +1149,6 @@ Editor::pop_canvas_cursor () } } -Gdk::Cursor* -Editor::which_grabber_cursor () const -{ - Gdk::Cursor* c = _cursors->grabber; - - 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 { @@ -1177,7 +1248,7 @@ Editor::which_track_cursor () const switch (_join_object_range_state) { case JOIN_OBJECT_RANGE_NONE: case JOIN_OBJECT_RANGE_OBJECT: - cursor = which_grabber_cursor (); + cursor = _cursors->grabber; break; case JOIN_OBJECT_RANGE_RANGE: cursor = _cursors->selector; @@ -1224,14 +1295,7 @@ Editor::which_canvas_cursor(ItemType type) const cursor = which_track_cursor (); break; case PlayheadCursorItem: - switch (_edit_point) { - case EditAtMouse: - cursor = _cursors->grabber_edit_point; - break; - default: - cursor = _cursors->grabber; - break; - } + cursor = _cursors->grabber; break; case SelectionItem: cursor = _cursors->selector; @@ -1273,13 +1337,13 @@ Editor::which_canvas_cursor(ItemType type) const cursor = _cursors->cross_hair; break; case LeftFrameHandle: - if ( effective_mouse_mode() == MouseObject ) // (smart mode): if the user is in the btm half, show the trim cursor + 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 + cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor break; case RightFrameHandle: - if ( effective_mouse_mode() == MouseObject ) //see above + if (effective_mouse_mode() == MouseObject) // see above cursor = which_trim_cursor (false); else cursor = _cursors->selector; @@ -1341,7 +1405,7 @@ Editor::which_canvas_cursor(ItemType type) const case VideoBarItem: case TransportMarkerBarItem: case DropZoneItem: - cursor = which_grabber_cursor(); + cursor = _cursors->grabber; break; default: