]> sigrok.org Git - pulseview.git/blame - pv/view/decodetrace.cpp
Only show sampling points when zoomed in far enough.
[pulseview.git] / pv / view / decodetrace.cpp
CommitLineData
55d3603d
JH
1/*
2 * This file is part of the PulseView project.
3 *
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
efdec55a 17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
55d3603d
JH
18 */
19
20extern "C" {
21#include <libsigrokdecode/libsigrokdecode.h>
22}
23
c3a740dd
JH
24#include <mutex>
25
06bb4e6a
JH
26#include <extdef.h>
27
53e35b2d
JH
28#include <tuple>
29
a855d71e 30#include <boost/functional/hash.hpp>
e71eb81c
JH
31#include <boost/thread/locks.hpp>
32#include <boost/thread/shared_mutex.hpp>
b213ef09 33
c51482b3 34#include <QAction>
d7c0ca4a 35#include <QApplication>
4e5a4405
JH
36#include <QComboBox>
37#include <QFormLayout>
38#include <QLabel>
b213ef09 39#include <QMenu>
ce94e4fd 40#include <QPushButton>
e2f90c50 41#include <QToolTip>
c51482b3 42
2acdb232
JH
43#include "decodetrace.hpp"
44
f65cd27b 45#include <pv/session.hpp>
51307fd6 46#include <pv/strnatcmp.hpp>
2acdb232
JH
47#include <pv/data/decoderstack.hpp>
48#include <pv/data/decode/decoder.hpp>
49#include <pv/data/logic.hpp>
f3d66e52 50#include <pv/data/logicsegment.hpp>
2acdb232 51#include <pv/data/decode/annotation.hpp>
2acdb232
JH
52#include <pv/view/view.hpp>
53#include <pv/view/viewport.hpp>
54#include <pv/widgets/decodergroupbox.hpp>
55#include <pv/widgets/decodermenu.hpp>
119aff65 56
aca64cac
JH
57using boost::shared_lock;
58using boost::shared_mutex;
f9abf97e 59using std::dynamic_pointer_cast;
819f4c25 60using std::list;
c3a740dd 61using std::lock_guard;
7f8517f6 62using std::make_pair;
819f4c25 63using std::max;
a5d93c27 64using std::make_pair;
819f4c25
JH
65using std::map;
66using std::min;
7f8517f6 67using std::pair;
f9abf97e 68using std::shared_ptr;
067bb624 69using std::make_shared;
53e35b2d 70using std::tie;
78b0af3e 71using std::unordered_set;
819f4c25 72using std::vector;
55d3603d
JH
73
74namespace pv {
f4e57597
SA
75namespace views {
76namespace TraceView {
55d3603d 77
b9329558 78const QColor DecodeTrace::DecodeColours[4] = {
06bb4e6a
JH
79 QColor(0xEF, 0x29, 0x29), // Red
80 QColor(0xFC, 0xE9, 0x4F), // Yellow
81 QColor(0x8A, 0xE2, 0x34), // Green
82 QColor(0x72, 0x9F, 0xCF) // Blue
83};
84
b9329558 85const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
5dfeb70f 86const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
ad50ac1a 87
88908838 88const int DecodeTrace::ArrowSize = 4;
06e810f2 89const double DecodeTrace::EndCapWidth = 5;
aee9dcf3 90const int DecodeTrace::RowTitleMargin = 10;
06e810f2
JH
91const int DecodeTrace::DrawPadding = 100;
92
287d607f
JH
93const QColor DecodeTrace::Colours[16] = {
94 QColor(0xEF, 0x29, 0x29),
95 QColor(0xF6, 0x6A, 0x32),
96 QColor(0xFC, 0xAE, 0x3E),
97 QColor(0xFB, 0xCA, 0x47),
98 QColor(0xFC, 0xE9, 0x4F),
99 QColor(0xCD, 0xF0, 0x40),
100 QColor(0x8A, 0xE2, 0x34),
101 QColor(0x4E, 0xDC, 0x44),
102 QColor(0x55, 0xD7, 0x95),
103 QColor(0x64, 0xD1, 0xD2),
104 QColor(0x72, 0x9F, 0xCF),
105 QColor(0xD4, 0x76, 0xC4),
106 QColor(0x9D, 0x79, 0xB9),
107 QColor(0xAD, 0x7F, 0xA8),
108 QColor(0xC2, 0x62, 0x9B),
109 QColor(0xD7, 0x47, 0x6F)
110};
111
112const QColor DecodeTrace::OutlineColours[16] = {
113 QColor(0x77, 0x14, 0x14),
114 QColor(0x7B, 0x35, 0x19),
115 QColor(0x7E, 0x57, 0x1F),
116 QColor(0x7D, 0x65, 0x23),
117 QColor(0x7E, 0x74, 0x27),
118 QColor(0x66, 0x78, 0x20),
119 QColor(0x45, 0x71, 0x1A),
120 QColor(0x27, 0x6E, 0x22),
121 QColor(0x2A, 0x6B, 0x4A),
122 QColor(0x32, 0x68, 0x69),
123 QColor(0x39, 0x4F, 0x67),
124 QColor(0x6A, 0x3B, 0x62),
125 QColor(0x4E, 0x3C, 0x5C),
126 QColor(0x56, 0x3F, 0x54),
127 QColor(0x61, 0x31, 0x4D),
128 QColor(0x6B, 0x23, 0x37)
06e810f2
JH
129};
130
2b81ae46 131DecodeTrace::DecodeTrace(pv::Session &session,
bb7dd726 132 shared_ptr<data::SignalBase> signalbase, int index) :
bf0edd2b 133 Trace(signalbase),
8dbbc7f0 134 session_(session),
8dbbc7f0 135 row_height_(0),
eee89ff8 136 max_visible_rows_(0),
8dbbc7f0
JH
137 delete_mapper_(this),
138 show_hide_mapper_(this)
55d3603d 139{
bb7dd726 140 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 141 base_->decoder_stack();
e0fc5810 142
752281db
SA
143 // Determine shortest string we want to see displayed in full
144 QFontMetrics m(QApplication::font());
145 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
146
455bc29a
SA
147 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
148 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
9cef9567 149
bb7dd726 150 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
9cef9567 151 this, SLOT(on_new_decode_data()));
8dbbc7f0 152 connect(&delete_mapper_, SIGNAL(mapped(int)),
613d097c 153 this, SLOT(on_delete_decoder(int)));
8dbbc7f0 154 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
dd048a7e 155 this, SLOT(on_show_hide_decoder(int)));
55d3603d
JH
156}
157
b9329558 158bool DecodeTrace::enabled() const
55d3603d
JH
159{
160 return true;
161}
162
bb7dd726 163std::shared_ptr<data::SignalBase> DecodeTrace::base() const
b6b267bb 164{
bb7dd726 165 return base_;
b6b267bb
JH
166}
167
a5d93c27
JH
168pair<int, int> DecodeTrace::v_extents() const
169{
5b5fa4da 170 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
796e1360 171
e40a79cb
SA
172 // Make an empty decode trace appear symmetrical
173 const int row_count = max(1, max_visible_rows_);
174
175 return make_pair(-row_height, row_height * row_count);
a5d93c27
JH
176}
177
5b5fa4da 178void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
fe08b6e8 179{
3eb29afd 180 Trace::paint_back(p, pp);
97904bf7 181 paint_axis(p, pp, get_visual_y());
fe08b6e8
JH
182}
183
5b5fa4da 184void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
55d3603d 185{
f9101a91 186 using namespace pv::data::decode;
9472f447 187
bb7dd726 188 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 189 base_->decoder_stack();
bb7dd726 190
0ce3d18c
JH
191 const int text_height = ViewItemPaintParams::text_height();
192 row_height_ = (text_height * 6) / 4;
193 const int annotation_height = (text_height * 5) / 4;
5dfeb70f 194
bb7dd726
SA
195 assert(decoder_stack);
196 const QString err = decoder_stack->error_message();
2ad82c2e 197 if (!err.isEmpty()) {
3eb29afd
JH
198 draw_unresolved_period(
199 p, annotation_height, pp.left(), pp.right());
200 draw_error(p, err, pp);
5dfeb70f
JH
201 return;
202 }
203
cd0c558b
SA
204 // Set default pen to allow for text width calculation
205 p.setPen(Qt::black);
206
f9101a91 207 // Iterate through the rows
be9e7b4b 208 int y = get_visual_y();
3eb29afd
JH
209 pair<uint64_t, uint64_t> sample_range = get_sample_range(
210 pp.left(), pp.right());
5dfeb70f 211
bb7dd726 212 const vector<Row> rows(decoder_stack->get_visible_rows());
7f8517f6 213
8dbbc7f0 214 visible_rows_.clear();
4fb5fb99 215 for (const Row& row : rows) {
aee9dcf3
SA
216 // Cache the row title widths
217 int row_title_width;
218 try {
219 row_title_width = row_title_widths_.at(row);
220 } catch (std::out_of_range) {
221 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
222 RowTitleMargin;
223 row_title_widths_[row] = w;
224 row_title_width = w;
225 }
226
227 // Determine the row's color
287d607f
JH
228 size_t base_colour = 0x13579BDF;
229 boost::hash_combine(base_colour, this);
230 boost::hash_combine(base_colour, row.decoder());
231 boost::hash_combine(base_colour, row.row());
232 base_colour >>= 16;
233
f9101a91 234 vector<Annotation> annotations;
bb7dd726 235 decoder_stack->get_annotation_subset(annotations, row,
7f8517f6 236 sample_range.first, sample_range.second);
f9101a91 237 if (!annotations.empty()) {
50631798 238 draw_annotations(annotations, p, annotation_height, pp, y,
aee9dcf3 239 base_colour, row_title_width);
50631798 240
8dbbc7f0 241 y += row_height_;
88908838 242
4fb5fb99 243 visible_rows_.push_back(row);
f9101a91 244 }
7e674e43 245 }
5dfeb70f 246
f9101a91 247 // Draw the hatching
3eb29afd 248 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
eee89ff8 249
a303c2d8
SA
250 if ((int)visible_rows_.size() > max_visible_rows_)
251 owner_->extents_changed(false, true);
252
eee89ff8
SA
253 // Update the maximum row count if needed
254 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
55d3603d
JH
255}
256
5b5fa4da 257void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
88908838
JH
258{
259 using namespace pv::data::decode;
260
8dbbc7f0 261 assert(row_height_);
88908838 262
2ad82c2e 263 for (size_t i = 0; i < visible_rows_.size(); i++) {
8dbbc7f0 264 const int y = i * row_height_ + get_visual_y();
88908838
JH
265
266 p.setPen(QPen(Qt::NoPen));
267 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
268
2ad82c2e 269 if (i != 0) {
88908838 270 const QPointF points[] = {
3eb29afd
JH
271 QPointF(pp.left(), y - ArrowSize),
272 QPointF(pp.left() + ArrowSize, y),
273 QPointF(pp.left(), y + ArrowSize)
88908838
JH
274 };
275 p.drawPolygon(points, countof(points));
276 }
277
3eb29afd
JH
278 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
279 pp.right() - pp.left(), row_height_);
8dbbc7f0 280 const QString h(visible_rows_[i].title());
88908838
JH
281 const int f = Qt::AlignLeft | Qt::AlignVCenter |
282 Qt::TextDontClip;
283
284 // Draw the outline
285 p.setPen(QApplication::palette().color(QPalette::Base));
286 for (int dx = -1; dx <= 1; dx++)
287 for (int dy = -1; dy <= 1; dy++)
288 if (dx != 0 && dy != 0)
289 p.drawText(r.translated(dx, dy), f, h);
290
291 // Draw the text
292 p.setPen(QApplication::palette().color(QPalette::WindowText));
293 p.drawText(r, f, h);
294 }
295}
296
b9329558 297void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
4e5a4405 298{
613d097c
JH
299 using pv::data::decode::Decoder;
300
bb7dd726 301 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 302 base_->decoder_stack();
bb7dd726 303
4e5a4405
JH
304 assert(form);
305 assert(parent);
bb7dd726 306 assert(decoder_stack);
4e5a4405 307
7491a29f 308 // Add the standard options
4e5a4405
JH
309 Trace::populate_popup_form(parent, form);
310
7491a29f 311 // Add the decoder options
8dbbc7f0
JH
312 bindings_.clear();
313 channel_selectors_.clear();
314 decoder_forms_.clear();
4e5a4405 315
bb7dd726 316 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
4e5a4405 317
2ad82c2e 318 if (stack.empty()) {
5069084a
JH
319 QLabel *const l = new QLabel(
320 tr("<p><i>No decoders in the stack</i></p>"));
321 l->setAlignment(Qt::AlignCenter);
322 form->addRow(l);
2ad82c2e 323 } else {
f46e495e 324 auto iter = stack.cbegin();
5069084a
JH
325 for (int i = 0; i < (int)stack.size(); i++, iter++) {
326 shared_ptr<Decoder> dec(*iter);
327 create_decoder_form(i, dec, parent, form);
328 }
329
330 form->addRow(new QLabel(
8bd26d8b 331 tr("<i>* Required channels</i>"), parent));
5069084a 332 }
4e5a4405 333
ce94e4fd 334 // Add stacking button
ce94e4fd
JH
335 pv::widgets::DecoderMenu *const decoder_menu =
336 new pv::widgets::DecoderMenu(parent);
7491a29f
JH
337 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
338 this, SLOT(on_stack_decoder(srd_decoder*)));
339
340 QPushButton *const stack_button =
341 new QPushButton(tr("Stack Decoder"), parent);
ce94e4fd
JH
342 stack_button->setMenu(decoder_menu);
343
344 QHBoxLayout *stack_button_box = new QHBoxLayout;
345 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
346 form->addRow(stack_button_box);
4e5a4405
JH
347}
348
b9329558 349QMenu* DecodeTrace::create_context_menu(QWidget *parent)
c51482b3
JH
350{
351 QMenu *const menu = Trace::create_context_menu(parent);
352
353 menu->addSeparator();
354
355 QAction *const del = new QAction(tr("Delete"), this);
a2d21018 356 del->setShortcuts(QKeySequence::Delete);
c51482b3
JH
357 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
358 menu->addAction(del);
359
360 return menu;
361}
362
50631798
SA
363void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
364 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
aee9dcf3 365 size_t base_colour, int row_title_width)
50631798
SA
366{
367 using namespace pv::data::decode;
368
369 vector<Annotation> a_block;
bdc2a99b 370 int p_end = INT_MIN;
50631798
SA
371
372 double samples_per_pixel, pixels_offset;
373 tie(pixels_offset, samples_per_pixel) =
374 get_pixels_offset_samples_per_pixel();
375
bdc2a99b
SA
376 // Sort the annotations by start sample so that decoders
377 // can't confuse us by creating annotations out of order
378 stable_sort(annotations.begin(), annotations.end(),
379 [](const Annotation &a, const Annotation &b) {
380 return a.start_sample() < b.start_sample(); });
381
50631798
SA
382 // Gather all annotations that form a visual "block" and draw them as such
383 for (const Annotation &a : annotations) {
384
bdc2a99b
SA
385 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
386 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
387 const int a_width = a_end - a_start;
388
389 const int delta = a_end - p_end;
390
391 bool a_is_separate = false;
392
393 // Annotation wider than the threshold for a useful label width?
752281db 394 if (a_width >= min_useful_label_width_) {
bdc2a99b
SA
395 for (const QString &ann_text : a.annotations()) {
396 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
397 // Annotation wide enough to fit a label? Don't put it in a block then
398 if (w <= a_width) {
399 a_is_separate = true;
400 break;
401 }
402 }
403 }
50631798 404
bdc2a99b
SA
405 // Were the previous and this annotation more than a pixel apart?
406 if ((abs(delta) > 1) || a_is_separate) {
407 // Block was broken, draw annotations that form the current block
408 if (a_block.size() == 1) {
aee9dcf3
SA
409 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
410 row_title_width);
bdc2a99b 411 }
50631798 412 else
bdc2a99b 413 draw_annotation_block(a_block, p, h, y, base_colour);
50631798
SA
414
415 a_block.clear();
416 }
417
bdc2a99b 418 if (a_is_separate) {
aee9dcf3 419 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
bdc2a99b
SA
420 // Next annotation must start a new block. delta will be > 1
421 // because we set p_end to INT_MIN but that's okay since
422 // a_block will be empty, so nothing will be drawn
423 p_end = INT_MIN;
424 } else {
425 a_block.push_back(a);
426 p_end = a_end;
427 }
50631798
SA
428 }
429
430 if (a_block.size() == 1)
aee9dcf3
SA
431 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
432 row_title_width);
50631798 433 else
33707990 434 draw_annotation_block(a_block, p, h, y, base_colour);
50631798
SA
435}
436
287d607f 437void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
5b5fa4da 438 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
aee9dcf3 439 size_t base_colour, int row_title_width) const
06e810f2 440{
53e35b2d
JH
441 double samples_per_pixel, pixels_offset;
442 tie(pixels_offset, samples_per_pixel) =
443 get_pixels_offset_samples_per_pixel();
7f8517f6 444
06e810f2
JH
445 const double start = a.start_sample() / samples_per_pixel -
446 pixels_offset;
447 const double end = a.end_sample() / samples_per_pixel -
448 pixels_offset;
287d607f
JH
449
450 const size_t colour = (base_colour + a.format()) % countof(Colours);
f765c3db
SA
451 p.setPen(OutlineColours[colour]);
452 p.setBrush(Colours[colour]);
06e810f2 453
3eb29afd 454 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
06e810f2
JH
455 return;
456
457 if (a.start_sample() == a.end_sample())
f765c3db 458 draw_instant(a, p, h, start, y);
06e810f2 459 else
f765c3db 460 draw_range(a, p, h, start, end, y, pp,
aee9dcf3 461 row_title_width);
06e810f2
JH
462}
463
33707990
SA
464void DecodeTrace::draw_annotation_block(
465 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
466 int y, size_t base_colour) const
50631798 467{
33707990
SA
468 using namespace pv::data::decode;
469
bdc2a99b
SA
470 if (annotations.empty())
471 return;
472
50631798
SA
473 double samples_per_pixel, pixels_offset;
474 tie(pixels_offset, samples_per_pixel) =
475 get_pixels_offset_samples_per_pixel();
476
33707990
SA
477 const double start = annotations.front().start_sample() /
478 samples_per_pixel - pixels_offset;
479 const double end = annotations.back().end_sample() /
480 samples_per_pixel - pixels_offset;
481
482 const double top = y + .5 - h / 2;
483 const double bottom = y + .5 + h / 2;
33707990
SA
484
485 const size_t colour = (base_colour + annotations.front().format()) %
486 countof(Colours);
487
488 // Check if all annotations are of the same type (i.e. we can use one color)
489 // or if we should use a neutral color (i.e. gray)
8c0302f5
JH
490 const int format = annotations.front().format();
491 const bool single_format = std::all_of(
492 annotations.begin(), annotations.end(),
493 [&](const Annotation &a) { return a.format() == format; });
50631798 494
33707990
SA
495 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
496 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
497 Qt::Dense4Pattern));
086f4df5
JH
498 p.drawRoundedRect(
499 QRectF(start, top, end - start, bottom - top), h/4, h/4);
50631798
SA
500}
501
06e810f2 502void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
f765c3db 503 int h, double x, int y) const
06e810f2
JH
504{
505 const QString text = a.annotations().empty() ?
506 QString() : a.annotations().back();
ea86bc4d 507 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
06e810f2
JH
508 0.0) + h;
509 const QRectF rect(x - w / 2, y - h / 2, w, h);
510
06e810f2
JH
511 p.drawRoundedRect(rect, h / 2, h / 2);
512
2a56e448 513 p.setPen(Qt::black);
06e810f2
JH
514 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
515}
516
517void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
f765c3db
SA
518 int h, double start, double end, int y, const ViewItemPaintParams &pp,
519 int row_title_width) const
06e810f2
JH
520{
521 const double top = y + .5 - h / 2;
522 const double bottom = y + .5 + h / 2;
523 const vector<QString> annotations = a.annotations();
524
06e810f2 525 // If the two ends are within 1 pixel, draw a vertical line
2ad82c2e 526 if (start + 1.0 > end) {
06e810f2
JH
527 p.drawLine(QPointF(start, top), QPointF(start, bottom));
528 return;
529 }
530
531 const double cap_width = min((end - start) / 4, EndCapWidth);
532
533 QPointF pts[] = {
534 QPointF(start, y + .5f),
535 QPointF(start + cap_width, top),
536 QPointF(end - cap_width, top),
537 QPointF(end, y + .5f),
538 QPointF(end - cap_width, bottom),
539 QPointF(start + cap_width, bottom)
540 };
541
542 p.drawConvexPolygon(pts, countof(pts));
543
544 if (annotations.empty())
545 return;
546
7352be72
SA
547 const int ann_start = start + cap_width;
548 const int ann_end = end - cap_width;
549
aee9dcf3 550 const int real_start = std::max(ann_start, pp.left() + row_title_width);
7352be72 551 const int real_end = std::min(ann_end, pp.right());
7352be72
SA
552 const int real_width = real_end - real_start;
553
554 QRectF rect(real_start, y - h / 2, real_width, h);
0f290e9b
JH
555 if (rect.width() <= 4)
556 return;
557
2a56e448 558 p.setPen(Qt::black);
06e810f2
JH
559
560 // Try to find an annotation that will fit
561 QString best_annotation;
562 int best_width = 0;
563
d9aecf1f 564 for (const QString &a : annotations) {
06e810f2
JH
565 const int w = p.boundingRect(QRectF(), 0, a).width();
566 if (w <= rect.width() && w > best_width)
567 best_annotation = a, best_width = w;
568 }
569
570 if (best_annotation.isEmpty())
571 best_annotation = annotations.back();
572
573 // If not ellide the last in the list
574 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
575 best_annotation, Qt::ElideRight, rect.width()));
576}
577
b9329558 578void DecodeTrace::draw_error(QPainter &p, const QString &message,
5b5fa4da 579 const ViewItemPaintParams &pp)
ad50ac1a 580{
be9e7b4b 581 const int y = get_visual_y();
ad50ac1a
JH
582
583 p.setPen(ErrorBgColour.darker());
584 p.setBrush(ErrorBgColour);
585
586 const QRectF bounding_rect =
3eb29afd 587 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
ad50ac1a
JH
588 const QRectF text_rect = p.boundingRect(bounding_rect,
589 Qt::AlignCenter, message);
590 const float r = text_rect.height() / 4;
591
592 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
593 Qt::AbsoluteSize);
594
2a56e448 595 p.setPen(Qt::black);
ad50ac1a
JH
596 p.drawText(text_rect, message);
597}
598
5dfeb70f 599void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
7f8517f6 600 int right) const
5dfeb70f
JH
601{
602 using namespace pv::data;
603 using pv::data::decode::Decoder;
604
53e35b2d
JH
605 double samples_per_pixel, pixels_offset;
606
bb7dd726 607 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 608 base_->decoder_stack();
bb7dd726
SA
609
610 assert(decoder_stack);
5dfeb70f
JH
611
612 shared_ptr<Logic> data;
04394ded 613 shared_ptr<data::SignalBase> signalbase;
5dfeb70f 614
bb7dd726 615 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
5dfeb70f 616
6ac6242b 617 // We get the logic data of the first channel in the list.
5dfeb70f 618 // This works because we are currently assuming all
f3d66e52 619 // LogicSignals have the same data/segment
d9aecf1f 620 for (const shared_ptr<Decoder> &dec : stack)
8bd26d8b 621 if (dec && !dec->channels().empty() &&
04394ded
SA
622 ((signalbase = (*dec->channels().begin()).second)) &&
623 ((data = signalbase->logic_data())))
5dfeb70f
JH
624 break;
625
f3d66e52 626 if (!data || data->logic_segments().empty())
5dfeb70f
JH
627 return;
628
f3d66e52
JH
629 const shared_ptr<LogicSegment> segment =
630 data->logic_segments().front();
631 assert(segment);
632 const int64_t sample_count = (int64_t)segment->get_sample_count();
5dfeb70f
JH
633 if (sample_count == 0)
634 return;
635
bb7dd726 636 const int64_t samples_decoded = decoder_stack->samples_decoded();
5dfeb70f
JH
637 if (sample_count == samples_decoded)
638 return;
639
be9e7b4b 640 const int y = get_visual_y();
7f8517f6 641
53e35b2d
JH
642 tie(pixels_offset, samples_per_pixel) =
643 get_pixels_offset_samples_per_pixel();
7f8517f6 644
5dfeb70f
JH
645 const double start = max(samples_decoded /
646 samples_per_pixel - pixels_offset, left - 1.0);
647 const double end = min(sample_count / samples_per_pixel -
648 pixels_offset, right + 1.0);
649 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
650
651 p.setPen(QPen(Qt::NoPen));
652 p.setBrush(Qt::white);
653 p.drawRect(no_decode_rect);
654
655 p.setPen(NoDecodeColour);
656 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
657 p.drawRect(no_decode_rect);
658}
659
53e35b2d 660pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
7f8517f6 661{
bb7dd726 662 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 663 base_->decoder_stack();
bb7dd726 664
8dbbc7f0 665 assert(owner_);
bb7dd726 666 assert(decoder_stack);
7f8517f6 667
8dbbc7f0 668 const View *view = owner_->view();
eae6e30a
JH
669 assert(view);
670
671 const double scale = view->scale();
7f8517f6
SA
672 assert(scale > 0);
673
53e35b2d 674 const double pixels_offset =
bb7dd726 675 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
7f8517f6 676
bb7dd726 677 double samplerate = decoder_stack->samplerate();
7f8517f6
SA
678
679 // Show sample rate as 1Hz when it is unknown
680 if (samplerate == 0.0)
681 samplerate = 1.0;
682
53e35b2d 683 return make_pair(pixels_offset, samplerate * scale);
7f8517f6
SA
684}
685
db1bf6bf
JH
686pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
687 int x_start, int x_end) const
7f8517f6 688{
53e35b2d
JH
689 double samples_per_pixel, pixels_offset;
690 tie(pixels_offset, samples_per_pixel) =
691 get_pixels_offset_samples_per_pixel();
7f8517f6 692
db1bf6bf
JH
693 const uint64_t start = (uint64_t)max(
694 (x_start + pixels_offset) * samples_per_pixel, 0.0);
695 const uint64_t end = (uint64_t)max(
696 (x_end + pixels_offset) * samples_per_pixel, 0.0);
7f8517f6
SA
697
698 return make_pair(start, end);
699}
700
117cdea3 701int DecodeTrace::get_row_at_point(const QPoint &point)
e2f90c50 702{
8dbbc7f0 703 if (!row_height_)
117cdea3 704 return -1;
e2f90c50 705
99029fda
SA
706 const int y = (point.y() - get_visual_y() + row_height_ / 2);
707
708 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
709 if (y < 0)
710 return -1;
711
712 const int row = y / row_height_;
713
714 if (row >= (int)visible_rows_.size())
117cdea3 715 return -1;
e2f90c50 716
117cdea3 717 return row;
e2f90c50
SA
718}
719
117cdea3 720const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
e2f90c50
SA
721{
722 using namespace pv::data::decode;
723
117cdea3
JH
724 if (!enabled())
725 return QString();
e2f90c50 726
117cdea3
JH
727 const pair<uint64_t, uint64_t> sample_range =
728 get_sample_range(point.x(), point.x() + 1);
729 const int row = get_row_at_point(point);
730 if (row < 0)
731 return QString();
e2f90c50
SA
732
733 vector<pv::data::decode::Annotation> annotations;
734
bb7dd726 735 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 736 base_->decoder_stack();
bb7dd726
SA
737
738 assert(decoder_stack);
739 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
e2f90c50
SA
740 sample_range.first, sample_range.second);
741
742 return (annotations.empty()) ?
743 QString() : annotations[0].annotations().front();
744}
745
117cdea3
JH
746void DecodeTrace::hover_point_changed()
747{
8dbbc7f0 748 assert(owner_);
eae6e30a 749
8dbbc7f0 750 const View *const view = owner_->view();
eae6e30a
JH
751 assert(view);
752
753 QPoint hp = view->hover_point();
117cdea3 754 QString ann = get_annotation_at_point(hp);
e2f90c50 755
eae6e30a 756 assert(view);
e2f90c50 757
8b9df0ad 758 if (!row_height_ || ann.isEmpty()) {
ebdfa094 759 QToolTip::hideText();
117cdea3
JH
760 return;
761 }
6e6881e2 762
117cdea3 763 const int hover_row = get_row_at_point(hp);
6e6881e2 764
117cdea3
JH
765 QFontMetrics m(QToolTip::font());
766 const QRect text_size = m.boundingRect(QRect(), 0, ann);
e2f90c50 767
117cdea3
JH
768 // This is OS-specific and unfortunately we can't query it, so
769 // use an approximation to at least try to minimize the error.
770 const int padding = 8;
6e6881e2 771
117cdea3
JH
772 // Make sure the tool tip doesn't overlap with the mouse cursor.
773 // If it did, the tool tip would constantly hide and re-appear.
774 // We also push it up by one row so that it appears above the
775 // decode trace, not below.
776 hp.setX(hp.x() - (text_size.width() / 2) - padding);
6e6881e2 777
8dbbc7f0
JH
778 hp.setY(get_visual_y() - (row_height_ / 2) +
779 (hover_row * row_height_) -
99029fda 780 row_height_ - text_size.height() - padding);
e2f90c50 781
eae6e30a 782 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
9555ca8b
SA
783}
784
613d097c
JH
785void DecodeTrace::create_decoder_form(int index,
786 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
787 QFormLayout *form)
7491a29f 788{
8bd26d8b 789 const GSList *l;
7491a29f
JH
790
791 assert(dec);
792 const srd_decoder *const decoder = dec->decoder();
793 assert(decoder);
794
ff59fa2c
SA
795 const bool decoder_deletable = index > 0;
796
204bae45 797 pv::widgets::DecoderGroupBox *const group =
27e8df22 798 new pv::widgets::DecoderGroupBox(
ff59fa2c 799 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
dd048a7e 800 group->set_decoder_visible(dec->shown());
613d097c 801
ff59fa2c
SA
802 if (decoder_deletable) {
803 delete_mapper_.setMapping(group, index);
804 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
805 }
613d097c 806
8dbbc7f0 807 show_hide_mapper_.setMapping(group, index);
dd048a7e 808 connect(group, SIGNAL(show_hide_decoder()),
8dbbc7f0 809 &show_hide_mapper_, SLOT(map()));
dd048a7e 810
204bae45
JH
811 QFormLayout *const decoder_form = new QFormLayout;
812 group->add_layout(decoder_form);
7491a29f 813
8bd26d8b 814 // Add the mandatory channels
f3290553 815 for (l = decoder->channels; l; l = l->next) {
8bd26d8b
UH
816 const struct srd_channel *const pdch =
817 (struct srd_channel *)l->data;
6ac6242b 818 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 819 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 820 this, SLOT(on_channel_selected(int)));
204bae45 821 decoder_form->addRow(tr("<b>%1</b> (%2) *")
744aa24f
UH
822 .arg(QString::fromUtf8(pdch->name),
823 QString::fromUtf8(pdch->desc)), combo);
7491a29f 824
6ac6242b 825 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 826 channel_selectors_.push_back(s);
7491a29f
JH
827 }
828
8bd26d8b 829 // Add the optional channels
f3290553 830 for (l = decoder->opt_channels; l; l = l->next) {
8bd26d8b
UH
831 const struct srd_channel *const pdch =
832 (struct srd_channel *)l->data;
6ac6242b 833 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 834 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 835 this, SLOT(on_channel_selected(int)));
204bae45 836 decoder_form->addRow(tr("<b>%1</b> (%2)")
744aa24f
UH
837 .arg(QString::fromUtf8(pdch->name),
838 QString::fromUtf8(pdch->desc)), combo);
7491a29f 839
6ac6242b 840 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 841 channel_selectors_.push_back(s);
7491a29f
JH
842 }
843
bb7dd726 844 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 845 base_->decoder_stack();
bb7dd726 846
7491a29f 847 // Add the options
3cc9ad7b 848 shared_ptr<binding::Decoder> binding(
bb7dd726 849 new binding::Decoder(decoder_stack, dec));
204bae45 850 binding->add_properties_to_form(decoder_form, true);
7491a29f 851
8dbbc7f0 852 bindings_.push_back(binding);
204bae45
JH
853
854 form->addRow(group);
8dbbc7f0 855 decoder_forms_.push_back(group);
7491a29f
JH
856}
857
6ac6242b 858QComboBox* DecodeTrace::create_channel_selector(
7491a29f 859 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
8bd26d8b 860 const srd_channel *const pdch)
4e5a4405 861{
7491a29f
JH
862 assert(dec);
863
47e9e7bb 864 const auto sigs(session_.signalbases());
78b0af3e 865
47e9e7bb 866 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
78b0af3e 867 std::sort(sig_list.begin(), sig_list.end(),
47e9e7bb
SA
868 [](const shared_ptr<data::SignalBase> &a,
869 const shared_ptr<data::SignalBase> &b) {
870 return strnatcasecmp(a->name().toStdString(),
871 b->name().toStdString()) < 0; });
4e5a4405 872
6ac6242b 873 const auto channel_iter = dec->channels().find(pdch);
4e5a4405
JH
874
875 QComboBox *selector = new QComboBox(parent);
876
4c60462b 877 selector->addItem("-", qVariantFromValue((void*)nullptr));
4e5a4405 878
6ac6242b 879 if (channel_iter == dec->channels().end())
4e5a4405
JH
880 selector->setCurrentIndex(0);
881
47e9e7bb
SA
882 for (const shared_ptr<data::SignalBase> &b : sig_list) {
883 assert(b);
884 if (b->type() == sigrok::ChannelType::LOGIC && b->enabled()) {
885 selector->addItem(b->name(),
886 qVariantFromValue((void*)b.get()));
5da5d081
TS
887
888 if (channel_iter != dec->channels().end() &&
47e9e7bb 889 (*channel_iter).second == b)
78b0af3e
JH
890 selector->setCurrentIndex(
891 selector->count() - 1);
4e5a4405
JH
892 }
893 }
894
895 return selector;
896}
897
6ac6242b 898void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
4e5a4405 899{
7491a29f 900 assert(dec);
4e5a4405 901
04394ded 902 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
c3a740dd 903
47e9e7bb
SA
904 const unordered_set< shared_ptr<data::SignalBase> >
905 sigs(session_.signalbases());
4e5a4405 906
2ad82c2e 907 for (const ChannelSelector &s : channel_selectors_) {
f3290553 908 if (s.decoder_ != dec)
7491a29f
JH
909 break;
910
04394ded
SA
911 const data::SignalBase *const selection =
912 (data::SignalBase*)s.combo_->itemData(
8dbbc7f0 913 s.combo_->currentIndex()).value<void*>();
4e5a4405 914
47e9e7bb
SA
915 for (shared_ptr<data::SignalBase> sig : sigs)
916 if (sig.get() == selection) {
917 channel_map[s.pdch_] = sig;
4e5a4405
JH
918 break;
919 }
920 }
921
6ac6242b 922 dec->set_channels(channel_map);
7491a29f
JH
923}
924
6ac6242b 925void DecodeTrace::commit_channels()
7491a29f 926{
bb7dd726 927 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 928 base_->decoder_stack();
bb7dd726
SA
929
930 assert(decoder_stack);
931 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
6ac6242b 932 commit_decoder_channels(dec);
7491a29f 933
bb7dd726 934 decoder_stack->begin_decode();
4e5a4405
JH
935}
936
b9329558 937void DecodeTrace::on_new_decode_data()
9cef9567 938{
8dbbc7f0 939 if (owner_)
6e2c3c85 940 owner_->row_item_appearance_changed(false, true);
9cef9567
JH
941}
942
b9329558 943void DecodeTrace::delete_pressed()
5ed1adf5
JH
944{
945 on_delete();
946}
947
b9329558 948void DecodeTrace::on_delete()
c51482b3 949{
bb7dd726 950 session_.remove_decode_signal(base_);
c51482b3
JH
951}
952
6ac6242b 953void DecodeTrace::on_channel_selected(int)
4e5a4405 954{
6ac6242b 955 commit_channels();
4e5a4405
JH
956}
957
7491a29f
JH
958void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
959{
bb7dd726 960 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 961 base_->decoder_stack();
bb7dd726 962
7491a29f 963 assert(decoder);
bb7dd726 964 assert(decoder_stack);
067bb624 965 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
bb7dd726 966 decoder_stack->begin_decode();
37fd11b1
JH
967
968 create_popup_form();
7491a29f
JH
969}
970
613d097c
JH
971void DecodeTrace::on_delete_decoder(int index)
972{
bb7dd726 973 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 974 base_->decoder_stack();
bb7dd726
SA
975
976 decoder_stack->remove(index);
613d097c
JH
977
978 // Update the popup
979 create_popup_form();
980
bb7dd726 981 decoder_stack->begin_decode();
613d097c
JH
982}
983
dd048a7e
JH
984void DecodeTrace::on_show_hide_decoder(int index)
985{
986 using pv::data::decode::Decoder;
987
bb7dd726 988 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
455bc29a 989 base_->decoder_stack();
bb7dd726
SA
990
991 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
dd048a7e
JH
992
993 // Find the decoder in the stack
f46e495e 994 auto iter = stack.cbegin();
f3290553 995 for (int i = 0; i < index; i++, iter++)
dd048a7e
JH
996 assert(iter != stack.end());
997
998 shared_ptr<Decoder> dec = *iter;
999 assert(dec);
1000
1001 const bool show = !dec->shown();
1002 dec->show(show);
1003
8dbbc7f0
JH
1004 assert(index < (int)decoder_forms_.size());
1005 decoder_forms_[index]->set_decoder_visible(show);
dd048a7e 1006
8dbbc7f0 1007 if (owner_)
6e2c3c85 1008 owner_->row_item_appearance_changed(false, true);
dd048a7e
JH
1009}
1010
f4e57597
SA
1011} // namespace TraceView
1012} // namespace views
55d3603d 1013} // namespace pv