X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fviews%2Ftrace%2Fview.cpp;h=375f5754c7750a5277e538044a8d6f122f27fb44;hp=65b06aea82cba76279f185f40b4af933578a18dd;hb=8835b5e7c3c6e07e9b99e31a1fb31e600cdab76d;hpb=cbd9ec7f751a5403d1465e966ccecd31b7086e8a diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index 65b06aea..375f5754 100644 --- a/pv/views/trace/view.cpp +++ b/pv/views/trace/view.cpp @@ -79,6 +79,7 @@ using std::max; using std::make_pair; using std::make_shared; using std::min; +using std::numeric_limits; using std::pair; using std::set; using std::set_difference; @@ -178,6 +179,7 @@ View::View(Session &session, bool is_main_view, QWidget *parent) : // Set up settings and event handlers GlobalSettings settings; colored_bg_ = settings.value(GlobalSettings::Key_View_ColoredBG).toBool(); + snap_distance_ = settings.value(GlobalSettings::Key_View_SnapDistance).toInt(); GlobalSettings::add_change_handler(this); @@ -320,6 +322,11 @@ void View::remove_decode_signal(shared_ptr signal) } #endif +shared_ptr View::get_signal_under_mouse_cursor() const +{ + return signal_under_mouse_cursor_; +} + View* View::view() { return this; @@ -853,6 +860,107 @@ const QPoint& View::hover_point() const return hover_point_; } +int64_t View::get_nearest_level_change(const QPoint &p) +{ + // Is snapping disabled? + if (snap_distance_ == 0) + return -1; + + struct entry_t { + entry_t(shared_ptr s) : + signal(s), delta(numeric_limits::max()), sample(-1), is_dense(false) {} + shared_ptr signal; + int64_t delta; + int64_t sample; + bool is_dense; + }; + + vector list; + + // Create list of signals to consider + if (signal_under_mouse_cursor_) + list.emplace_back(signal_under_mouse_cursor_); + else + for (shared_ptr s : signals_) { + if (!s->enabled()) + continue; + + list.emplace_back(s); + } + + // Get data for listed signals + for (entry_t &e : list) { + // Calculate sample number from cursor position + const double samples_per_pixel = e.signal->base()->get_samplerate() * scale(); + const int64_t x_offset = offset().convert_to() / scale(); + const int64_t sample_num = max(((x_offset + p.x()) * samples_per_pixel), 0.0); + + vector edges = + e.signal->get_nearest_level_changes(sample_num); + + if (edges.empty()) + continue; + + // Check first edge + const int64_t first_sample_delta = abs(sample_num - edges.front().first); + const int64_t first_delta = first_sample_delta / samples_per_pixel; + e.delta = first_delta; + e.sample = edges.front().first; + + // Check second edge if available + if (edges.size() == 2) { + // Note: -1 because this is usually the right edge and sample points are left-aligned + const int64_t second_sample_delta = abs(sample_num - edges.back().first - 1); + const int64_t second_delta = second_sample_delta / samples_per_pixel; + + // If both edges are too close, we mark this signal as being dense + if ((first_delta + second_delta) <= snap_distance_) + e.is_dense = true; + + if (second_delta < first_delta) { + e.delta = second_delta; + e.sample = edges.back().first; + } + } + } + + // Look for the best match: non-dense first, then dense + entry_t *match = nullptr; + + for (entry_t &e : list) { + if (e.delta > snap_distance_ || e.is_dense) + continue; + + if (match) { + if (e.delta < match->delta) + match = &e; + } else + match = &e; + } + + if (!match) { + for (entry_t &e : list) { + if (!e.is_dense) + continue; + + if (match) { + if (e.delta < match->delta) + match = &e; + } else + match = &e; + } + } + + if (match) { + // Somewhat ugly hack to make TimeItem::drag_by() work + signal_under_mouse_cursor_ = match->signal; + + return match->sample; + } + + return -1; +} + void View::restack_all_trace_tree_items() { // Make a list of owners that is sorted from deepest first @@ -884,6 +992,11 @@ void View::on_setting_changed(const QString &key, const QVariant &value) { if (key == GlobalSettings::Key_View_TriggerIsZeroTime) on_settingViewTriggerIsZeroTime_changed(value); + + if (key == GlobalSettings::Key_View_SnapDistance) { + GlobalSettings settings; + snap_distance_ = settings.value(GlobalSettings::Key_View_SnapDistance).toInt(); + } } void View::trigger_event(int segment_id, util::Timestamp location) @@ -1215,7 +1328,10 @@ bool View::eventFilter(QObject *object, QEvent *event) if (object == viewport_) hover_point_ = mouse_event->pos(); else if (object == ruler_) - hover_point_ = QPoint(mouse_event->x(), 0); + // Adjust the hover point's y coordinate so that it's relative to + // the top of the viewport. The result may be negative. + hover_point_ = QPoint(mouse_event->pos().x(), + mouse_event->pos().y() - ruler_->sizeHint().height()); else if (object == header_) hover_point_ = QPoint(0, mouse_event->y()); else @@ -1266,7 +1382,7 @@ void View::contextMenuEvent(QContextMenuEvent *event) QMenu *menu = r->create_view_context_menu(this, pos); if (menu) - menu->exec(event->globalPos()); + menu->popup(event->globalPos()); } void View::resizeEvent(QResizeEvent* event) @@ -1280,11 +1396,24 @@ void View::resizeEvent(QResizeEvent* event) void View::update_hover_point() { + // Determine signal that the mouse cursor is hovering over + signal_under_mouse_cursor_.reset(); + for (shared_ptr s : signals_) { + const pair extents = s->v_extents(); + const int top = s->get_visual_y() + extents.first; + const int btm = s->get_visual_y() + extents.second; + if ((hover_point_.y() >= top) && (hover_point_.y() <= btm) + && s->base()->enabled()) + signal_under_mouse_cursor_ = s; + } + + // Update all trace tree items const vector> trace_tree_items( list_by_type()); for (shared_ptr r : trace_tree_items) r->hover_point_changed(hover_point_); + // Notify any other listeners hover_point_changed(hover_point_); }