+void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
+ int right) const
+{
+ using namespace pv::data;
+ using pv::data::decode::Decoder;
+
+ assert(_decoder_stack);
+
+ shared_ptr<Logic> data;
+ shared_ptr<LogicSignal> logic_signal;
+
+ const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
+
+ // We get the logic data of the first channel in the list.
+ // This works because we are currently assuming all
+ // LogicSignals have the same data/snapshot
+ for (const shared_ptr<Decoder> &dec : stack)
+ if (dec && !dec->channels().empty() &&
+ ((logic_signal = (*dec->channels().begin()).second)) &&
+ ((data = logic_signal->logic_data())))
+ break;
+
+ if (!data || data->get_snapshots().empty())
+ return;
+
+ const shared_ptr<LogicSnapshot> snapshot =
+ data->get_snapshots().front();
+ assert(snapshot);
+ const int64_t sample_count = (int64_t)snapshot->get_sample_count();
+ if (sample_count == 0)
+ return;
+
+ const int64_t samples_decoded = _decoder_stack->samples_decoded();
+ if (sample_count == samples_decoded)
+ return;
+
+ const int y = get_y();
+
+ const double samples_per_pixel = get_samples_per_pixel();
+ const double pixels_offset = get_pixels_offset();
+
+ const double start = max(samples_decoded /
+ samples_per_pixel - pixels_offset, left - 1.0);
+ const double end = min(sample_count / samples_per_pixel -
+ pixels_offset, right + 1.0);
+ const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
+
+ p.setPen(QPen(Qt::NoPen));
+ p.setBrush(Qt::white);
+ p.drawRect(no_decode_rect);
+
+ p.setPen(NoDecodeColour);
+ p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
+ p.drawRect(no_decode_rect);
+}
+
+double DecodeTrace::get_pixels_offset() const
+{
+ assert(_view);
+ assert(_decoder_stack);
+
+ const double scale = _view->scale();
+ assert(scale > 0);
+
+ return (_view->offset() - _decoder_stack->get_start_time()) / scale;
+}
+
+double DecodeTrace::get_samples_per_pixel() const
+{
+ assert(_view);
+ assert(_decoder_stack);
+
+ const double scale = _view->scale();
+ assert(scale > 0);
+
+ double samplerate = _decoder_stack->samplerate();
+
+ // Show sample rate as 1Hz when it is unknown
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ return samplerate * scale;
+}
+
+pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(int x_start, int x_end) const
+{
+ assert(_view);
+ assert(_decoder_stack);
+
+ const double samples_per_pixel = get_samples_per_pixel();
+ const double pixels_offset = get_pixels_offset();
+
+ uint64_t start, end;
+
+ start = (uint64_t)max((x_start + pixels_offset) * samples_per_pixel, 0.0);
+ end = (uint64_t)max((x_end + pixels_offset) * samples_per_pixel, 0.0);
+
+ return make_pair(start, end);
+}
+
+bool DecodeTrace::hover_point_is_over_trace()
+{
+ assert(_view);
+ assert(_row_height);
+
+ // Note: if _row_height is valid then _cur_row_headings is valid, too,
+ // as both are set in paint_mid().
+
+ // Note: hp.x will be 0 if the cursor is above the header area,
+ // so we set trace.left to 1 to exclude this.
+
+ QRect trace(1, get_y() - (_row_height/2),
+ _view->width(), _row_height * _cur_row_headings.size());
+
+ // Note: We don't need to check for _row_height being 0 here but
+ // we do it anyway to be more robust.
+
+ return _row_height && enabled() && trace.contains(_view->hover_point());
+}
+
+int DecodeTrace::get_row_at_hover_point()
+{
+ assert(_view);
+ assert(_row_height);
+ assert(_decoder_stack);
+
+ QPoint hp = _view->hover_point();
+ int hover_row = (hp.y() - get_y() + (_row_height/2)) / _row_height;
+
+ const vector<pv::data::decode::Row> rows(_decoder_stack->get_visible_rows());
+
+ return min(hover_row, (int)rows.size() - 1);
+}
+
+const QString DecodeTrace::get_annotation_at_hover_point()
+{
+ using namespace pv::data::decode;
+
+ assert(_view);
+ QPoint hp = _view->hover_point();
+
+ pair<uint64_t, uint64_t> sample_range = get_sample_range(hp.x(), hp.x() + 1);
+
+ assert(_decoder_stack);
+ const vector<pv::data::decode::Row> rows(_decoder_stack->get_visible_rows());
+
+ const int hover_row = get_row_at_hover_point();
+
+ vector<pv::data::decode::Annotation> annotations;
+
+ _decoder_stack->get_annotation_subset(annotations, rows[hover_row],
+ sample_range.first, sample_range.second);
+
+ return (annotations.empty()) ?
+ QString() : annotations[0].annotations().front();
+}
+
+void DecodeTrace::show_hover_annotation()
+{
+ QString ann = get_annotation_at_hover_point();
+
+ assert(_view);
+ assert(_row_height);
+ assert(_text_height);
+
+ if (!ann.isEmpty()) {
+ const int hover_row = get_row_at_hover_point();
+
+ // Make sure the tool tip doesn't overlap with the mouse cursor.
+ // If it did, the tool tip would constantly hide and re-appear.
+ QPoint hp = _view->hover_point();
+ hp.setY(get_y() - (_row_height/2) +
+ (hover_row * _row_height) - _text_height);
+
+ QToolTip::showText(_view->mapToGlobal(hp), ann);
+ } else
+ hide_hover_annotation();
+}
+
+void DecodeTrace::hide_hover_annotation()