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