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