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