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