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