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