]> sigrok.org Git - pulseview.git/blame - pv/view/decodetrace.cpp
Renamed pv::data::Analog::get_snapshots and Logic::get_snapshots
[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>
48#include <pv/data/logicsnapshot.hpp>
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),
132 text_height_(0),
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
9ab27c43 163 const int row_height = (RowItemPaintParams::text_height() * 6) / 4;
a5d93c27
JH
164 return make_pair(-row_height / 2, row_height * 7 / 2);
165}
166
3eb29afd 167void DecodeTrace::paint_back(QPainter &p, const RowItemPaintParams &pp)
fe08b6e8 168{
3eb29afd 169 Trace::paint_back(p, pp);
97904bf7 170 paint_axis(p, pp, get_visual_y());
fe08b6e8
JH
171}
172
3eb29afd 173void DecodeTrace::paint_mid(QPainter &p, const RowItemPaintParams &pp)
55d3603d 174{
f9101a91 175 using namespace pv::data::decode;
9472f447 176
9ab27c43 177 text_height_ = RowItemPaintParams::text_height();
8dbbc7f0
JH
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)
f9101a91 215 draw_annotation(a, p, get_text_colour(),
3eb29afd 216 annotation_height, 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
3eb29afd 227void DecodeTrace::paint_fore(QPainter &p, const RowItemPaintParams &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,
3eb29afd 336 QPainter &p, QColor text_color, int h, const RowItemPaintParams &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())
356 draw_instant(a, p, fill, outline, text_color, h,
357 start, y);
358 else
359 draw_range(a, p, fill, outline, text_color, h,
360 start, end, y);
361}
362
363void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
364 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
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
376 p.setPen(text_color);
377 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
378}
379
380void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
381 QColor fill, QColor outline, QColor text_color, int h, double start,
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
06e810f2
JH
419 p.setPen(text_color);
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,
3eb29afd 440 const RowItemPaintParams &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
456 p.setPen(get_text_colour());
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
JH
476 // This works because we are currently assuming all
477 // LogicSignals have the same data/snapshot
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
1df18f6b 484 if (!data || data->logic_snapshots().empty())
5dfeb70f
JH
485 return;
486
487 const shared_ptr<LogicSnapshot> snapshot =
1df18f6b 488 data->logic_snapshots().front();
5dfeb70f
JH
489 assert(snapshot);
490 const int64_t sample_count = (int64_t)snapshot->get_sample_count();
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 =
8dbbc7f0 530 (view->offset() - decoder_stack_->get_start_time()) / scale;
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
8dbbc7f0
JH
561 const int row = (point.y() - get_visual_y() + row_height_ / 2) /
562 row_height_;
563 if (row < 0 || row >= (int)visible_rows_.size())
117cdea3 564 return -1;
e2f90c50 565
117cdea3 566 return row;
e2f90c50
SA
567}
568
117cdea3 569const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
e2f90c50
SA
570{
571 using namespace pv::data::decode;
572
117cdea3
JH
573 if (!enabled())
574 return QString();
e2f90c50 575
117cdea3
JH
576 const pair<uint64_t, uint64_t> sample_range =
577 get_sample_range(point.x(), point.x() + 1);
578 const int row = get_row_at_point(point);
579 if (row < 0)
580 return QString();
e2f90c50
SA
581
582 vector<pv::data::decode::Annotation> annotations;
583
8dbbc7f0
JH
584 assert(decoder_stack_);
585 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
e2f90c50
SA
586 sample_range.first, sample_range.second);
587
588 return (annotations.empty()) ?
589 QString() : annotations[0].annotations().front();
590}
591
117cdea3 592void DecodeTrace::hide_hover_annotation()
e2f90c50 593{
117cdea3
JH
594 QToolTip::hideText();
595}
596
597void DecodeTrace::hover_point_changed()
598{
8dbbc7f0 599 assert(owner_);
eae6e30a 600
8dbbc7f0 601 const View *const view = owner_->view();
eae6e30a
JH
602 assert(view);
603
604 QPoint hp = view->hover_point();
117cdea3 605 QString ann = get_annotation_at_point(hp);
e2f90c50 606
eae6e30a 607 assert(view);
e2f90c50 608
8b9df0ad 609 if (!row_height_ || ann.isEmpty()) {
117cdea3
JH
610 hide_hover_annotation();
611 return;
612 }
6e6881e2 613
117cdea3 614 const int hover_row = get_row_at_point(hp);
6e6881e2 615
117cdea3
JH
616 QFontMetrics m(QToolTip::font());
617 const QRect text_size = m.boundingRect(QRect(), 0, ann);
e2f90c50 618
117cdea3
JH
619 // This is OS-specific and unfortunately we can't query it, so
620 // use an approximation to at least try to minimize the error.
621 const int padding = 8;
6e6881e2 622
117cdea3
JH
623 // Make sure the tool tip doesn't overlap with the mouse cursor.
624 // If it did, the tool tip would constantly hide and re-appear.
625 // We also push it up by one row so that it appears above the
626 // decode trace, not below.
627 hp.setX(hp.x() - (text_size.width() / 2) - padding);
6e6881e2 628
8dbbc7f0
JH
629 hp.setY(get_visual_y() - (row_height_ / 2) +
630 (hover_row * row_height_) -
631 row_height_ - text_size.height());
e2f90c50 632
eae6e30a 633 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
9555ca8b
SA
634}
635
613d097c
JH
636void DecodeTrace::create_decoder_form(int index,
637 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
638 QFormLayout *form)
7491a29f 639{
8bd26d8b 640 const GSList *l;
7491a29f
JH
641
642 assert(dec);
643 const srd_decoder *const decoder = dec->decoder();
644 assert(decoder);
645
204bae45 646 pv::widgets::DecoderGroupBox *const group =
27e8df22
JH
647 new pv::widgets::DecoderGroupBox(
648 QString::fromUtf8(decoder->name));
dd048a7e 649 group->set_decoder_visible(dec->shown());
613d097c 650
8dbbc7f0
JH
651 delete_mapper_.setMapping(group, index);
652 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
613d097c 653
8dbbc7f0 654 show_hide_mapper_.setMapping(group, index);
dd048a7e 655 connect(group, SIGNAL(show_hide_decoder()),
8dbbc7f0 656 &show_hide_mapper_, SLOT(map()));
dd048a7e 657
204bae45
JH
658 QFormLayout *const decoder_form = new QFormLayout;
659 group->add_layout(decoder_form);
7491a29f 660
8bd26d8b
UH
661 // Add the mandatory channels
662 for(l = decoder->channels; l; l = l->next) {
663 const struct srd_channel *const pdch =
664 (struct srd_channel *)l->data;
6ac6242b 665 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 666 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 667 this, SLOT(on_channel_selected(int)));
204bae45 668 decoder_form->addRow(tr("<b>%1</b> (%2) *")
8bd26d8b
UH
669 .arg(QString::fromUtf8(pdch->name))
670 .arg(QString::fromUtf8(pdch->desc)), combo);
7491a29f 671
6ac6242b 672 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 673 channel_selectors_.push_back(s);
7491a29f
JH
674 }
675
8bd26d8b
UH
676 // Add the optional channels
677 for(l = decoder->opt_channels; l; l = l->next) {
678 const struct srd_channel *const pdch =
679 (struct srd_channel *)l->data;
6ac6242b 680 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
7491a29f 681 connect(combo, SIGNAL(currentIndexChanged(int)),
6ac6242b 682 this, SLOT(on_channel_selected(int)));
204bae45 683 decoder_form->addRow(tr("<b>%1</b> (%2)")
8bd26d8b
UH
684 .arg(QString::fromUtf8(pdch->name))
685 .arg(QString::fromUtf8(pdch->desc)), combo);
7491a29f 686
6ac6242b 687 const ChannelSelector s = {combo, dec, pdch};
8dbbc7f0 688 channel_selectors_.push_back(s);
7491a29f
JH
689 }
690
691 // Add the options
692 shared_ptr<prop::binding::DecoderOptions> binding(
8dbbc7f0 693 new prop::binding::DecoderOptions(decoder_stack_, dec));
204bae45 694 binding->add_properties_to_form(decoder_form, true);
7491a29f 695
8dbbc7f0 696 bindings_.push_back(binding);
204bae45
JH
697
698 form->addRow(group);
8dbbc7f0 699 decoder_forms_.push_back(group);
7491a29f
JH
700}
701
6ac6242b 702QComboBox* DecodeTrace::create_channel_selector(
7491a29f 703 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
8bd26d8b 704 const srd_channel *const pdch)
4e5a4405 705{
7491a29f
JH
706 assert(dec);
707
8dbbc7f0
JH
708 shared_lock<shared_mutex> lock(session_.signals_mutex());
709 const vector< shared_ptr<Signal> > &sigs(session_.signals());
4e5a4405 710
8dbbc7f0 711 assert(decoder_stack_);
6ac6242b 712 const auto channel_iter = dec->channels().find(pdch);
4e5a4405
JH
713
714 QComboBox *selector = new QComboBox(parent);
715
716 selector->addItem("-", qVariantFromValue((void*)NULL));
717
6ac6242b 718 if (channel_iter == dec->channels().end())
4e5a4405
JH
719 selector->setCurrentIndex(0);
720
721 for(size_t i = 0; i < sigs.size(); i++) {
722 const shared_ptr<view::Signal> s(sigs[i]);
723 assert(s);
724
725 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
726 {
0a47889b 727 selector->addItem(s->name(),
4e5a4405 728 qVariantFromValue((void*)s.get()));
6ac6242b 729 if ((*channel_iter).second == s)
4e5a4405
JH
730 selector->setCurrentIndex(i + 1);
731 }
732 }
733
734 return selector;
735}
736
6ac6242b 737void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
4e5a4405 738{
7491a29f 739 assert(dec);
4e5a4405 740
6ac6242b 741 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
c3a740dd 742
8dbbc7f0
JH
743 shared_lock<shared_mutex> lock(session_.signals_mutex());
744 const vector< shared_ptr<Signal> > &sigs(session_.signals());
4e5a4405 745
8dbbc7f0 746 for (const ChannelSelector &s : channel_selectors_)
4e5a4405 747 {
8dbbc7f0 748 if(s.decoder_ != dec)
7491a29f
JH
749 break;
750
4e5a4405 751 const LogicSignal *const selection =
8dbbc7f0
JH
752 (LogicSignal*)s.combo_->itemData(
753 s.combo_->currentIndex()).value<void*>();
4e5a4405 754
d9aecf1f 755 for (shared_ptr<Signal> sig : sigs)
7491a29f 756 if(sig.get() == selection) {
8dbbc7f0 757 channel_map[s.pdch_] =
7491a29f 758 dynamic_pointer_cast<LogicSignal>(sig);
4e5a4405
JH
759 break;
760 }
761 }
762
6ac6242b 763 dec->set_channels(channel_map);
7491a29f
JH
764}
765
6ac6242b 766void DecodeTrace::commit_channels()
7491a29f 767{
8dbbc7f0
JH
768 assert(decoder_stack_);
769 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
6ac6242b 770 commit_decoder_channels(dec);
7491a29f 771
8dbbc7f0 772 decoder_stack_->begin_decode();
4e5a4405
JH
773}
774
b9329558 775void DecodeTrace::on_new_decode_data()
9cef9567 776{
8dbbc7f0
JH
777 if (owner_)
778 owner_->appearance_changed(false, true);
9cef9567
JH
779}
780
b9329558 781void DecodeTrace::delete_pressed()
5ed1adf5
JH
782{
783 on_delete();
784}
785
b9329558 786void DecodeTrace::on_delete()
c51482b3 787{
8dbbc7f0 788 session_.remove_decode_signal(this);
c51482b3
JH
789}
790
6ac6242b 791void DecodeTrace::on_channel_selected(int)
4e5a4405 792{
6ac6242b 793 commit_channels();
4e5a4405
JH
794}
795
7491a29f
JH
796void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
797{
798 assert(decoder);
8dbbc7f0
JH
799 assert(decoder_stack_);
800 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
7491a29f 801 new data::decode::Decoder(decoder)));
8dbbc7f0 802 decoder_stack_->begin_decode();
37fd11b1
JH
803
804 create_popup_form();
7491a29f
JH
805}
806
613d097c
JH
807void DecodeTrace::on_delete_decoder(int index)
808{
8dbbc7f0 809 decoder_stack_->remove(index);
613d097c
JH
810
811 // Update the popup
812 create_popup_form();
813
8dbbc7f0 814 decoder_stack_->begin_decode();
613d097c
JH
815}
816
dd048a7e
JH
817void DecodeTrace::on_show_hide_decoder(int index)
818{
819 using pv::data::decode::Decoder;
820
8dbbc7f0 821 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
dd048a7e
JH
822
823 // Find the decoder in the stack
f46e495e 824 auto iter = stack.cbegin();
dd048a7e
JH
825 for(int i = 0; i < index; i++, iter++)
826 assert(iter != stack.end());
827
828 shared_ptr<Decoder> dec = *iter;
829 assert(dec);
830
831 const bool show = !dec->shown();
832 dec->show(show);
833
8dbbc7f0
JH
834 assert(index < (int)decoder_forms_.size());
835 decoder_forms_[index]->set_decoder_visible(show);
dd048a7e 836
8dbbc7f0
JH
837 if (owner_)
838 owner_->appearance_changed(false, true);
dd048a7e
JH
839}
840
55d3603d
JH
841} // namespace view
842} // namespace pv