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