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, see <http://www.gnu.org/licenses/>.
21 #include <libsigrokdecode/libsigrokdecode.h>
30 #include <boost/functional/hash.hpp>
33 #include <QApplication>
35 #include <QFormLayout>
38 #include <QPushButton>
41 #include "decodetrace.hpp"
43 #include "viewport.hpp"
45 #include <pv/globalsettings.hpp>
46 #include <pv/session.hpp>
47 #include <pv/strnatcmp.hpp>
48 #include <pv/data/decodesignal.hpp>
49 #include <pv/data/decode/annotation.hpp>
50 #include <pv/data/decode/decoder.hpp>
51 #include <pv/data/logic.hpp>
52 #include <pv/data/logicsegment.hpp>
53 #include <pv/widgets/decodergroupbox.hpp>
54 #include <pv/widgets/decodermenu.hpp>
63 using std::out_of_range;
65 using std::shared_ptr;
66 using std::make_shared;
68 using std::unordered_set;
71 using pv::data::decode::Annotation;
72 using pv::data::decode::Row;
73 using pv::data::DecodeChannel;
74 using pv::data::DecodeSignal;
80 const QColor DecodeTrace::DecodeColours[4] = {
81 QColor(0xEF, 0x29, 0x29), // Red
82 QColor(0xFC, 0xE9, 0x4F), // Yellow
83 QColor(0x8A, 0xE2, 0x34), // Green
84 QColor(0x72, 0x9F, 0xCF) // Blue
87 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
88 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
90 const int DecodeTrace::ArrowSize = 4;
91 const double DecodeTrace::EndCapWidth = 5;
92 const int DecodeTrace::RowTitleMargin = 10;
93 const int DecodeTrace::DrawPadding = 100;
95 const QColor DecodeTrace::Colours[16] = {
96 QColor(0xEF, 0x29, 0x29),
97 QColor(0xF6, 0x6A, 0x32),
98 QColor(0xFC, 0xAE, 0x3E),
99 QColor(0xFB, 0xCA, 0x47),
100 QColor(0xFC, 0xE9, 0x4F),
101 QColor(0xCD, 0xF0, 0x40),
102 QColor(0x8A, 0xE2, 0x34),
103 QColor(0x4E, 0xDC, 0x44),
104 QColor(0x55, 0xD7, 0x95),
105 QColor(0x64, 0xD1, 0xD2),
106 QColor(0x72, 0x9F, 0xCF),
107 QColor(0xD4, 0x76, 0xC4),
108 QColor(0x9D, 0x79, 0xB9),
109 QColor(0xAD, 0x7F, 0xA8),
110 QColor(0xC2, 0x62, 0x9B),
111 QColor(0xD7, 0x47, 0x6F)
114 const QColor DecodeTrace::OutlineColours[16] = {
115 QColor(0x77, 0x14, 0x14),
116 QColor(0x7B, 0x35, 0x19),
117 QColor(0x7E, 0x57, 0x1F),
118 QColor(0x7D, 0x65, 0x23),
119 QColor(0x7E, 0x74, 0x27),
120 QColor(0x66, 0x78, 0x20),
121 QColor(0x45, 0x71, 0x1A),
122 QColor(0x27, 0x6E, 0x22),
123 QColor(0x2A, 0x6B, 0x4A),
124 QColor(0x32, 0x68, 0x69),
125 QColor(0x39, 0x4F, 0x67),
126 QColor(0x6A, 0x3B, 0x62),
127 QColor(0x4E, 0x3C, 0x5C),
128 QColor(0x56, 0x3F, 0x54),
129 QColor(0x61, 0x31, 0x4D),
130 QColor(0x6B, 0x23, 0x37)
133 DecodeTrace::DecodeTrace(pv::Session &session,
134 shared_ptr<data::SignalBase> signalbase, int index) :
138 max_visible_rows_(0),
139 delete_mapper_(this),
140 show_hide_mapper_(this)
142 decode_signal_ = dynamic_pointer_cast<data::DecodeSignal>(base_);
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 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
150 connect(decode_signal_.get(), SIGNAL(new_annotations()),
151 this, SLOT(on_new_annotations()));
152 connect(decode_signal_.get(), SIGNAL(channels_updated()),
153 this, SLOT(on_channels_updated()));
155 connect(&delete_mapper_, SIGNAL(mapped(int)),
156 this, SLOT(on_delete_decoder(int)));
157 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
158 this, SLOT(on_show_hide_decoder(int)));
161 bool DecodeTrace::enabled() const
166 shared_ptr<data::SignalBase> DecodeTrace::base() const
171 pair<int, int> DecodeTrace::v_extents() const
173 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
175 // Make an empty decode trace appear symmetrical
176 const int row_count = max(1, max_visible_rows_);
178 return make_pair(-row_height, row_height * row_count);
181 void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
183 Trace::paint_back(p, pp);
184 paint_axis(p, pp, get_visual_y());
187 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
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 const QString err = decode_signal_->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 const vector<Row> rows = decode_signal_->visible_rows();
211 visible_rows_.clear();
212 for (const Row& row : rows) {
213 // Cache the row title widths
216 row_title_width = row_title_widths_.at(row);
217 } catch (out_of_range) {
218 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
220 row_title_widths_[row] = w;
224 // Determine the row's color
225 size_t base_colour = 0x13579BDF;
226 boost::hash_combine(base_colour, this);
227 boost::hash_combine(base_colour, row.decoder());
228 boost::hash_combine(base_colour, row.row());
231 vector<Annotation> annotations;
232 decode_signal_->get_annotation_subset(annotations, row,
233 sample_range.first, sample_range.second);
234 if (!annotations.empty()) {
235 draw_annotations(annotations, p, annotation_height, pp, y,
236 base_colour, row_title_width);
240 visible_rows_.push_back(row);
245 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
247 if ((int)visible_rows_.size() > max_visible_rows_)
248 owner_->extents_changed(false, true);
250 // Update the maximum row count if needed
251 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
254 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
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 // Add the standard options
299 Trace::populate_popup_form(parent, form);
301 // Add the decoder options
303 channel_id_map_.clear();
304 init_state_map_.clear();
305 decoder_forms_.clear();
307 const list< shared_ptr<Decoder> > &stack =
308 decode_signal_->decoder_stack_list();
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);
335 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
337 QHBoxLayout *stack_button_box = new QHBoxLayout;
338 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
339 form->addRow(stack_button_box);
342 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
344 QMenu *const menu = Trace::create_context_menu(parent);
346 menu->addSeparator();
348 QAction *const del = new QAction(tr("Delete"), this);
349 del->setShortcuts(QKeySequence::Delete);
350 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
351 menu->addAction(del);
356 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
357 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
358 size_t base_colour, int row_title_width)
360 using namespace pv::data::decode;
362 vector<Annotation> a_block;
365 double samples_per_pixel, pixels_offset;
366 tie(pixels_offset, samples_per_pixel) =
367 get_pixels_offset_samples_per_pixel();
369 // Sort the annotations by start sample so that decoders
370 // can't confuse us by creating annotations out of order
371 stable_sort(annotations.begin(), annotations.end(),
372 [](const Annotation &a, const Annotation &b) {
373 return a.start_sample() < b.start_sample(); });
375 // Gather all annotations that form a visual "block" and draw them as such
376 for (const Annotation &a : annotations) {
378 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
379 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
380 const int a_width = a_end - a_start;
382 const int delta = a_end - p_end;
384 bool a_is_separate = false;
386 // Annotation wider than the threshold for a useful label width?
387 if (a_width >= min_useful_label_width_) {
388 for (const QString &ann_text : a.annotations()) {
389 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
390 // Annotation wide enough to fit a label? Don't put it in a block then
392 a_is_separate = true;
398 // Were the previous and this annotation more than a pixel apart?
399 if ((abs(delta) > 1) || a_is_separate) {
400 // Block was broken, draw annotations that form the current block
401 if (a_block.size() == 1) {
402 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
406 draw_annotation_block(a_block, p, h, y, base_colour);
412 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
413 // Next annotation must start a new block. delta will be > 1
414 // because we set p_end to INT_MIN but that's okay since
415 // a_block will be empty, so nothing will be drawn
418 a_block.push_back(a);
423 if (a_block.size() == 1)
424 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
427 draw_annotation_block(a_block, p, h, y, base_colour);
430 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
431 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
432 size_t base_colour, int row_title_width) const
434 double samples_per_pixel, pixels_offset;
435 tie(pixels_offset, samples_per_pixel) =
436 get_pixels_offset_samples_per_pixel();
438 const double start = a.start_sample() / samples_per_pixel -
440 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
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, row_title_width);
455 void DecodeTrace::draw_annotation_block(
456 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
457 int y, size_t base_colour) const
459 using namespace pv::data::decode;
461 if (annotations.empty())
464 double samples_per_pixel, pixels_offset;
465 tie(pixels_offset, samples_per_pixel) =
466 get_pixels_offset_samples_per_pixel();
468 const double start = annotations.front().start_sample() /
469 samples_per_pixel - pixels_offset;
470 const double end = annotations.back().end_sample() /
471 samples_per_pixel - pixels_offset;
473 const double top = y + .5 - h / 2;
474 const double bottom = y + .5 + h / 2;
476 const size_t colour = (base_colour + annotations.front().format()) %
479 // Check if all annotations are of the same type (i.e. we can use one color)
480 // or if we should use a neutral color (i.e. gray)
481 const int format = annotations.front().format();
482 const bool single_format = all_of(
483 annotations.begin(), annotations.end(),
484 [&](const Annotation &a) { return a.format() == format; });
486 const QRectF rect(start, top, end - start, bottom - top);
489 p.setPen(QPen(Qt::NoPen));
490 p.setBrush(Qt::white);
491 p.drawRoundedRect(rect, r, r);
493 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
494 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
496 p.drawRoundedRect(rect, r, r);
499 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
500 int h, double x, int y) const
502 const QString text = a.annotations().empty() ?
503 QString() : a.annotations().back();
504 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
506 const QRectF rect(x - w / 2, y - h / 2, w, h);
508 p.drawRoundedRect(rect, h / 2, h / 2);
511 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
514 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
515 int h, double start, double end, int y, const ViewItemPaintParams &pp,
516 int row_title_width) const
518 const double top = y + .5 - h / 2;
519 const double bottom = y + .5 + h / 2;
520 const vector<QString> annotations = a.annotations();
522 // If the two ends are within 1 pixel, draw a vertical line
523 if (start + 1.0 > end) {
524 p.drawLine(QPointF(start, top), QPointF(start, bottom));
528 const double cap_width = min((end - start) / 4, EndCapWidth);
531 QPointF(start, y + .5f),
532 QPointF(start + cap_width, top),
533 QPointF(end - cap_width, top),
534 QPointF(end, y + .5f),
535 QPointF(end - cap_width, bottom),
536 QPointF(start + cap_width, bottom)
539 p.drawConvexPolygon(pts, countof(pts));
541 if (annotations.empty())
544 const int ann_start = start + cap_width;
545 const int ann_end = end - cap_width;
547 const int real_start = max(ann_start, pp.left() + row_title_width);
548 const int real_end = min(ann_end, pp.right());
549 const int real_width = real_end - real_start;
551 QRectF rect(real_start, y - h / 2, real_width, h);
552 if (rect.width() <= 4)
557 // Try to find an annotation that will fit
558 QString best_annotation;
561 for (const QString &a : annotations) {
562 const int w = p.boundingRect(QRectF(), 0, a).width();
563 if (w <= rect.width() && w > best_width)
564 best_annotation = a, best_width = w;
567 if (best_annotation.isEmpty())
568 best_annotation = annotations.back();
570 // If not ellide the last in the list
571 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
572 best_annotation, Qt::ElideRight, rect.width()));
575 void DecodeTrace::draw_error(QPainter &p, const QString &message,
576 const ViewItemPaintParams &pp)
578 const int y = get_visual_y();
580 p.setPen(ErrorBgColour.darker());
581 p.setBrush(ErrorBgColour);
583 const QRectF bounding_rect =
584 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
585 const QRectF text_rect = p.boundingRect(bounding_rect,
586 Qt::AlignCenter, message);
587 const float r = text_rect.height() / 4;
589 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
593 p.drawText(text_rect, message);
596 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
598 using namespace pv::data;
599 using pv::data::decode::Decoder;
601 double samples_per_pixel, pixels_offset;
603 const int64_t sample_count = decode_signal_->sample_count();
604 if (sample_count == 0)
607 const int64_t samples_decoded = decode_signal_->samples_decoded();
608 if (sample_count == samples_decoded)
611 const int y = get_visual_y();
613 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
615 const double start = max(samples_decoded /
616 samples_per_pixel - pixels_offset, left - 1.0);
617 const double end = min(sample_count / samples_per_pixel -
618 pixels_offset, right + 1.0);
619 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
621 p.setPen(QPen(Qt::NoPen));
622 p.setBrush(Qt::white);
623 p.drawRect(no_decode_rect);
625 p.setPen(NoDecodeColour);
626 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
627 p.drawRect(no_decode_rect);
630 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
634 const View *view = owner_->view();
637 const double scale = view->scale();
640 const double pixels_offset =
641 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
643 double samplerate = decode_signal_->samplerate();
645 // Show sample rate as 1Hz when it is unknown
646 if (samplerate == 0.0)
649 return make_pair(pixels_offset, samplerate * scale);
652 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
653 int x_start, int x_end) const
655 double samples_per_pixel, pixels_offset;
656 tie(pixels_offset, samples_per_pixel) =
657 get_pixels_offset_samples_per_pixel();
659 const uint64_t start = (uint64_t)max(
660 (x_start + pixels_offset) * samples_per_pixel, 0.0);
661 const uint64_t end = (uint64_t)max(
662 (x_end + pixels_offset) * samples_per_pixel, 0.0);
664 return make_pair(start, end);
667 int DecodeTrace::get_row_at_point(const QPoint &point)
672 const int y = (point.y() - get_visual_y() + row_height_ / 2);
674 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
678 const int row = y / row_height_;
680 if (row >= (int)visible_rows_.size())
686 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
688 using namespace pv::data::decode;
693 const pair<uint64_t, uint64_t> sample_range =
694 get_sample_range(point.x(), point.x() + 1);
695 const int row = get_row_at_point(point);
699 vector<pv::data::decode::Annotation> annotations;
701 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
702 sample_range.first, sample_range.second);
704 return (annotations.empty()) ?
705 QString() : annotations[0].annotations().front();
708 void DecodeTrace::hover_point_changed()
712 const View *const view = owner_->view();
715 QPoint hp = view->hover_point();
716 QString ann = get_annotation_at_point(hp);
720 if (!row_height_ || ann.isEmpty()) {
721 QToolTip::hideText();
725 const int hover_row = get_row_at_point(hp);
727 QFontMetrics m(QToolTip::font());
728 const QRect text_size = m.boundingRect(QRect(), 0, ann);
730 // This is OS-specific and unfortunately we can't query it, so
731 // use an approximation to at least try to minimize the error.
732 const int padding = 8;
734 // Make sure the tool tip doesn't overlap with the mouse cursor.
735 // If it did, the tool tip would constantly hide and re-appear.
736 // We also push it up by one row so that it appears above the
737 // decode trace, not below.
738 hp.setX(hp.x() - (text_size.width() / 2) - padding);
740 hp.setY(get_visual_y() - (row_height_ / 2) +
741 (hover_row * row_height_) -
742 row_height_ - text_size.height() - padding);
744 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
747 void DecodeTrace::create_decoder_form(int index,
748 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
751 GlobalSettings settings;
754 const srd_decoder *const decoder = dec->decoder();
757 const bool decoder_deletable = index > 0;
759 pv::widgets::DecoderGroupBox *const group =
760 new pv::widgets::DecoderGroupBox(
761 QString::fromUtf8(decoder->name),
762 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
763 QString::fromUtf8(decoder->desc)),
764 nullptr, decoder_deletable);
765 group->set_decoder_visible(dec->shown());
767 if (decoder_deletable) {
768 delete_mapper_.setMapping(group, index);
769 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
772 show_hide_mapper_.setMapping(group, index);
773 connect(group, SIGNAL(show_hide_decoder()),
774 &show_hide_mapper_, SLOT(map()));
776 QFormLayout *const decoder_form = new QFormLayout;
777 group->add_layout(decoder_form);
779 const list<DecodeChannel> channels = decode_signal_->get_channels();
782 for (DecodeChannel ch : channels) {
783 // Ignore channels not part of the decoder we create the form for
784 if (ch.decoder_ != dec)
787 QComboBox *const combo = create_channel_selector(parent, &ch);
788 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
790 channel_id_map_[combo] = ch.id;
791 init_state_map_[combo_init_state] = ch.id;
793 connect(combo, SIGNAL(currentIndexChanged(int)),
794 this, SLOT(on_channel_selected(int)));
795 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
796 this, SLOT(on_init_state_changed(int)));
798 QHBoxLayout *const hlayout = new QHBoxLayout;
799 hlayout->addWidget(combo);
800 hlayout->addWidget(combo_init_state);
802 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
803 combo_init_state->hide();
805 const QString required_flag = ch.is_optional ? QString() : QString("*");
806 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
807 .arg(ch.name, ch.desc, required_flag), hlayout);
810 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
813 shared_ptr<binding::Decoder> binding(
814 new binding::Decoder(decoder_stack, dec));
815 binding->add_properties_to_form(decoder_form, true);
817 bindings_.push_back(binding);
820 decoder_forms_.push_back(group);
823 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
825 const auto sigs(session_.signalbases());
827 // Sort signals in natural order
828 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
829 sort(sig_list.begin(), sig_list.end(),
830 [](const shared_ptr<data::SignalBase> &a,
831 const shared_ptr<data::SignalBase> &b) {
832 return strnatcasecmp(a->name().toStdString(),
833 b->name().toStdString()) < 0; });
835 QComboBox *selector = new QComboBox(parent);
837 selector->addItem("-", qVariantFromValue((void*)nullptr));
839 if (!ch->assigned_signal)
840 selector->setCurrentIndex(0);
842 for (const shared_ptr<data::SignalBase> &b : sig_list) {
844 if (b->logic_data() && b->enabled()) {
845 selector->addItem(b->name(),
846 qVariantFromValue((void*)b.get()));
848 if (ch->assigned_signal == b.get())
849 selector->setCurrentIndex(selector->count() - 1);
856 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
857 const DecodeChannel *ch)
859 QComboBox *selector = new QComboBox(parent);
861 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
862 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
863 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
865 selector->setCurrentIndex(ch->initial_pin_state);
867 selector->setToolTip("Initial (assumed) pin value before the first sample");
872 void DecodeTrace::on_new_annotations()
875 owner_->row_item_appearance_changed(false, true);
878 void DecodeTrace::delete_pressed()
883 void DecodeTrace::on_delete()
885 session_.remove_decode_signal(decode_signal_);
888 void DecodeTrace::on_channel_selected(int)
890 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
892 // Determine signal that was selected
893 const data::SignalBase *signal =
894 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
896 // Determine decode channel ID this combo box is the channel selector for
897 const uint16_t id = channel_id_map_.at(cb);
899 decode_signal_->assign_signal(id, signal);
902 void DecodeTrace::on_channels_updated()
905 owner_->row_item_appearance_changed(false, true);
908 void DecodeTrace::on_init_state_changed(int)
910 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
912 // Determine inital pin state that was selected
913 int init_state = cb->itemData(cb->currentIndex()).value<int>();
915 // Determine decode channel ID this combo box is the channel selector for
916 const uint16_t id = init_state_map_.at(cb);
918 decode_signal_->set_initial_pin_state(id, init_state);
921 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
923 decode_signal_->stack_decoder(decoder);
928 void DecodeTrace::on_delete_decoder(int index)
930 decode_signal_->remove_decoder(index);
936 void DecodeTrace::on_show_hide_decoder(int index)
938 const bool state = decode_signal_->toggle_decoder_visibility(index);
940 assert(index < (int)decoder_forms_.size());
941 decoder_forms_[index]->set_decoder_visible(state);
944 owner_->row_item_appearance_changed(false, true);