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