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