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