]> sigrok.org Git - pulseview.git/blame - pv/view/decodetrace.cpp
Refactoring: move samplerate algo from View to Session
[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
8dbbc7f0 141 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{
163 /// @todo Replace this with an implementation that knows the true
164 /// height of the trace
5b5fa4da 165 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
a5d93c27
JH
166 return make_pair(-row_height / 2, row_height * 7 / 2);
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();
5dfeb70f
JH
185 if (!err.isEmpty())
186 {
3eb29afd
JH
187 draw_unresolved_period(
188 p, annotation_height, pp.left(), pp.right());
189 draw_error(p, err, pp);
5dfeb70f
JH
190 return;
191 }
192
f9101a91 193 // Iterate through the rows
be9e7b4b 194 int y = get_visual_y();
3eb29afd
JH
195 pair<uint64_t, uint64_t> sample_range = get_sample_range(
196 pp.left(), pp.right());
5dfeb70f 197
8dbbc7f0
JH
198 assert(decoder_stack_);
199 const vector<Row> rows(decoder_stack_->get_visible_rows());
7f8517f6 200
8dbbc7f0 201 visible_rows_.clear();
bf51365c 202 for (size_t i = 0; i < rows.size(); i++)
92421299 203 {
bf51365c 204 const Row &row = rows[i];
287d607f
JH
205
206 size_t base_colour = 0x13579BDF;
207 boost::hash_combine(base_colour, this);
208 boost::hash_combine(base_colour, row.decoder());
209 boost::hash_combine(base_colour, row.row());
210 base_colour >>= 16;
211
f9101a91 212 vector<Annotation> annotations;
8dbbc7f0 213 decoder_stack_->get_annotation_subset(annotations, row,
7f8517f6 214 sample_range.first, sample_range.second);
f9101a91 215 if (!annotations.empty()) {
d9aecf1f 216 for (const Annotation &a : annotations)
2a56e448
JH
217 draw_annotation(a, p, annotation_height,
218 pp, y, base_colour);
8dbbc7f0 219 y += row_height_;
88908838 220
8dbbc7f0 221 visible_rows_.push_back(rows[i]);
f9101a91 222 }
7e674e43 223 }
5dfeb70f 224
f9101a91 225 // Draw the hatching
3eb29afd 226 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
55d3603d
JH
227}
228
5b5fa4da 229void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
88908838
JH
230{
231 using namespace pv::data::decode;
232
8dbbc7f0 233 assert(row_height_);
88908838 234
8dbbc7f0 235 for (size_t i = 0; i < visible_rows_.size(); i++)
88908838 236 {
8dbbc7f0 237 const int y = i * row_height_ + get_visual_y();
88908838
JH
238
239 p.setPen(QPen(Qt::NoPen));
240 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
241
242 if (i != 0)
243 {
244 const QPointF points[] = {
3eb29afd
JH
245 QPointF(pp.left(), y - ArrowSize),
246 QPointF(pp.left() + ArrowSize, y),
247 QPointF(pp.left(), y + ArrowSize)
88908838
JH
248 };
249 p.drawPolygon(points, countof(points));
250 }
251
3eb29afd
JH
252 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
253 pp.right() - pp.left(), row_height_);
8dbbc7f0 254 const QString h(visible_rows_[i].title());
88908838
JH
255 const int f = Qt::AlignLeft | Qt::AlignVCenter |
256 Qt::TextDontClip;
257
258 // Draw the outline
259 p.setPen(QApplication::palette().color(QPalette::Base));
260 for (int dx = -1; dx <= 1; dx++)
261 for (int dy = -1; dy <= 1; dy++)
262 if (dx != 0 && dy != 0)
263 p.drawText(r.translated(dx, dy), f, h);
264
265 // Draw the text
266 p.setPen(QApplication::palette().color(QPalette::WindowText));
267 p.drawText(r, f, h);
268 }
269}
270
b9329558 271void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
4e5a4405 272{
613d097c
JH
273 using pv::data::decode::Decoder;
274
4e5a4405
JH
275 assert(form);
276 assert(parent);
8dbbc7f0 277 assert(decoder_stack_);
4e5a4405 278
7491a29f 279 // Add the standard options
4e5a4405
JH
280 Trace::populate_popup_form(parent, form);
281
7491a29f 282 // Add the decoder options
8dbbc7f0
JH
283 bindings_.clear();
284 channel_selectors_.clear();
285 decoder_forms_.clear();
4e5a4405 286
8dbbc7f0 287 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
4e5a4405 288
5069084a
JH
289 if (stack.empty())
290 {
291 QLabel *const l = new QLabel(
292 tr("<p><i>No decoders in the stack</i></p>"));
293 l->setAlignment(Qt::AlignCenter);
294 form->addRow(l);
295 }
296 else
297 {
f46e495e 298 auto iter = stack.cbegin();
5069084a
JH
299 for (int i = 0; i < (int)stack.size(); i++, iter++) {
300 shared_ptr<Decoder> dec(*iter);
301 create_decoder_form(i, dec, parent, form);
302 }
303
304 form->addRow(new QLabel(
8bd26d8b 305 tr("<i>* Required channels</i>"), parent));
5069084a 306 }
4e5a4405 307
ce94e4fd 308 // Add stacking button
ce94e4fd
JH
309 pv::widgets::DecoderMenu *const decoder_menu =
310 new pv::widgets::DecoderMenu(parent);
7491a29f
JH
311 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
312 this, SLOT(on_stack_decoder(srd_decoder*)));
313
314 QPushButton *const stack_button =
315 new QPushButton(tr("Stack Decoder"), parent);
ce94e4fd
JH
316 stack_button->setMenu(decoder_menu);
317
318 QHBoxLayout *stack_button_box = new QHBoxLayout;
319 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
320 form->addRow(stack_button_box);
4e5a4405
JH
321}
322
b9329558 323QMenu* DecodeTrace::create_context_menu(QWidget *parent)
c51482b3
JH
324{
325 QMenu *const menu = Trace::create_context_menu(parent);
326
327 menu->addSeparator();
328
329 QAction *const del = new QAction(tr("Delete"), this);
a2d21018 330 del->setShortcuts(QKeySequence::Delete);
c51482b3
JH
331 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
332 menu->addAction(del);
333
334 return menu;
335}
336
287d607f 337void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
5b5fa4da 338 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
287d607f 339 size_t base_colour) const
06e810f2 340{
53e35b2d
JH
341 double samples_per_pixel, pixels_offset;
342 tie(pixels_offset, samples_per_pixel) =
343 get_pixels_offset_samples_per_pixel();
7f8517f6 344
06e810f2
JH
345 const double start = a.start_sample() / samples_per_pixel -
346 pixels_offset;
347 const double end = a.end_sample() / samples_per_pixel -
348 pixels_offset;
287d607f
JH
349
350 const size_t colour = (base_colour + a.format()) % countof(Colours);
351 const QColor &fill = Colours[colour];
352 const QColor &outline = OutlineColours[colour];
06e810f2 353
3eb29afd 354 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
06e810f2
JH
355 return;
356
357 if (a.start_sample() == a.end_sample())
2a56e448 358 draw_instant(a, p, fill, outline, h, start, y);
06e810f2 359 else
2a56e448 360 draw_range(a, p, fill, outline, h, start, end, y);
06e810f2
JH
361}
362
363void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
2a56e448 364 QColor fill, QColor outline, int h, double x, int y) const
06e810f2
JH
365{
366 const QString text = a.annotations().empty() ?
367 QString() : a.annotations().back();
ea86bc4d 368 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
06e810f2
JH
369 0.0) + h;
370 const QRectF rect(x - w / 2, y - h / 2, w, h);
371
372 p.setPen(outline);
373 p.setBrush(fill);
374 p.drawRoundedRect(rect, h / 2, h / 2);
375
2a56e448 376 p.setPen(Qt::black);
06e810f2
JH
377 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
378}
379
380void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
2a56e448 381 QColor fill, QColor outline, int h, double start,
06e810f2
JH
382 double end, int y) const
383{
384 const double top = y + .5 - h / 2;
385 const double bottom = y + .5 + h / 2;
386 const vector<QString> annotations = a.annotations();
387
388 p.setPen(outline);
389 p.setBrush(fill);
390
391 // If the two ends are within 1 pixel, draw a vertical line
392 if (start + 1.0 > end)
393 {
394 p.drawLine(QPointF(start, top), QPointF(start, bottom));
395 return;
396 }
397
398 const double cap_width = min((end - start) / 4, EndCapWidth);
399
400 QPointF pts[] = {
401 QPointF(start, y + .5f),
402 QPointF(start + cap_width, top),
403 QPointF(end - cap_width, top),
404 QPointF(end, y + .5f),
405 QPointF(end - cap_width, bottom),
406 QPointF(start + cap_width, bottom)
407 };
408
409 p.drawConvexPolygon(pts, countof(pts));
410
411 if (annotations.empty())
412 return;
413
414 QRectF rect(start + cap_width, y - h / 2,
415 end - start - cap_width * 2, h);
0f290e9b
JH
416 if (rect.width() <= 4)
417 return;
418
2a56e448 419 p.setPen(Qt::black);
06e810f2
JH
420
421 // Try to find an annotation that will fit
422 QString best_annotation;
423 int best_width = 0;
424
d9aecf1f 425 for (const QString &a : annotations) {
06e810f2
JH
426 const int w = p.boundingRect(QRectF(), 0, a).width();
427 if (w <= rect.width() && w > best_width)
428 best_annotation = a, best_width = w;
429 }
430
431 if (best_annotation.isEmpty())
432 best_annotation = annotations.back();
433
434 // If not ellide the last in the list
435 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
436 best_annotation, Qt::ElideRight, rect.width()));
437}
438
b9329558 439void DecodeTrace::draw_error(QPainter &p, const QString &message,
5b5fa4da 440 const ViewItemPaintParams &pp)
ad50ac1a 441{
be9e7b4b 442 const int y = get_visual_y();
ad50ac1a
JH
443
444 p.setPen(ErrorBgColour.darker());
445 p.setBrush(ErrorBgColour);
446
447 const QRectF bounding_rect =
3eb29afd 448 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
ad50ac1a
JH
449 const QRectF text_rect = p.boundingRect(bounding_rect,
450 Qt::AlignCenter, message);
451 const float r = text_rect.height() / 4;
452
453 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
454 Qt::AbsoluteSize);
455
2a56e448 456 p.setPen(Qt::black);
ad50ac1a
JH
457 p.drawText(text_rect, message);
458}
459
5dfeb70f 460void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
7f8517f6 461 int right) const
5dfeb70f
JH
462{
463 using namespace pv::data;
464 using pv::data::decode::Decoder;
465
53e35b2d
JH
466 double samples_per_pixel, pixels_offset;
467
8dbbc7f0 468 assert(decoder_stack_);
5dfeb70f
JH
469
470 shared_ptr<Logic> data;
471 shared_ptr<LogicSignal> logic_signal;
472
8dbbc7f0 473 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
5dfeb70f 474
6ac6242b 475 // We get the logic data of the first channel in the list.
5dfeb70f 476 // This works because we are currently assuming all
f3d66e52 477 // LogicSignals have the same data/segment
d9aecf1f 478 for (const shared_ptr<Decoder> &dec : stack)
8bd26d8b
UH
479 if (dec && !dec->channels().empty() &&
480 ((logic_signal = (*dec->channels().begin()).second)) &&
7aa09b00 481 ((data = logic_signal->logic_data())))
5dfeb70f
JH
482 break;
483
f3d66e52 484 if (!data || data->logic_segments().empty())
5dfeb70f
JH
485 return;
486
f3d66e52
JH
487 const shared_ptr<LogicSegment> segment =
488 data->logic_segments().front();
489 assert(segment);
490 const int64_t sample_count = (int64_t)segment->get_sample_count();
5dfeb70f
JH
491 if (sample_count == 0)
492 return;
493
8dbbc7f0 494 const int64_t samples_decoded = decoder_stack_->samples_decoded();
5dfeb70f
JH
495 if (sample_count == samples_decoded)
496 return;
497
be9e7b4b 498 const int y = get_visual_y();
7f8517f6 499
53e35b2d
JH
500 tie(pixels_offset, samples_per_pixel) =
501 get_pixels_offset_samples_per_pixel();
7f8517f6 502
5dfeb70f
JH
503 const double start = max(samples_decoded /
504 samples_per_pixel - pixels_offset, left - 1.0);
505 const double end = min(sample_count / samples_per_pixel -
506 pixels_offset, right + 1.0);
507 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
508
509 p.setPen(QPen(Qt::NoPen));
510 p.setBrush(Qt::white);
511 p.drawRect(no_decode_rect);
512
513 p.setPen(NoDecodeColour);
514 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
515 p.drawRect(no_decode_rect);
516}
517
53e35b2d 518pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
7f8517f6 519{
8dbbc7f0
JH
520 assert(owner_);
521 assert(decoder_stack_);
7f8517f6 522
8dbbc7f0 523 const View *view = owner_->view();
eae6e30a
JH
524 assert(view);
525
526 const double scale = view->scale();
7f8517f6
SA
527 assert(scale > 0);
528
53e35b2d 529 const double pixels_offset =
60d9b99a 530 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
7f8517f6 531
8dbbc7f0 532 double samplerate = decoder_stack_->samplerate();
7f8517f6
SA
533
534 // Show sample rate as 1Hz when it is unknown
535 if (samplerate == 0.0)
536 samplerate = 1.0;
537
53e35b2d 538 return make_pair(pixels_offset, samplerate * scale);
7f8517f6
SA
539}
540
db1bf6bf
JH
541pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
542 int x_start, int x_end) const
7f8517f6 543{
53e35b2d
JH
544 double samples_per_pixel, pixels_offset;
545 tie(pixels_offset, samples_per_pixel) =
546 get_pixels_offset_samples_per_pixel();
7f8517f6 547
db1bf6bf
JH
548 const uint64_t start = (uint64_t)max(
549 (x_start + pixels_offset) * samples_per_pixel, 0.0);
550 const uint64_t end = (uint64_t)max(
551 (x_end + pixels_offset) * samples_per_pixel, 0.0);
7f8517f6
SA
552
553 return make_pair(start, end);
554}
555
117cdea3 556int DecodeTrace::get_row_at_point(const QPoint &point)
e2f90c50 557{
8dbbc7f0 558 if (!row_height_)
117cdea3 559 return -1;
e2f90c50 560
99029fda
SA
561 const int y = (point.y() - get_visual_y() + row_height_ / 2);
562
563 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
564 if (y < 0)
565 return -1;
566
567 const int row = y / row_height_;
568
569 if (row >= (int)visible_rows_.size())
117cdea3 570 return -1;
e2f90c50 571
117cdea3 572 return row;
e2f90c50
SA
573}
574
117cdea3 575const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
e2f90c50
SA
576{
577 using namespace pv::data::decode;
578
117cdea3
JH
579 if (!enabled())
580 return QString();
e2f90c50 581
117cdea3
JH
582 const pair<uint64_t, uint64_t> sample_range =
583 get_sample_range(point.x(), point.x() + 1);
584 const int row = get_row_at_point(point);
585 if (row < 0)
586 return QString();
e2f90c50
SA
587
588 vector<pv::data::decode::Annotation> annotations;
589
8dbbc7f0
JH
590 assert(decoder_stack_);
591 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
e2f90c50
SA
592 sample_range.first, sample_range.second);
593
594 return (annotations.empty()) ?
595 QString() : annotations[0].annotations().front();
596}
597
117cdea3
JH
598void DecodeTrace::hover_point_changed()
599{
8dbbc7f0 600 assert(owner_);
eae6e30a 601
8dbbc7f0 602 const View *const view = owner_->view();
eae6e30a
JH
603 assert(view);
604
605 QPoint hp = view->hover_point();
117cdea3 606 QString ann = get_annotation_at_point(hp);
e2f90c50 607
eae6e30a 608 assert(view);
e2f90c50 609
8b9df0ad 610 if (!row_height_ || ann.isEmpty()) {
ebdfa094 611 QToolTip::hideText();
117cdea3
JH
612 return;
613 }
6e6881e2 614
117cdea3 615 const int hover_row = get_row_at_point(hp);
6e6881e2 616
117cdea3
JH
617 QFontMetrics m(QToolTip::font());
618 const QRect text_size = m.boundingRect(QRect(), 0, ann);
e2f90c50 619
117cdea3
JH
620 // This is OS-specific and unfortunately we can't query it, so
621 // use an approximation to at least try to minimize the error.
622 const int padding = 8;
6e6881e2 623
117cdea3
JH
624 // Make sure the tool tip doesn't overlap with the mouse cursor.
625 // If it did, the tool tip would constantly hide and re-appear.
626 // We also push it up by one row so that it appears above the
627 // decode trace, not below.
628 hp.setX(hp.x() - (text_size.width() / 2) - padding);
6e6881e2 629
8dbbc7f0
JH
630 hp.setY(get_visual_y() - (row_height_ / 2) +
631 (hover_row * row_height_) -
99029fda 632 row_height_ - text_size.height() - padding);
e2f90c50 633
eae6e30a 634 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
9555ca8b
SA
635}
636
613d097c
JH
637void DecodeTrace::create_decoder_form(int index,
638 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
639 QFormLayout *form)
7491a29f 640{
8bd26d8b 641 const GSList *l;
7491a29f
JH
642
643 assert(dec);
644 const srd_decoder *const decoder = dec->decoder();
645 assert(decoder);
646
ff59fa2c
SA
647 const bool decoder_deletable = index > 0;
648
204bae45 649 pv::widgets::DecoderGroupBox *const group =
27e8df22 650 new pv::widgets::DecoderGroupBox(
ff59fa2c 651 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
dd048a7e 652 group->set_decoder_visible(dec->shown());
613d097c 653
ff59fa2c
SA
654 if (decoder_deletable) {
655 delete_mapper_.setMapping(group, index);
656 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
657 }
613d097c 658
8dbbc7f0 659 show_hide_mapper_.setMapping(group, index);
dd048a7e 660 connect(group, SIGNAL(show_hide_decoder()),
8dbbc7f0 661 &show_hide_mapper_, SLOT(map()));
dd048a7e 662
204bae45
JH
663 QFormLayout *const decoder_form = new QFormLayout;
664 group->add_layout(decoder_form);
7491a29f 665
8bd26d8b 666 // Add the mandatory channels
f3290553 667 for (l = decoder->channels; l; l = l->next) {
8bd26d8b
UH
668 const struct srd_channel *const pdch =
669 (struct srd_channel *)l->data;
6ac6242b 670 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 671 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 672 this, SLOT(on_channel_selected(int)));
204bae45 673 decoder_form->addRow(tr("<b>%1</b> (%2) *")
8bd26d8b
UH
674 .arg(QString::fromUtf8(pdch->name))
675 .arg(QString::fromUtf8(pdch->desc)), combo);
7491a29f 676
6ac6242b 677 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 678 channel_selectors_.push_back(s);
7491a29f
JH
679 }
680
8bd26d8b 681 // Add the optional channels
f3290553 682 for (l = decoder->opt_channels; l; l = l->next) {
8bd26d8b
UH
683 const struct srd_channel *const pdch =
684 (struct srd_channel *)l->data;
6ac6242b 685 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 686 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 687 this, SLOT(on_channel_selected(int)));
204bae45 688 decoder_form->addRow(tr("<b>%1</b> (%2)")
8bd26d8b
UH
689 .arg(QString::fromUtf8(pdch->name))
690 .arg(QString::fromUtf8(pdch->desc)), combo);
7491a29f 691
6ac6242b 692 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 693 channel_selectors_.push_back(s);
7491a29f
JH
694 }
695
696 // Add the options
3cc9ad7b
JH
697 shared_ptr<binding::Decoder> binding(
698 new binding::Decoder(decoder_stack_, dec));
204bae45 699 binding->add_properties_to_form(decoder_form, true);
7491a29f 700
8dbbc7f0 701 bindings_.push_back(binding);
204bae45
JH
702
703 form->addRow(group);
8dbbc7f0 704 decoder_forms_.push_back(group);
7491a29f
JH
705}
706
6ac6242b 707QComboBox* DecodeTrace::create_channel_selector(
7491a29f 708 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
8bd26d8b 709 const srd_channel *const pdch)
4e5a4405 710{
7491a29f
JH
711 assert(dec);
712
bf914698 713 const auto sigs(session_.signals());
78b0af3e
JH
714
715 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
716 std::sort(sig_list.begin(), sig_list.end(),
717 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
718 return a->name().compare(b->name()) < 0; });
4e5a4405 719
8dbbc7f0 720 assert(decoder_stack_);
6ac6242b 721 const auto channel_iter = dec->channels().find(pdch);
4e5a4405
JH
722
723 QComboBox *selector = new QComboBox(parent);
724
4c60462b 725 selector->addItem("-", qVariantFromValue((void*)nullptr));
4e5a4405 726
6ac6242b 727 if (channel_iter == dec->channels().end())
4e5a4405
JH
728 selector->setCurrentIndex(0);
729
78b0af3e 730 for (const shared_ptr<view::Signal> &s : sig_list) {
4e5a4405 731 assert(s);
4e5a4405
JH
732 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
733 {
0a47889b 734 selector->addItem(s->name(),
4e5a4405 735 qVariantFromValue((void*)s.get()));
6ac6242b 736 if ((*channel_iter).second == s)
78b0af3e
JH
737 selector->setCurrentIndex(
738 selector->count() - 1);
4e5a4405
JH
739 }
740 }
741
742 return selector;
743}
744
6ac6242b 745void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
4e5a4405 746{
7491a29f 747 assert(dec);
4e5a4405 748
6ac6242b 749 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
c3a740dd 750
bf914698 751 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
4e5a4405 752
8dbbc7f0 753 for (const ChannelSelector &s : channel_selectors_)
4e5a4405 754 {
f3290553 755 if (s.decoder_ != dec)
7491a29f
JH
756 break;
757
4e5a4405 758 const LogicSignal *const selection =
8dbbc7f0
JH
759 (LogicSignal*)s.combo_->itemData(
760 s.combo_->currentIndex()).value<void*>();
4e5a4405 761
d9aecf1f 762 for (shared_ptr<Signal> sig : sigs)
f3290553 763 if (sig.get() == selection) {
8dbbc7f0 764 channel_map[s.pdch_] =
7491a29f 765 dynamic_pointer_cast<LogicSignal>(sig);
4e5a4405
JH
766 break;
767 }
768 }
769
6ac6242b 770 dec->set_channels(channel_map);
7491a29f
JH
771}
772
6ac6242b 773void DecodeTrace::commit_channels()
7491a29f 774{
8dbbc7f0
JH
775 assert(decoder_stack_);
776 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
6ac6242b 777 commit_decoder_channels(dec);
7491a29f 778
8dbbc7f0 779 decoder_stack_->begin_decode();
4e5a4405
JH
780}
781
b9329558 782void DecodeTrace::on_new_decode_data()
9cef9567 783{
8dbbc7f0 784 if (owner_)
6e2c3c85 785 owner_->row_item_appearance_changed(false, true);
9cef9567
JH
786}
787
b9329558 788void DecodeTrace::delete_pressed()
5ed1adf5
JH
789{
790 on_delete();
791}
792
b9329558 793void DecodeTrace::on_delete()
c51482b3 794{
8dbbc7f0 795 session_.remove_decode_signal(this);
c51482b3
JH
796}
797
6ac6242b 798void DecodeTrace::on_channel_selected(int)
4e5a4405 799{
6ac6242b 800 commit_channels();
4e5a4405
JH
801}
802
7491a29f
JH
803void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
804{
805 assert(decoder);
8dbbc7f0
JH
806 assert(decoder_stack_);
807 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
7491a29f 808 new data::decode::Decoder(decoder)));
8dbbc7f0 809 decoder_stack_->begin_decode();
37fd11b1
JH
810
811 create_popup_form();
7491a29f
JH
812}
813
613d097c
JH
814void DecodeTrace::on_delete_decoder(int index)
815{
8dbbc7f0 816 decoder_stack_->remove(index);
613d097c
JH
817
818 // Update the popup
819 create_popup_form();
820
8dbbc7f0 821 decoder_stack_->begin_decode();
613d097c
JH
822}
823
dd048a7e
JH
824void DecodeTrace::on_show_hide_decoder(int index)
825{
826 using pv::data::decode::Decoder;
827
8dbbc7f0 828 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
dd048a7e
JH
829
830 // Find the decoder in the stack
f46e495e 831 auto iter = stack.cbegin();
f3290553 832 for (int i = 0; i < index; i++, iter++)
dd048a7e
JH
833 assert(iter != stack.end());
834
835 shared_ptr<Decoder> dec = *iter;
836 assert(dec);
837
838 const bool show = !dec->shown();
839 dec->show(show);
840
8dbbc7f0
JH
841 assert(index < (int)decoder_forms_.size());
842 decoder_forms_[index]->set_decoder_visible(show);
dd048a7e 843
8dbbc7f0 844 if (owner_)
6e2c3c85 845 owner_->row_item_appearance_changed(false, true);
dd048a7e
JH
846}
847
55d3603d
JH
848} // namespace view
849} // namespace pv