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