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/view.hpp>
54 #include <pv/view/viewport.hpp>
55 #include <pv/widgets/decodergroupbox.hpp>
56 #include <pv/widgets/decodermenu.hpp>
58 using boost::shared_lock;
59 using boost::shared_mutex;
60 using std::dynamic_pointer_cast;
62 using std::lock_guard;
69 using std::shared_ptr;
71 using std::unordered_set;
77 const 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
84 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
85 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
87 const int DecodeTrace::ArrowSize = 4;
88 const double DecodeTrace::EndCapWidth = 5;
89 const int DecodeTrace::RowTitleMargin = 10;
90 const int DecodeTrace::DrawPadding = 100;
92 const QColor DecodeTrace::Colours[16] = {
93 QColor(0xEF, 0x29, 0x29),
94 QColor(0xF6, 0x6A, 0x32),
95 QColor(0xFC, 0xAE, 0x3E),
96 QColor(0xFB, 0xCA, 0x47),
97 QColor(0xFC, 0xE9, 0x4F),
98 QColor(0xCD, 0xF0, 0x40),
99 QColor(0x8A, 0xE2, 0x34),
100 QColor(0x4E, 0xDC, 0x44),
101 QColor(0x55, 0xD7, 0x95),
102 QColor(0x64, 0xD1, 0xD2),
103 QColor(0x72, 0x9F, 0xCF),
104 QColor(0xD4, 0x76, 0xC4),
105 QColor(0x9D, 0x79, 0xB9),
106 QColor(0xAD, 0x7F, 0xA8),
107 QColor(0xC2, 0x62, 0x9B),
108 QColor(0xD7, 0x47, 0x6F)
111 const QColor DecodeTrace::OutlineColours[16] = {
112 QColor(0x77, 0x14, 0x14),
113 QColor(0x7B, 0x35, 0x19),
114 QColor(0x7E, 0x57, 0x1F),
115 QColor(0x7D, 0x65, 0x23),
116 QColor(0x7E, 0x74, 0x27),
117 QColor(0x66, 0x78, 0x20),
118 QColor(0x45, 0x71, 0x1A),
119 QColor(0x27, 0x6E, 0x22),
120 QColor(0x2A, 0x6B, 0x4A),
121 QColor(0x32, 0x68, 0x69),
122 QColor(0x39, 0x4F, 0x67),
123 QColor(0x6A, 0x3B, 0x62),
124 QColor(0x4E, 0x3C, 0x5C),
125 QColor(0x56, 0x3F, 0x54),
126 QColor(0x61, 0x31, 0x4D),
127 QColor(0x6B, 0x23, 0x37)
130 DecodeTrace::DecodeTrace(pv::Session &session,
131 shared_ptr<data::SignalBase> signalbase,
132 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
135 signalbase_(signalbase),
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 signalbase_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
149 signalbase_->set_colour(DecodeColours[index % countof(DecodeColours)]);
151 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
152 this, SLOT(on_new_decode_data()));
153 connect(&delete_mapper_, SIGNAL(mapped(int)),
154 this, SLOT(on_delete_decoder(int)));
155 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
156 this, SLOT(on_show_hide_decoder(int)));
159 bool DecodeTrace::enabled() const
164 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
166 return decoder_stack_;
169 pair<int, int> DecodeTrace::v_extents() const
171 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
173 // Make an empty decode trace appear symmetrical
174 const int row_count = max(1, max_visible_rows_);
176 return make_pair(-row_height, row_height * row_count);
179 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
181 Trace::paint_back(p, pp);
182 paint_axis(p, pp, get_visual_y());
185 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
187 using namespace pv::data::decode;
189 const int text_height = ViewItemPaintParams::text_height();
190 row_height_ = (text_height * 6) / 4;
191 const int annotation_height = (text_height * 5) / 4;
193 assert(decoder_stack_);
194 const QString err = decoder_stack_->error_message();
195 if (!err.isEmpty()) {
196 draw_unresolved_period(
197 p, annotation_height, pp.left(), pp.right());
198 draw_error(p, err, pp);
202 // Set default pen to allow for text width calculation
205 // Iterate through the rows
206 int y = get_visual_y();
207 pair<uint64_t, uint64_t> sample_range = get_sample_range(
208 pp.left(), pp.right());
210 assert(decoder_stack_);
211 const vector<Row> rows(decoder_stack_->get_visible_rows());
213 visible_rows_.clear();
214 for (const Row& row : rows) {
215 // Cache the row title widths
218 row_title_width = row_title_widths_.at(row);
219 } catch (std::out_of_range) {
220 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
222 row_title_widths_[row] = w;
226 // Determine the row's color
227 size_t base_colour = 0x13579BDF;
228 boost::hash_combine(base_colour, this);
229 boost::hash_combine(base_colour, row.decoder());
230 boost::hash_combine(base_colour, row.row());
233 vector<Annotation> annotations;
234 decoder_stack_->get_annotation_subset(annotations, row,
235 sample_range.first, sample_range.second);
236 if (!annotations.empty()) {
237 draw_annotations(annotations, p, annotation_height, pp, y,
238 base_colour, row_title_width);
242 visible_rows_.push_back(row);
247 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
249 if ((int)visible_rows_.size() > max_visible_rows_)
250 owner_->extents_changed(false, true);
252 // Update the maximum row count if needed
253 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
256 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
258 using namespace pv::data::decode;
262 for (size_t i = 0; i < visible_rows_.size(); i++) {
263 const int y = i * row_height_ + get_visual_y();
265 p.setPen(QPen(Qt::NoPen));
266 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
269 const QPointF points[] = {
270 QPointF(pp.left(), y - ArrowSize),
271 QPointF(pp.left() + ArrowSize, y),
272 QPointF(pp.left(), y + ArrowSize)
274 p.drawPolygon(points, countof(points));
277 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
278 pp.right() - pp.left(), row_height_);
279 const QString h(visible_rows_[i].title());
280 const int f = Qt::AlignLeft | Qt::AlignVCenter |
284 p.setPen(QApplication::palette().color(QPalette::Base));
285 for (int dx = -1; dx <= 1; dx++)
286 for (int dy = -1; dy <= 1; dy++)
287 if (dx != 0 && dy != 0)
288 p.drawText(r.translated(dx, dy), f, h);
291 p.setPen(QApplication::palette().color(QPalette::WindowText));
296 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
298 using pv::data::decode::Decoder;
302 assert(decoder_stack_);
304 // Add the standard options
305 Trace::populate_popup_form(parent, form);
307 // Add the decoder options
309 channel_selectors_.clear();
310 decoder_forms_.clear();
312 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
315 QLabel *const l = new QLabel(
316 tr("<p><i>No decoders in the stack</i></p>"));
317 l->setAlignment(Qt::AlignCenter);
320 auto iter = stack.cbegin();
321 for (int i = 0; i < (int)stack.size(); i++, iter++) {
322 shared_ptr<Decoder> dec(*iter);
323 create_decoder_form(i, dec, parent, form);
326 form->addRow(new QLabel(
327 tr("<i>* Required channels</i>"), parent));
330 // Add stacking button
331 pv::widgets::DecoderMenu *const decoder_menu =
332 new pv::widgets::DecoderMenu(parent);
333 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
334 this, SLOT(on_stack_decoder(srd_decoder*)));
336 QPushButton *const stack_button =
337 new QPushButton(tr("Stack Decoder"), parent);
338 stack_button->setMenu(decoder_menu);
340 QHBoxLayout *stack_button_box = new QHBoxLayout;
341 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
342 form->addRow(stack_button_box);
345 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
347 QMenu *const menu = Trace::create_context_menu(parent);
349 menu->addSeparator();
351 QAction *const del = new QAction(tr("Delete"), this);
352 del->setShortcuts(QKeySequence::Delete);
353 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
354 menu->addAction(del);
359 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
360 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
361 size_t base_colour, int row_title_width)
363 using namespace pv::data::decode;
365 vector<Annotation> a_block;
368 double samples_per_pixel, pixels_offset;
369 tie(pixels_offset, samples_per_pixel) =
370 get_pixels_offset_samples_per_pixel();
372 // Sort the annotations by start sample so that decoders
373 // can't confuse us by creating annotations out of order
374 stable_sort(annotations.begin(), annotations.end(),
375 [](const Annotation &a, const Annotation &b) {
376 return a.start_sample() < b.start_sample(); });
378 // Gather all annotations that form a visual "block" and draw them as such
379 for (const Annotation &a : annotations) {
381 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
382 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
383 const int a_width = a_end - a_start;
385 const int delta = a_end - p_end;
387 bool a_is_separate = false;
389 // Annotation wider than the threshold for a useful label width?
390 if (a_width >= min_useful_label_width_) {
391 for (const QString &ann_text : a.annotations()) {
392 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
393 // Annotation wide enough to fit a label? Don't put it in a block then
395 a_is_separate = true;
401 // Were the previous and this annotation more than a pixel apart?
402 if ((abs(delta) > 1) || a_is_separate) {
403 // Block was broken, draw annotations that form the current block
404 if (a_block.size() == 1) {
405 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
409 draw_annotation_block(a_block, p, h, y, base_colour);
415 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
416 // Next annotation must start a new block. delta will be > 1
417 // because we set p_end to INT_MIN but that's okay since
418 // a_block will be empty, so nothing will be drawn
421 a_block.push_back(a);
426 if (a_block.size() == 1)
427 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
430 draw_annotation_block(a_block, p, h, y, base_colour);
433 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
434 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
435 size_t base_colour, int row_title_width) const
437 double samples_per_pixel, pixels_offset;
438 tie(pixels_offset, samples_per_pixel) =
439 get_pixels_offset_samples_per_pixel();
441 const double start = a.start_sample() / samples_per_pixel -
443 const double end = a.end_sample() / samples_per_pixel -
446 const size_t colour = (base_colour + a.format()) % countof(Colours);
447 p.setPen(OutlineColours[colour]);
448 p.setBrush(Colours[colour]);
450 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
453 if (a.start_sample() == a.end_sample())
454 draw_instant(a, p, h, start, y);
456 draw_range(a, p, h, start, end, y, pp,
460 void DecodeTrace::draw_annotation_block(
461 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
462 int y, size_t base_colour) const
464 using namespace pv::data::decode;
466 if (annotations.empty())
469 double samples_per_pixel, pixels_offset;
470 tie(pixels_offset, samples_per_pixel) =
471 get_pixels_offset_samples_per_pixel();
473 const double start = annotations.front().start_sample() /
474 samples_per_pixel - pixels_offset;
475 const double end = annotations.back().end_sample() /
476 samples_per_pixel - pixels_offset;
478 const double top = y + .5 - h / 2;
479 const double bottom = y + .5 + h / 2;
481 const size_t colour = (base_colour + annotations.front().format()) %
484 // Check if all annotations are of the same type (i.e. we can use one color)
485 // or if we should use a neutral color (i.e. gray)
486 const int format = annotations.front().format();
487 const bool single_format = std::all_of(
488 annotations.begin(), annotations.end(),
489 [&](const Annotation &a) { return a.format() == format; });
491 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
492 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
495 QRectF(start, top, end - start, bottom - top), h/4, h/4);
498 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
499 int h, double x, int y) const
501 const QString text = a.annotations().empty() ?
502 QString() : a.annotations().back();
503 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
505 const QRectF rect(x - w / 2, y - h / 2, w, h);
507 p.drawRoundedRect(rect, h / 2, h / 2);
510 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
513 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
514 int h, double start, double end, int y, const ViewItemPaintParams &pp,
515 int row_title_width) const
517 const double top = y + .5 - h / 2;
518 const double bottom = y + .5 + h / 2;
519 const vector<QString> annotations = a.annotations();
521 // If the two ends are within 1 pixel, draw a vertical line
522 if (start + 1.0 > end) {
523 p.drawLine(QPointF(start, top), QPointF(start, bottom));
527 const double cap_width = min((end - start) / 4, EndCapWidth);
530 QPointF(start, y + .5f),
531 QPointF(start + cap_width, top),
532 QPointF(end - cap_width, top),
533 QPointF(end, y + .5f),
534 QPointF(end - cap_width, bottom),
535 QPointF(start + cap_width, bottom)
538 p.drawConvexPolygon(pts, countof(pts));
540 if (annotations.empty())
543 const int ann_start = start + cap_width;
544 const int ann_end = end - cap_width;
546 const int real_start = std::max(ann_start, pp.left() + row_title_width);
547 const int real_end = std::min(ann_end, pp.right());
548 const int real_width = real_end - real_start;
550 QRectF rect(real_start, y - h / 2, real_width, h);
551 if (rect.width() <= 4)
556 // Try to find an annotation that will fit
557 QString best_annotation;
560 for (const QString &a : annotations) {
561 const int w = p.boundingRect(QRectF(), 0, a).width();
562 if (w <= rect.width() && w > best_width)
563 best_annotation = a, best_width = w;
566 if (best_annotation.isEmpty())
567 best_annotation = annotations.back();
569 // If not ellide the last in the list
570 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
571 best_annotation, Qt::ElideRight, rect.width()));
574 void DecodeTrace::draw_error(QPainter &p, const QString &message,
575 const ViewItemPaintParams &pp)
577 const int y = get_visual_y();
579 p.setPen(ErrorBgColour.darker());
580 p.setBrush(ErrorBgColour);
582 const QRectF bounding_rect =
583 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
584 const QRectF text_rect = p.boundingRect(bounding_rect,
585 Qt::AlignCenter, message);
586 const float r = text_rect.height() / 4;
588 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
592 p.drawText(text_rect, message);
595 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
598 using namespace pv::data;
599 using pv::data::decode::Decoder;
601 double samples_per_pixel, pixels_offset;
603 assert(decoder_stack_);
605 shared_ptr<Logic> data;
606 shared_ptr<data::SignalBase> signalbase;
608 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
610 // We get the logic data of the first channel in the list.
611 // This works because we are currently assuming all
612 // LogicSignals have the same data/segment
613 for (const shared_ptr<Decoder> &dec : stack)
614 if (dec && !dec->channels().empty() &&
615 ((signalbase = (*dec->channels().begin()).second)) &&
616 ((data = signalbase->logic_data())))
619 if (!data || data->logic_segments().empty())
622 const shared_ptr<LogicSegment> segment =
623 data->logic_segments().front();
625 const int64_t sample_count = (int64_t)segment->get_sample_count();
626 if (sample_count == 0)
629 const int64_t samples_decoded = decoder_stack_->samples_decoded();
630 if (sample_count == samples_decoded)
633 const int y = get_visual_y();
635 tie(pixels_offset, samples_per_pixel) =
636 get_pixels_offset_samples_per_pixel();
638 const double start = max(samples_decoded /
639 samples_per_pixel - pixels_offset, left - 1.0);
640 const double end = min(sample_count / samples_per_pixel -
641 pixels_offset, right + 1.0);
642 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
644 p.setPen(QPen(Qt::NoPen));
645 p.setBrush(Qt::white);
646 p.drawRect(no_decode_rect);
648 p.setPen(NoDecodeColour);
649 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
650 p.drawRect(no_decode_rect);
653 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
656 assert(decoder_stack_);
658 const View *view = owner_->view();
661 const double scale = view->scale();
664 const double pixels_offset =
665 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
667 double samplerate = decoder_stack_->samplerate();
669 // Show sample rate as 1Hz when it is unknown
670 if (samplerate == 0.0)
673 return make_pair(pixels_offset, samplerate * scale);
676 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
677 int x_start, int x_end) const
679 double samples_per_pixel, pixels_offset;
680 tie(pixels_offset, samples_per_pixel) =
681 get_pixels_offset_samples_per_pixel();
683 const uint64_t start = (uint64_t)max(
684 (x_start + pixels_offset) * samples_per_pixel, 0.0);
685 const uint64_t end = (uint64_t)max(
686 (x_end + pixels_offset) * samples_per_pixel, 0.0);
688 return make_pair(start, end);
691 int DecodeTrace::get_row_at_point(const QPoint &point)
696 const int y = (point.y() - get_visual_y() + row_height_ / 2);
698 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
702 const int row = y / row_height_;
704 if (row >= (int)visible_rows_.size())
710 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
712 using namespace pv::data::decode;
717 const pair<uint64_t, uint64_t> sample_range =
718 get_sample_range(point.x(), point.x() + 1);
719 const int row = get_row_at_point(point);
723 vector<pv::data::decode::Annotation> annotations;
725 assert(decoder_stack_);
726 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
727 sample_range.first, sample_range.second);
729 return (annotations.empty()) ?
730 QString() : annotations[0].annotations().front();
733 void DecodeTrace::hover_point_changed()
737 const View *const view = owner_->view();
740 QPoint hp = view->hover_point();
741 QString ann = get_annotation_at_point(hp);
745 if (!row_height_ || ann.isEmpty()) {
746 QToolTip::hideText();
750 const int hover_row = get_row_at_point(hp);
752 QFontMetrics m(QToolTip::font());
753 const QRect text_size = m.boundingRect(QRect(), 0, ann);
755 // This is OS-specific and unfortunately we can't query it, so
756 // use an approximation to at least try to minimize the error.
757 const int padding = 8;
759 // Make sure the tool tip doesn't overlap with the mouse cursor.
760 // If it did, the tool tip would constantly hide and re-appear.
761 // We also push it up by one row so that it appears above the
762 // decode trace, not below.
763 hp.setX(hp.x() - (text_size.width() / 2) - padding);
765 hp.setY(get_visual_y() - (row_height_ / 2) +
766 (hover_row * row_height_) -
767 row_height_ - text_size.height() - padding);
769 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
772 void DecodeTrace::create_decoder_form(int index,
773 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
779 const srd_decoder *const decoder = dec->decoder();
782 const bool decoder_deletable = index > 0;
784 pv::widgets::DecoderGroupBox *const group =
785 new pv::widgets::DecoderGroupBox(
786 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
787 group->set_decoder_visible(dec->shown());
789 if (decoder_deletable) {
790 delete_mapper_.setMapping(group, index);
791 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
794 show_hide_mapper_.setMapping(group, index);
795 connect(group, SIGNAL(show_hide_decoder()),
796 &show_hide_mapper_, SLOT(map()));
798 QFormLayout *const decoder_form = new QFormLayout;
799 group->add_layout(decoder_form);
801 // Add the mandatory channels
802 for (l = decoder->channels; l; l = l->next) {
803 const struct srd_channel *const pdch =
804 (struct srd_channel *)l->data;
805 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
806 connect(combo, SIGNAL(currentIndexChanged(int)),
807 this, SLOT(on_channel_selected(int)));
808 decoder_form->addRow(tr("<b>%1</b> (%2) *")
809 .arg(QString::fromUtf8(pdch->name),
810 QString::fromUtf8(pdch->desc)), combo);
812 const ChannelSelector s = {combo, dec, pdch};
813 channel_selectors_.push_back(s);
816 // Add the optional channels
817 for (l = decoder->opt_channels; l; l = l->next) {
818 const struct srd_channel *const pdch =
819 (struct srd_channel *)l->data;
820 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
821 connect(combo, SIGNAL(currentIndexChanged(int)),
822 this, SLOT(on_channel_selected(int)));
823 decoder_form->addRow(tr("<b>%1</b> (%2)")
824 .arg(QString::fromUtf8(pdch->name),
825 QString::fromUtf8(pdch->desc)), combo);
827 const ChannelSelector s = {combo, dec, pdch};
828 channel_selectors_.push_back(s);
832 shared_ptr<binding::Decoder> binding(
833 new binding::Decoder(decoder_stack_, dec));
834 binding->add_properties_to_form(decoder_form, true);
836 bindings_.push_back(binding);
839 decoder_forms_.push_back(group);
842 QComboBox* DecodeTrace::create_channel_selector(
843 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
844 const srd_channel *const pdch)
848 const auto sigs(session_.signalbases());
850 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
851 std::sort(sig_list.begin(), sig_list.end(),
852 [](const shared_ptr<data::SignalBase> &a,
853 const shared_ptr<data::SignalBase> &b) {
854 return strnatcasecmp(a->name().toStdString(),
855 b->name().toStdString()) < 0; });
857 const auto channel_iter = dec->channels().find(pdch);
859 QComboBox *selector = new QComboBox(parent);
861 selector->addItem("-", qVariantFromValue((void*)nullptr));
863 if (channel_iter == dec->channels().end())
864 selector->setCurrentIndex(0);
866 for (const shared_ptr<data::SignalBase> &b : sig_list) {
868 if (b->type() == sigrok::ChannelType::LOGIC && b->enabled()) {
869 selector->addItem(b->name(),
870 qVariantFromValue((void*)b.get()));
872 if (channel_iter != dec->channels().end() &&
873 (*channel_iter).second == b)
874 selector->setCurrentIndex(
875 selector->count() - 1);
882 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
886 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
888 const unordered_set< shared_ptr<data::SignalBase> >
889 sigs(session_.signalbases());
891 for (const ChannelSelector &s : channel_selectors_) {
892 if (s.decoder_ != dec)
895 const data::SignalBase *const selection =
896 (data::SignalBase*)s.combo_->itemData(
897 s.combo_->currentIndex()).value<void*>();
899 for (shared_ptr<data::SignalBase> sig : sigs)
900 if (sig.get() == selection) {
901 channel_map[s.pdch_] = sig;
906 dec->set_channels(channel_map);
909 void DecodeTrace::commit_channels()
911 assert(decoder_stack_);
912 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
913 commit_decoder_channels(dec);
915 decoder_stack_->begin_decode();
918 void DecodeTrace::on_new_decode_data()
921 owner_->row_item_appearance_changed(false, true);
924 void DecodeTrace::delete_pressed()
929 void DecodeTrace::on_delete()
931 session_.remove_decode_signal(this);
934 void DecodeTrace::on_channel_selected(int)
939 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
942 assert(decoder_stack_);
943 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
944 new data::decode::Decoder(decoder)));
945 decoder_stack_->begin_decode();
950 void DecodeTrace::on_delete_decoder(int index)
952 decoder_stack_->remove(index);
957 decoder_stack_->begin_decode();
960 void DecodeTrace::on_show_hide_decoder(int index)
962 using pv::data::decode::Decoder;
964 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
966 // Find the decoder in the stack
967 auto iter = stack.cbegin();
968 for (int i = 0; i < index; i++, iter++)
969 assert(iter != stack.end());
971 shared_ptr<Decoder> dec = *iter;
974 const bool show = !dec->shown();
977 assert(index < (int)decoder_forms_.size());
978 decoder_forms_[index]->set_decoder_visible(show);
981 owner_->row_item_appearance_changed(false, true);