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