]> sigrok.org Git - pulseview.git/blame - pv/view/decodetrace.cpp
Replace View ownership of traces with RowItemOwner
[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::paint_back(QPainter &p, int left, int right)
fe08b6e8 154{
5dfeb70f 155 Trace::paint_back(p, left, right);
fe08b6e8
JH
156 paint_axis(p, get_y(), left, right);
157}
158
b9329558 159void DecodeTrace::paint_mid(QPainter &p, int left, int right)
55d3603d 160{
f9101a91 161 using namespace pv::data::decode;
9472f447 162
d7c0ca4a 163 QFontMetrics m(QApplication::font());
ef40ad83
SA
164 _text_height = m.boundingRect(QRect(), 0, "Tg").height();
165 _row_height = (_text_height * 6) / 4;
166 const int annotation_height = (_text_height * 5) / 4;
5dfeb70f
JH
167
168 assert(_decoder_stack);
169 const QString err = _decoder_stack->error_message();
170 if (!err.isEmpty())
171 {
7f8517f6 172 draw_unresolved_period(p, annotation_height, left, right);
5b8b1e47 173 draw_error(p, err, left, right);
5dfeb70f
JH
174 return;
175 }
176
f9101a91 177 // Iterate through the rows
f9101a91 178 int y = get_y();
7f8517f6 179 pair<uint64_t, uint64_t> sample_range = get_sample_range(left, right);
5dfeb70f 180
6e89374a 181 assert(_decoder_stack);
dd048a7e 182 const vector<Row> rows(_decoder_stack->get_visible_rows());
7f8517f6 183
c294543f 184 _visible_rows.clear();
bf51365c 185 for (size_t i = 0; i < rows.size(); i++)
92421299 186 {
bf51365c 187 const Row &row = rows[i];
287d607f
JH
188
189 size_t base_colour = 0x13579BDF;
190 boost::hash_combine(base_colour, this);
191 boost::hash_combine(base_colour, row.decoder());
192 boost::hash_combine(base_colour, row.row());
193 base_colour >>= 16;
194
f9101a91
JH
195 vector<Annotation> annotations;
196 _decoder_stack->get_annotation_subset(annotations, row,
7f8517f6 197 sample_range.first, sample_range.second);
f9101a91 198 if (!annotations.empty()) {
d9aecf1f 199 for (const Annotation &a : annotations)
f9101a91 200 draw_annotation(a, p, get_text_colour(),
7f8517f6 201 annotation_height, left, right, y,
287d607f 202 base_colour);
ef40ad83 203 y += _row_height;
88908838 204
c294543f 205 _visible_rows.push_back(rows[i]);
f9101a91 206 }
7e674e43 207 }
5dfeb70f 208
f9101a91 209 // Draw the hatching
7f8517f6 210 draw_unresolved_period(p, annotation_height, left, right);
55d3603d
JH
211}
212
88908838
JH
213void DecodeTrace::paint_fore(QPainter &p, int left, int right)
214{
215 using namespace pv::data::decode;
216
217 (void)right;
218
ef40ad83 219 assert(_row_height);
88908838 220
c294543f 221 for (size_t i = 0; i < _visible_rows.size(); i++)
88908838 222 {
ef40ad83 223 const int y = i * _row_height + get_y();
88908838
JH
224
225 p.setPen(QPen(Qt::NoPen));
226 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
227
228 if (i != 0)
229 {
230 const QPointF points[] = {
231 QPointF(left, y - ArrowSize),
232 QPointF(left + ArrowSize, y),
233 QPointF(left, y + ArrowSize)
234 };
235 p.drawPolygon(points, countof(points));
236 }
237
ef40ad83
SA
238 const QRect r(left + ArrowSize * 2, y - _row_height / 2,
239 right - left, _row_height);
c294543f 240 const QString h(_visible_rows[i].title());
88908838
JH
241 const int f = Qt::AlignLeft | Qt::AlignVCenter |
242 Qt::TextDontClip;
243
244 // Draw the outline
245 p.setPen(QApplication::palette().color(QPalette::Base));
246 for (int dx = -1; dx <= 1; dx++)
247 for (int dy = -1; dy <= 1; dy++)
248 if (dx != 0 && dy != 0)
249 p.drawText(r.translated(dx, dy), f, h);
250
251 // Draw the text
252 p.setPen(QApplication::palette().color(QPalette::WindowText));
253 p.drawText(r, f, h);
254 }
255}
256
b9329558 257void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
4e5a4405 258{
613d097c
JH
259 using pv::data::decode::Decoder;
260
4e5a4405
JH
261 assert(form);
262 assert(parent);
6e89374a 263 assert(_decoder_stack);
4e5a4405 264
7491a29f 265 // Add the standard options
4e5a4405
JH
266 Trace::populate_popup_form(parent, form);
267
7491a29f
JH
268 // Add the decoder options
269 _bindings.clear();
6ac6242b 270 _channel_selectors.clear();
dd048a7e 271 _decoder_forms.clear();
4e5a4405 272
613d097c 273 const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
4e5a4405 274
5069084a
JH
275 if (stack.empty())
276 {
277 QLabel *const l = new QLabel(
278 tr("<p><i>No decoders in the stack</i></p>"));
279 l->setAlignment(Qt::AlignCenter);
280 form->addRow(l);
281 }
282 else
283 {
f46e495e 284 auto iter = stack.cbegin();
5069084a
JH
285 for (int i = 0; i < (int)stack.size(); i++, iter++) {
286 shared_ptr<Decoder> dec(*iter);
287 create_decoder_form(i, dec, parent, form);
288 }
289
290 form->addRow(new QLabel(
8bd26d8b 291 tr("<i>* Required channels</i>"), parent));
5069084a 292 }
4e5a4405 293
ce94e4fd 294 // Add stacking button
ce94e4fd
JH
295 pv::widgets::DecoderMenu *const decoder_menu =
296 new pv::widgets::DecoderMenu(parent);
7491a29f
JH
297 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
298 this, SLOT(on_stack_decoder(srd_decoder*)));
299
300 QPushButton *const stack_button =
301 new QPushButton(tr("Stack Decoder"), parent);
ce94e4fd
JH
302 stack_button->setMenu(decoder_menu);
303
304 QHBoxLayout *stack_button_box = new QHBoxLayout;
305 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
306 form->addRow(stack_button_box);
4e5a4405
JH
307}
308
b9329558 309QMenu* DecodeTrace::create_context_menu(QWidget *parent)
c51482b3
JH
310{
311 QMenu *const menu = Trace::create_context_menu(parent);
312
313 menu->addSeparator();
314
315 QAction *const del = new QAction(tr("Delete"), this);
a2d21018 316 del->setShortcuts(QKeySequence::Delete);
c51482b3
JH
317 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
318 menu->addAction(del);
319
320 return menu;
321}
322
287d607f 323void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
7f8517f6 324 QPainter &p, QColor text_color, int h, int left, int right, int y,
287d607f 325 size_t base_colour) const
06e810f2 326{
53e35b2d
JH
327 double samples_per_pixel, pixels_offset;
328 tie(pixels_offset, samples_per_pixel) =
329 get_pixels_offset_samples_per_pixel();
7f8517f6 330
06e810f2
JH
331 const double start = a.start_sample() / samples_per_pixel -
332 pixels_offset;
333 const double end = a.end_sample() / samples_per_pixel -
334 pixels_offset;
287d607f
JH
335
336 const size_t colour = (base_colour + a.format()) % countof(Colours);
337 const QColor &fill = Colours[colour];
338 const QColor &outline = OutlineColours[colour];
06e810f2
JH
339
340 if (start > right + DrawPadding || end < left - DrawPadding)
341 return;
342
343 if (a.start_sample() == a.end_sample())
344 draw_instant(a, p, fill, outline, text_color, h,
345 start, y);
346 else
347 draw_range(a, p, fill, outline, text_color, h,
348 start, end, y);
349}
350
351void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
352 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
353{
354 const QString text = a.annotations().empty() ?
355 QString() : a.annotations().back();
ea86bc4d 356 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
06e810f2
JH
357 0.0) + h;
358 const QRectF rect(x - w / 2, y - h / 2, w, h);
359
360 p.setPen(outline);
361 p.setBrush(fill);
362 p.drawRoundedRect(rect, h / 2, h / 2);
363
364 p.setPen(text_color);
365 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
366}
367
368void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
369 QColor fill, QColor outline, QColor text_color, int h, double start,
370 double end, int y) const
371{
372 const double top = y + .5 - h / 2;
373 const double bottom = y + .5 + h / 2;
374 const vector<QString> annotations = a.annotations();
375
376 p.setPen(outline);
377 p.setBrush(fill);
378
379 // If the two ends are within 1 pixel, draw a vertical line
380 if (start + 1.0 > end)
381 {
382 p.drawLine(QPointF(start, top), QPointF(start, bottom));
383 return;
384 }
385
386 const double cap_width = min((end - start) / 4, EndCapWidth);
387
388 QPointF pts[] = {
389 QPointF(start, y + .5f),
390 QPointF(start + cap_width, top),
391 QPointF(end - cap_width, top),
392 QPointF(end, y + .5f),
393 QPointF(end - cap_width, bottom),
394 QPointF(start + cap_width, bottom)
395 };
396
397 p.drawConvexPolygon(pts, countof(pts));
398
399 if (annotations.empty())
400 return;
401
402 QRectF rect(start + cap_width, y - h / 2,
403 end - start - cap_width * 2, h);
0f290e9b
JH
404 if (rect.width() <= 4)
405 return;
406
06e810f2
JH
407 p.setPen(text_color);
408
409 // Try to find an annotation that will fit
410 QString best_annotation;
411 int best_width = 0;
412
d9aecf1f 413 for (const QString &a : annotations) {
06e810f2
JH
414 const int w = p.boundingRect(QRectF(), 0, a).width();
415 if (w <= rect.width() && w > best_width)
416 best_annotation = a, best_width = w;
417 }
418
419 if (best_annotation.isEmpty())
420 best_annotation = annotations.back();
421
422 // If not ellide the last in the list
423 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
424 best_annotation, Qt::ElideRight, rect.width()));
425}
426
b9329558 427void DecodeTrace::draw_error(QPainter &p, const QString &message,
ad50ac1a
JH
428 int left, int right)
429{
430 const int y = get_y();
431
432 p.setPen(ErrorBgColour.darker());
433 p.setBrush(ErrorBgColour);
434
435 const QRectF bounding_rect =
436 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
437 const QRectF text_rect = p.boundingRect(bounding_rect,
438 Qt::AlignCenter, message);
439 const float r = text_rect.height() / 4;
440
441 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
442 Qt::AbsoluteSize);
443
444 p.setPen(get_text_colour());
445 p.drawText(text_rect, message);
446}
447
5dfeb70f 448void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
7f8517f6 449 int right) const
5dfeb70f
JH
450{
451 using namespace pv::data;
452 using pv::data::decode::Decoder;
453
53e35b2d
JH
454 double samples_per_pixel, pixels_offset;
455
5dfeb70f
JH
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 487
53e35b2d
JH
488 tie(pixels_offset, samples_per_pixel) =
489 get_pixels_offset_samples_per_pixel();
7f8517f6 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
53e35b2d 506pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
7f8517f6 507{
eae6e30a 508 assert(_owner);
7f8517f6
SA
509 assert(_decoder_stack);
510
eae6e30a
JH
511 const View *view = _owner->view();
512 assert(view);
513
514 const double scale = view->scale();
7f8517f6
SA
515 assert(scale > 0);
516
53e35b2d 517 const double pixels_offset =
eae6e30a 518 (view->offset() - _decoder_stack->get_start_time()) / scale;
7f8517f6
SA
519
520 double samplerate = _decoder_stack->samplerate();
521
522 // Show sample rate as 1Hz when it is unknown
523 if (samplerate == 0.0)
524 samplerate = 1.0;
525
53e35b2d 526 return make_pair(pixels_offset, samplerate * scale);
7f8517f6
SA
527}
528
db1bf6bf
JH
529pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
530 int x_start, int x_end) const
7f8517f6 531{
53e35b2d
JH
532 double samples_per_pixel, pixels_offset;
533 tie(pixels_offset, samples_per_pixel) =
534 get_pixels_offset_samples_per_pixel();
7f8517f6 535
db1bf6bf
JH
536 const uint64_t start = (uint64_t)max(
537 (x_start + pixels_offset) * samples_per_pixel, 0.0);
538 const uint64_t end = (uint64_t)max(
539 (x_end + pixels_offset) * samples_per_pixel, 0.0);
7f8517f6
SA
540
541 return make_pair(start, end);
542}
543
117cdea3 544int DecodeTrace::get_row_at_point(const QPoint &point)
e2f90c50 545{
117cdea3
JH
546 if (!_row_height)
547 return -1;
e2f90c50 548
117cdea3
JH
549 const int row = (point.y() - get_y() + _row_height / 2) / _row_height;
550 if (row < 0 || row >= (int)_visible_rows.size())
551 return -1;
e2f90c50 552
117cdea3 553 return row;
e2f90c50
SA
554}
555
117cdea3 556const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
e2f90c50
SA
557{
558 using namespace pv::data::decode;
559
117cdea3
JH
560 if (!enabled())
561 return QString();
e2f90c50 562
117cdea3
JH
563 const pair<uint64_t, uint64_t> sample_range =
564 get_sample_range(point.x(), point.x() + 1);
565 const int row = get_row_at_point(point);
566 if (row < 0)
567 return QString();
e2f90c50
SA
568
569 vector<pv::data::decode::Annotation> annotations;
570
c294543f 571 assert(_decoder_stack);
117cdea3 572 _decoder_stack->get_annotation_subset(annotations, _visible_rows[row],
e2f90c50
SA
573 sample_range.first, sample_range.second);
574
575 return (annotations.empty()) ?
576 QString() : annotations[0].annotations().front();
577}
578
117cdea3 579void DecodeTrace::hide_hover_annotation()
e2f90c50 580{
117cdea3
JH
581 QToolTip::hideText();
582}
583
584void DecodeTrace::hover_point_changed()
585{
eae6e30a
JH
586 assert(_owner);
587
588 const View *const view = _owner->view();
589 assert(view);
590
591 QPoint hp = view->hover_point();
117cdea3 592 QString ann = get_annotation_at_point(hp);
e2f90c50 593
eae6e30a 594 assert(view);
e2f90c50 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
eae6e30a 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 {
0a47889b 713 selector->addItem(s->name(),
4e5a4405 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 760{
eae6e30a
JH
761 if (_owner)
762 _owner->update_viewport();
9cef9567
JH
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
eae6e30a 821 _owner->update_viewport();
dd048a7e
JH
822}
823
55d3603d
JH
824} // namespace view
825} // namespace pv