X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fviews%2Ftrace%2Fview.cpp;h=f1d7aa3dfca872a29bc81f5c0c2c444e4a34e03e;hp=b9872130890f4e124c9cb052680f852e68124757;hb=f4ab4b5c657e5613caba82feaa81a8a400e4f331;hpb=eeceee9955e7db4ac777d49d1b7a766069476b08 diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp index b9872130..f1d7aa3d 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); @@ -239,6 +241,7 @@ void View::reset_view_state() cursors_ = make_shared(*this); next_flag_text_ = 'A'; trigger_markers_.clear(); + hover_widget_ = nullptr; hover_point_ = QPoint(-1, -1); scroll_needs_defaults_ = true; saved_v_offset_ = 0; @@ -345,6 +348,11 @@ const Viewport* View::viewport() const return viewport_; } +const Ruler* View::ruler() const +{ + return ruler_; +} + void View::save_settings(QSettings &settings) const { settings.setValue("scale", scale_); @@ -367,7 +375,7 @@ void View::save_settings(QSettings &settings) const settings.setValue("offset", QString::fromStdString(ss.str())); } - for (shared_ptr signal : signals_) { + for (const shared_ptr& signal : signals_) { settings.beginGroup(signal->base()->internal_name()); signal->save_settings(settings); settings.endGroup(); @@ -449,7 +457,7 @@ vector< shared_ptr > View::time_items() const items.push_back(cursors_->second()); } - for (auto trigger_marker : trigger_markers_) + for (auto& trigger_marker : trigger_markers_) items.push_back(trigger_marker); return items; @@ -588,10 +596,10 @@ void View::set_current_segment(uint32_t segment_id) { current_segment_ = segment_id; - for (shared_ptr signal : signals_) + for (const shared_ptr& signal : signals_) signal->set_current_segment(current_segment_); #ifdef ENABLE_DECODE - for (shared_ptr dt : decode_traces_) + for (shared_ptr& dt : decode_traces_) dt->set_current_segment(current_segment_); #endif @@ -627,7 +635,7 @@ void View::set_segment_display_mode(Trace::SegmentDisplayMode mode) { segment_display_mode_ = mode; - for (shared_ptr signal : signals_) + for (const shared_ptr& signal : signals_) signal->set_segment_display_mode(mode); uint32_t last_segment = session_.get_segment_count() - 1; @@ -736,7 +744,7 @@ set< shared_ptr > View::get_visible_data() const { // Make a set of all the visible data objects set< shared_ptr > visible_data; - for (const shared_ptr sig : signals_) + for (const shared_ptr& sig : signals_) if (sig->enabled()) visible_data.insert(sig->data()); @@ -747,9 +755,9 @@ pair View::get_time_extents() const { boost::optional left_time, right_time; const set< shared_ptr > visible_data = get_visible_data(); - for (const shared_ptr d : visible_data) { + for (const shared_ptr& d : visible_data) { const vector< shared_ptr > segments = d->segments(); - for (const shared_ptr &s : segments) { + for (const shared_ptr& s : segments) { double samplerate = s->samplerate(); samplerate = (samplerate <= 0.0) ? 1.0 : samplerate; @@ -858,41 +866,110 @@ const QPoint& View::hover_point() const return hover_point_; } -int64_t View::get_nearest_level_change(const QPoint &p) const +const QWidget* View::hover_widget() const { - shared_ptr signal = signal_under_mouse_cursor_; + return hover_widget_; +} - if (!signal) +int64_t View::get_nearest_level_change(const QPoint &p) +{ + // Is snapping disabled? + if (snap_distance_ == 0) return -1; - // Calculate sample number from cursor position - const double samples_per_pixel = 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); + 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; + }; - // Query for nearest level changes - vector edges = - signal->get_nearest_level_changes(sample_num); + vector list; - if (edges.size() != 2) - return -1; + // 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; - // We received absolute sample numbers, make them relative - const int64_t left_sample_delta = sample_num - edges.front().first; - const int64_t right_sample_delta = edges.back().first - sample_num - 1; + 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; + } - const int64_t left_delta = left_sample_delta / samples_per_pixel; - const int64_t right_delta = right_sample_delta / samples_per_pixel; + if (!match) { + for (entry_t &e : list) { + if (!e.is_dense) + continue; + + if (match) { + if (e.delta < match->delta) + match = &e; + } else + match = &e; + } + } - int64_t nearest = -1; + if (match) { + // Somewhat ugly hack to make TimeItem::drag_by() work + signal_under_mouse_cursor_ = match->signal; - // Only use closest left or right edge if they're close to the cursor - if ((left_delta < right_delta) && (left_delta < 15)) - nearest = edges.front().first; - if ((left_delta >= right_delta) && (right_delta < 15)) - nearest = edges.back().first; + return match->sample; + } - return nearest; + return -1; } void View::restack_all_trace_tree_items() @@ -926,6 +1003,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) @@ -1178,8 +1260,8 @@ TraceTreeItemOwner* View::find_prevalent_trace_group( vector owner_list; // Make a set and a list of all the owners - for (const auto &channel : group->channels()) { - for (auto entry : signal_map) { + for (const auto& channel : group->channels()) { + for (auto& entry : signal_map) { if (entry.first->channel() == channel) { TraceTreeItemOwner *const o = (entry.second)->owner(); owner_list.push_back(o); @@ -1212,8 +1294,8 @@ vector< shared_ptr > View::extract_new_traces_for_channels( { vector< shared_ptr > filtered_traces; - for (const auto &channel : channels) { - for (auto entry : signal_map) { + for (const auto& channel : channels) { + for (auto& entry : signal_map) { if (entry.first->channel() == channel) { shared_ptr trace = entry.second; const auto list_iter = add_list.find(trace); @@ -1234,7 +1316,7 @@ void View::determine_time_unit() // Check whether we know the sample rate and hence can use time as the unit if (time_unit_ == util::TimeUnit::Samples) { // Check all signals but... - for (const shared_ptr signal : signals_) { + for (const shared_ptr& signal : signals_) { const shared_ptr data = signal->data(); // ...only check first segment of each @@ -1253,11 +1335,14 @@ bool View::eventFilter(QObject *object, QEvent *event) const QEvent::Type type = event->type(); if (type == QEvent::MouseMove) { + if (object) + hover_widget_ = qobject_cast(object); + const QMouseEvent *const mouse_event = (QMouseEvent*)event; if (object == viewport_) hover_point_ = mouse_event->pos(); else if (object == ruler_) - hover_point_ = QPoint(mouse_event->x(), 0); + hover_point_ = mouse_event->pos(); else if (object == header_) hover_point_ = QPoint(0, mouse_event->y()); else @@ -1308,7 +1393,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) @@ -1324,23 +1409,25 @@ 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; + if (hover_widget_ == this) { + for (const 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) + for (const shared_ptr& r : trace_tree_items) r->hover_point_changed(hover_point_); // Notify any other listeners - hover_point_changed(hover_point_); + hover_point_changed(hover_widget_, hover_point_); } void View::row_item_appearance_changed(bool label, bool content) @@ -1471,12 +1558,12 @@ void View::signals_changed() // Make a look-up table of sigrok Channels to pulseview Signals unordered_map, shared_ptr > signal_map; - for (const shared_ptr &sig : signals_) + for (const shared_ptr& sig : signals_) signal_map[sig->base()] = sig; // Populate channel groups if (sr_dev) - for (auto entry : sr_dev->channel_groups()) { + for (auto& entry : sr_dev->channel_groups()) { const shared_ptr &group = entry.second; if (group->channels().size() <= 1) @@ -1502,7 +1589,7 @@ void View::signals_changed() // Add the traces to the group const pair prev_v_extents = owner->v_extents(); int offset = prev_v_extents.second - prev_v_extents.first; - for (shared_ptr trace : new_traces_in_group) { + for (const shared_ptr& trace : new_traces_in_group) { assert(trace); owner->add_child_item(trace); @@ -1537,7 +1624,7 @@ void View::signals_changed() if (non_grouped_logic_signals.size() > 0) { const shared_ptr non_grouped_trace_group( make_shared()); - for (shared_ptr trace : non_grouped_logic_signals) + for (const shared_ptr& trace : non_grouped_logic_signals) non_grouped_trace_group->add_child_item(trace); non_grouped_trace_group->restack_items(); @@ -1555,7 +1642,7 @@ void View::signals_changed() add_traces.begin(), add_traces.end()); // Remove any removed traces - for (shared_ptr trace : remove_traces) { + for (const shared_ptr& trace : remove_traces) { TraceTreeItemOwner *const owner = trace->owner(); assert(owner); owner->remove_child_item(trace);