2 * This file is part of the PulseView project.
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
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.
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.
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
22 #include <libsigrokdecode/libsigrokdecode.h>
31 #include <boost/functional/hash.hpp>
32 #include <boost/thread/locks.hpp>
33 #include <boost/thread/shared_mutex.hpp>
36 #include <QApplication>
38 #include <QFormLayout>
41 #include <QPushButton>
44 #include "decodetrace.hpp"
46 #include <pv/session.hpp>
47 #include <pv/strnatcmp.hpp>
48 #include <pv/data/decoderstack.hpp>
49 #include <pv/data/decode/decoder.hpp>
50 #include <pv/data/logic.hpp>
51 #include <pv/data/logicsegment.hpp>
52 #include <pv/data/decode/annotation.hpp>
53 #include <pv/view/logicsignal.hpp>
54 #include <pv/view/view.hpp>
55 #include <pv/view/viewport.hpp>
56 #include <pv/widgets/decodergroupbox.hpp>
57 #include <pv/widgets/decodermenu.hpp>
59 using boost::shared_lock;
60 using boost::shared_mutex;
61 using std::dynamic_pointer_cast;
63 using std::lock_guard;
70 using std::shared_ptr;
72 using std::unordered_set;
78 const QColor DecodeTrace::DecodeColours[4] = {
79 QColor(0xEF, 0x29, 0x29), // Red
80 QColor(0xFC, 0xE9, 0x4F), // Yellow
81 QColor(0x8A, 0xE2, 0x34), // Green
82 QColor(0x72, 0x9F, 0xCF) // Blue
85 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
86 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
88 const int DecodeTrace::ArrowSize = 4;
89 const double DecodeTrace::EndCapWidth = 5;
90 const int DecodeTrace::RowTitleMargin = 10;
91 const int DecodeTrace::DrawPadding = 100;
93 const QColor DecodeTrace::Colours[16] = {
94 QColor(0xEF, 0x29, 0x29),
95 QColor(0xF6, 0x6A, 0x32),
96 QColor(0xFC, 0xAE, 0x3E),
97 QColor(0xFB, 0xCA, 0x47),
98 QColor(0xFC, 0xE9, 0x4F),
99 QColor(0xCD, 0xF0, 0x40),
100 QColor(0x8A, 0xE2, 0x34),
101 QColor(0x4E, 0xDC, 0x44),
102 QColor(0x55, 0xD7, 0x95),
103 QColor(0x64, 0xD1, 0xD2),
104 QColor(0x72, 0x9F, 0xCF),
105 QColor(0xD4, 0x76, 0xC4),
106 QColor(0x9D, 0x79, 0xB9),
107 QColor(0xAD, 0x7F, 0xA8),
108 QColor(0xC2, 0x62, 0x9B),
109 QColor(0xD7, 0x47, 0x6F)
112 const QColor DecodeTrace::OutlineColours[16] = {
113 QColor(0x77, 0x14, 0x14),
114 QColor(0x7B, 0x35, 0x19),
115 QColor(0x7E, 0x57, 0x1F),
116 QColor(0x7D, 0x65, 0x23),
117 QColor(0x7E, 0x74, 0x27),
118 QColor(0x66, 0x78, 0x20),
119 QColor(0x45, 0x71, 0x1A),
120 QColor(0x27, 0x6E, 0x22),
121 QColor(0x2A, 0x6B, 0x4A),
122 QColor(0x32, 0x68, 0x69),
123 QColor(0x39, 0x4F, 0x67),
124 QColor(0x6A, 0x3B, 0x62),
125 QColor(0x4E, 0x3C, 0x5C),
126 QColor(0x56, 0x3F, 0x54),
127 QColor(0x61, 0x31, 0x4D),
128 QColor(0x6B, 0x23, 0x37)
131 DecodeTrace::DecodeTrace(pv::Session &session,
132 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
133 Trace(QString::fromUtf8(
134 decoder_stack->stack().front()->decoder()->name)),
136 decoder_stack_(decoder_stack),
138 max_visible_rows_(0),
139 delete_mapper_(this),
140 show_hide_mapper_(this)
142 assert(decoder_stack_);
144 // Determine shortest string we want to see displayed in full
145 QFontMetrics m(QApplication::font());
146 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
148 set_colour(DecodeColours[index % countof(DecodeColours)]);
150 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
151 this, SLOT(on_new_decode_data()));
152 connect(&delete_mapper_, SIGNAL(mapped(int)),
153 this, SLOT(on_delete_decoder(int)));
154 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
155 this, SLOT(on_show_hide_decoder(int)));
158 bool DecodeTrace::enabled() const
163 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
165 return decoder_stack_;
168 pair<int, int> DecodeTrace::v_extents() const
170 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
172 // Make an empty decode trace appear symmetrical
173 const int row_count = max(1, max_visible_rows_);
175 return make_pair(-row_height, row_height * row_count);
178 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
180 Trace::paint_back(p, pp);
181 paint_axis(p, pp, get_visual_y());
184 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
186 using namespace pv::data::decode;
188 const int text_height = ViewItemPaintParams::text_height();
189 row_height_ = (text_height * 6) / 4;
190 const int annotation_height = (text_height * 5) / 4;
192 assert(decoder_stack_);
193 const QString err = decoder_stack_->error_message();
194 if (!err.isEmpty()) {
195 draw_unresolved_period(
196 p, annotation_height, pp.left(), pp.right());
197 draw_error(p, err, pp);
201 // Set default pen to allow for text width calculation
204 // Iterate through the rows
205 int y = get_visual_y();
206 pair<uint64_t, uint64_t> sample_range = get_sample_range(
207 pp.left(), pp.right());
209 assert(decoder_stack_);
210 const vector<Row> rows(decoder_stack_->get_visible_rows());
212 visible_rows_.clear();
213 for (const Row& row : rows) {
214 // Cache the row title widths
217 row_title_width = row_title_widths_.at(row);
218 } catch (std::out_of_range) {
219 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
221 row_title_widths_[row] = w;
225 // Determine the row's color
226 size_t base_colour = 0x13579BDF;
227 boost::hash_combine(base_colour, this);
228 boost::hash_combine(base_colour, row.decoder());
229 boost::hash_combine(base_colour, row.row());
232 vector<Annotation> annotations;
233 decoder_stack_->get_annotation_subset(annotations, row,
234 sample_range.first, sample_range.second);
235 if (!annotations.empty()) {
236 draw_annotations(annotations, p, annotation_height, pp, y,
237 base_colour, row_title_width);
241 visible_rows_.push_back(row);
246 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
248 // Update the maximum row count if needed
249 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
252 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
254 using namespace pv::data::decode;
258 for (size_t i = 0; i < visible_rows_.size(); i++) {
259 const int y = i * row_height_ + get_visual_y();
261 p.setPen(QPen(Qt::NoPen));
262 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
265 const QPointF points[] = {
266 QPointF(pp.left(), y - ArrowSize),
267 QPointF(pp.left() + ArrowSize, y),
268 QPointF(pp.left(), y + ArrowSize)
270 p.drawPolygon(points, countof(points));
273 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
274 pp.right() - pp.left(), row_height_);
275 const QString h(visible_rows_[i].title());
276 const int f = Qt::AlignLeft | Qt::AlignVCenter |
280 p.setPen(QApplication::palette().color(QPalette::Base));
281 for (int dx = -1; dx <= 1; dx++)
282 for (int dy = -1; dy <= 1; dy++)
283 if (dx != 0 && dy != 0)
284 p.drawText(r.translated(dx, dy), f, h);
287 p.setPen(QApplication::palette().color(QPalette::WindowText));
292 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
294 using pv::data::decode::Decoder;
298 assert(decoder_stack_);
300 // Add the standard options
301 Trace::populate_popup_form(parent, form);
303 // Add the decoder options
305 channel_selectors_.clear();
306 decoder_forms_.clear();
308 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
311 QLabel *const l = new QLabel(
312 tr("<p><i>No decoders in the stack</i></p>"));
313 l->setAlignment(Qt::AlignCenter);
316 auto iter = stack.cbegin();
317 for (int i = 0; i < (int)stack.size(); i++, iter++) {
318 shared_ptr<Decoder> dec(*iter);
319 create_decoder_form(i, dec, parent, form);
322 form->addRow(new QLabel(
323 tr("<i>* Required channels</i>"), parent));
326 // Add stacking button
327 pv::widgets::DecoderMenu *const decoder_menu =
328 new pv::widgets::DecoderMenu(parent);
329 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
330 this, SLOT(on_stack_decoder(srd_decoder*)));
332 QPushButton *const stack_button =
333 new QPushButton(tr("Stack Decoder"), parent);
334 stack_button->setMenu(decoder_menu);
336 QHBoxLayout *stack_button_box = new QHBoxLayout;
337 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
338 form->addRow(stack_button_box);
341 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
343 QMenu *const menu = Trace::create_context_menu(parent);
345 menu->addSeparator();
347 QAction *const del = new QAction(tr("Delete"), this);
348 del->setShortcuts(QKeySequence::Delete);
349 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
350 menu->addAction(del);
355 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
356 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
357 size_t base_colour, int row_title_width)
359 using namespace pv::data::decode;
361 vector<Annotation> a_block;
364 double samples_per_pixel, pixels_offset;
365 tie(pixels_offset, samples_per_pixel) =
366 get_pixels_offset_samples_per_pixel();
368 // Sort the annotations by start sample so that decoders
369 // can't confuse us by creating annotations out of order
370 stable_sort(annotations.begin(), annotations.end(),
371 [](const Annotation &a, const Annotation &b) {
372 return a.start_sample() < b.start_sample(); });
374 // Gather all annotations that form a visual "block" and draw them as such
375 for (const Annotation &a : annotations) {
377 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
378 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
379 const int a_width = a_end - a_start;
381 const int delta = a_end - p_end;
383 bool a_is_separate = false;
385 // Annotation wider than the threshold for a useful label width?
386 if (a_width >= min_useful_label_width_) {
387 for (const QString &ann_text : a.annotations()) {
388 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
389 // Annotation wide enough to fit a label? Don't put it in a block then
391 a_is_separate = true;
397 // Were the previous and this annotation more than a pixel apart?
398 if ((abs(delta) > 1) || a_is_separate) {
399 // Block was broken, draw annotations that form the current block
400 if (a_block.size() == 1) {
401 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
405 draw_annotation_block(a_block, p, h, y, base_colour);
411 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
412 // Next annotation must start a new block. delta will be > 1
413 // because we set p_end to INT_MIN but that's okay since
414 // a_block will be empty, so nothing will be drawn
417 a_block.push_back(a);
422 if (a_block.size() == 1)
423 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
426 draw_annotation_block(a_block, p, h, y, base_colour);
429 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
430 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
431 size_t base_colour, int row_title_width) const
433 double samples_per_pixel, pixels_offset;
434 tie(pixels_offset, samples_per_pixel) =
435 get_pixels_offset_samples_per_pixel();
437 const double start = a.start_sample() / samples_per_pixel -
439 const double end = a.end_sample() / samples_per_pixel -
442 const size_t colour = (base_colour + a.format()) % countof(Colours);
443 p.setPen(OutlineColours[colour]);
444 p.setBrush(Colours[colour]);
446 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
449 if (a.start_sample() == a.end_sample())
450 draw_instant(a, p, h, start, y);
452 draw_range(a, p, h, start, end, y, pp,
456 void DecodeTrace::draw_annotation_block(
457 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
458 int y, size_t base_colour) const
460 using namespace pv::data::decode;
462 if (annotations.empty())
465 double samples_per_pixel, pixels_offset;
466 tie(pixels_offset, samples_per_pixel) =
467 get_pixels_offset_samples_per_pixel();
469 const double start = annotations.front().start_sample() /
470 samples_per_pixel - pixels_offset;
471 const double end = annotations.back().end_sample() /
472 samples_per_pixel - pixels_offset;
474 const double top = y + .5 - h / 2;
475 const double bottom = y + .5 + h / 2;
477 const size_t colour = (base_colour + annotations.front().format()) %
480 // Check if all annotations are of the same type (i.e. we can use one color)
481 // or if we should use a neutral color (i.e. gray)
482 const int format = annotations.front().format();
483 const bool single_format = std::all_of(
484 annotations.begin(), annotations.end(),
485 [&](const Annotation &a) { return a.format() == format; });
487 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
488 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
491 QRectF(start, top, end - start, bottom - top), h/4, h/4);
494 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
495 int h, double x, int y) const
497 const QString text = a.annotations().empty() ?
498 QString() : a.annotations().back();
499 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
501 const QRectF rect(x - w / 2, y - h / 2, w, h);
503 p.drawRoundedRect(rect, h / 2, h / 2);
506 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
509 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
510 int h, double start, double end, int y, const ViewItemPaintParams &pp,
511 int row_title_width) const
513 const double top = y + .5 - h / 2;
514 const double bottom = y + .5 + h / 2;
515 const vector<QString> annotations = a.annotations();
517 // If the two ends are within 1 pixel, draw a vertical line
518 if (start + 1.0 > end) {
519 p.drawLine(QPointF(start, top), QPointF(start, bottom));
523 const double cap_width = min((end - start) / 4, EndCapWidth);
526 QPointF(start, y + .5f),
527 QPointF(start + cap_width, top),
528 QPointF(end - cap_width, top),
529 QPointF(end, y + .5f),
530 QPointF(end - cap_width, bottom),
531 QPointF(start + cap_width, bottom)
534 p.drawConvexPolygon(pts, countof(pts));
536 if (annotations.empty())
539 const int ann_start = start + cap_width;
540 const int ann_end = end - cap_width;
542 const int real_start = std::max(ann_start, pp.left() + row_title_width);
543 const int real_end = std::min(ann_end, pp.right());
544 const int real_width = real_end - real_start;
546 QRectF rect(real_start, y - h / 2, real_width, h);
547 if (rect.width() <= 4)
552 // Try to find an annotation that will fit
553 QString best_annotation;
556 for (const QString &a : annotations) {
557 const int w = p.boundingRect(QRectF(), 0, a).width();
558 if (w <= rect.width() && w > best_width)
559 best_annotation = a, best_width = w;
562 if (best_annotation.isEmpty())
563 best_annotation = annotations.back();
565 // If not ellide the last in the list
566 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
567 best_annotation, Qt::ElideRight, rect.width()));
570 void DecodeTrace::draw_error(QPainter &p, const QString &message,
571 const ViewItemPaintParams &pp)
573 const int y = get_visual_y();
575 p.setPen(ErrorBgColour.darker());
576 p.setBrush(ErrorBgColour);
578 const QRectF bounding_rect =
579 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
580 const QRectF text_rect = p.boundingRect(bounding_rect,
581 Qt::AlignCenter, message);
582 const float r = text_rect.height() / 4;
584 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
588 p.drawText(text_rect, message);
591 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
594 using namespace pv::data;
595 using pv::data::decode::Decoder;
597 double samples_per_pixel, pixels_offset;
599 assert(decoder_stack_);
601 shared_ptr<Logic> data;
602 shared_ptr<LogicSignal> logic_signal;
604 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
606 // We get the logic data of the first channel in the list.
607 // This works because we are currently assuming all
608 // LogicSignals have the same data/segment
609 for (const shared_ptr<Decoder> &dec : stack)
610 if (dec && !dec->channels().empty() &&
611 ((logic_signal = (*dec->channels().begin()).second)) &&
612 ((data = logic_signal->logic_data())))
615 if (!data || data->logic_segments().empty())
618 const shared_ptr<LogicSegment> segment =
619 data->logic_segments().front();
621 const int64_t sample_count = (int64_t)segment->get_sample_count();
622 if (sample_count == 0)
625 const int64_t samples_decoded = decoder_stack_->samples_decoded();
626 if (sample_count == samples_decoded)
629 const int y = get_visual_y();
631 tie(pixels_offset, samples_per_pixel) =
632 get_pixels_offset_samples_per_pixel();
634 const double start = max(samples_decoded /
635 samples_per_pixel - pixels_offset, left - 1.0);
636 const double end = min(sample_count / samples_per_pixel -
637 pixels_offset, right + 1.0);
638 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
640 p.setPen(QPen(Qt::NoPen));
641 p.setBrush(Qt::white);
642 p.drawRect(no_decode_rect);
644 p.setPen(NoDecodeColour);
645 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
646 p.drawRect(no_decode_rect);
649 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
652 assert(decoder_stack_);
654 const View *view = owner_->view();
657 const double scale = view->scale();
660 const double pixels_offset =
661 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
663 double samplerate = decoder_stack_->samplerate();
665 // Show sample rate as 1Hz when it is unknown
666 if (samplerate == 0.0)
669 return make_pair(pixels_offset, samplerate * scale);
672 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
673 int x_start, int x_end) const
675 double samples_per_pixel, pixels_offset;
676 tie(pixels_offset, samples_per_pixel) =
677 get_pixels_offset_samples_per_pixel();
679 const uint64_t start = (uint64_t)max(
680 (x_start + pixels_offset) * samples_per_pixel, 0.0);
681 const uint64_t end = (uint64_t)max(
682 (x_end + pixels_offset) * samples_per_pixel, 0.0);
684 return make_pair(start, end);
687 int DecodeTrace::get_row_at_point(const QPoint &point)
692 const int y = (point.y() - get_visual_y() + row_height_ / 2);
694 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
698 const int row = y / row_height_;
700 if (row >= (int)visible_rows_.size())
706 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
708 using namespace pv::data::decode;
713 const pair<uint64_t, uint64_t> sample_range =
714 get_sample_range(point.x(), point.x() + 1);
715 const int row = get_row_at_point(point);
719 vector<pv::data::decode::Annotation> annotations;
721 assert(decoder_stack_);
722 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
723 sample_range.first, sample_range.second);
725 return (annotations.empty()) ?
726 QString() : annotations[0].annotations().front();
729 void DecodeTrace::hover_point_changed()
733 const View *const view = owner_->view();
736 QPoint hp = view->hover_point();
737 QString ann = get_annotation_at_point(hp);
741 if (!row_height_ || ann.isEmpty()) {
742 QToolTip::hideText();
746 const int hover_row = get_row_at_point(hp);
748 QFontMetrics m(QToolTip::font());
749 const QRect text_size = m.boundingRect(QRect(), 0, ann);
751 // This is OS-specific and unfortunately we can't query it, so
752 // use an approximation to at least try to minimize the error.
753 const int padding = 8;
755 // Make sure the tool tip doesn't overlap with the mouse cursor.
756 // If it did, the tool tip would constantly hide and re-appear.
757 // We also push it up by one row so that it appears above the
758 // decode trace, not below.
759 hp.setX(hp.x() - (text_size.width() / 2) - padding);
761 hp.setY(get_visual_y() - (row_height_ / 2) +
762 (hover_row * row_height_) -
763 row_height_ - text_size.height() - padding);
765 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
768 void DecodeTrace::create_decoder_form(int index,
769 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
775 const srd_decoder *const decoder = dec->decoder();
778 const bool decoder_deletable = index > 0;
780 pv::widgets::DecoderGroupBox *const group =
781 new pv::widgets::DecoderGroupBox(
782 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
783 group->set_decoder_visible(dec->shown());
785 if (decoder_deletable) {
786 delete_mapper_.setMapping(group, index);
787 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
790 show_hide_mapper_.setMapping(group, index);
791 connect(group, SIGNAL(show_hide_decoder()),
792 &show_hide_mapper_, SLOT(map()));
794 QFormLayout *const decoder_form = new QFormLayout;
795 group->add_layout(decoder_form);
797 // Add the mandatory channels
798 for (l = decoder->channels; l; l = l->next) {
799 const struct srd_channel *const pdch =
800 (struct srd_channel *)l->data;
801 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
802 connect(combo, SIGNAL(currentIndexChanged(int)),
803 this, SLOT(on_channel_selected(int)));
804 decoder_form->addRow(tr("<b>%1</b> (%2) *")
805 .arg(QString::fromUtf8(pdch->name),
806 QString::fromUtf8(pdch->desc)), combo);
808 const ChannelSelector s = {combo, dec, pdch};
809 channel_selectors_.push_back(s);
812 // Add the optional channels
813 for (l = decoder->opt_channels; l; l = l->next) {
814 const struct srd_channel *const pdch =
815 (struct srd_channel *)l->data;
816 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
817 connect(combo, SIGNAL(currentIndexChanged(int)),
818 this, SLOT(on_channel_selected(int)));
819 decoder_form->addRow(tr("<b>%1</b> (%2)")
820 .arg(QString::fromUtf8(pdch->name),
821 QString::fromUtf8(pdch->desc)), combo);
823 const ChannelSelector s = {combo, dec, pdch};
824 channel_selectors_.push_back(s);
828 shared_ptr<binding::Decoder> binding(
829 new binding::Decoder(decoder_stack_, dec));
830 binding->add_properties_to_form(decoder_form, true);
832 bindings_.push_back(binding);
835 decoder_forms_.push_back(group);
838 QComboBox* DecodeTrace::create_channel_selector(
839 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
840 const srd_channel *const pdch)
844 const auto sigs(session_.signals());
846 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
847 std::sort(sig_list.begin(), sig_list.end(),
848 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
849 return strnatcasecmp(a->name().toStdString(),
850 b->name().toStdString()) < 0; });
852 assert(decoder_stack_);
853 const auto channel_iter = dec->channels().find(pdch);
855 QComboBox *selector = new QComboBox(parent);
857 selector->addItem("-", qVariantFromValue((void*)nullptr));
859 if (channel_iter == dec->channels().end())
860 selector->setCurrentIndex(0);
862 for (const shared_ptr<view::Signal> &s : sig_list) {
864 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
865 selector->addItem(s->name(),
866 qVariantFromValue((void*)s.get()));
868 if (channel_iter != dec->channels().end() &&
869 (*channel_iter).second == s)
870 selector->setCurrentIndex(
871 selector->count() - 1);
878 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
882 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
884 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
886 for (const ChannelSelector &s : channel_selectors_) {
887 if (s.decoder_ != dec)
890 const LogicSignal *const selection =
891 (LogicSignal*)s.combo_->itemData(
892 s.combo_->currentIndex()).value<void*>();
894 for (shared_ptr<Signal> sig : sigs)
895 if (sig.get() == selection) {
896 channel_map[s.pdch_] =
897 dynamic_pointer_cast<LogicSignal>(sig);
902 dec->set_channels(channel_map);
905 void DecodeTrace::commit_channels()
907 assert(decoder_stack_);
908 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
909 commit_decoder_channels(dec);
911 decoder_stack_->begin_decode();
914 void DecodeTrace::on_new_decode_data()
917 owner_->row_item_appearance_changed(false, true);
920 void DecodeTrace::delete_pressed()
925 void DecodeTrace::on_delete()
927 session_.remove_decode_signal(this);
930 void DecodeTrace::on_channel_selected(int)
935 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
938 assert(decoder_stack_);
939 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
940 new data::decode::Decoder(decoder)));
941 decoder_stack_->begin_decode();
946 void DecodeTrace::on_delete_decoder(int index)
948 decoder_stack_->remove(index);
953 decoder_stack_->begin_decode();
956 void DecodeTrace::on_show_hide_decoder(int index)
958 using pv::data::decode::Decoder;
960 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
962 // Find the decoder in the stack
963 auto iter = stack.cbegin();
964 for (int i = 0; i < index; i++, iter++)
965 assert(iter != stack.end());
967 shared_ptr<Decoder> dec = *iter;
970 const bool show = !dec->shown();
973 assert(index < (int)decoder_forms_.size());
974 decoder_forms_[index]->set_decoder_visible(show);
977 owner_->row_item_appearance_changed(false, true);