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>
61 using std::out_of_range;
63 using std::shared_ptr;
64 using std::make_shared;
66 using std::unordered_set;
69 using pv::data::decode::Annotation;
70 using pv::data::decode::Row;
71 using pv::data::DecodeChannel;
72 using pv::data::DecodeSignal;
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 shared_ptr<data::SignalBase> signalbase, int index) :
136 max_visible_rows_(0),
137 delete_mapper_(this),
138 show_hide_mapper_(this)
140 decode_signal_ = dynamic_pointer_cast<data::DecodeSignal>(base_);
142 // Determine shortest string we want to see displayed in full
143 QFontMetrics m(QApplication::font());
144 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
146 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
148 connect(decode_signal_.get(), SIGNAL(new_annotations()),
149 this, SLOT(on_new_annotations()));
150 connect(decode_signal_.get(), SIGNAL(channels_updated()),
151 this, SLOT(on_channels_updated()));
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 shared_ptr<data::SignalBase> DecodeTrace::base() const
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, ViewItemPaintParams &pp)
181 Trace::paint_back(p, pp);
182 paint_axis(p, pp, get_visual_y());
185 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
187 const int text_height = ViewItemPaintParams::text_height();
188 row_height_ = (text_height * 6) / 4;
189 const int annotation_height = (text_height * 5) / 4;
191 const QString err = decode_signal_->error_message();
192 if (!err.isEmpty()) {
193 draw_unresolved_period(
194 p, annotation_height, pp.left(), pp.right());
195 draw_error(p, err, pp);
199 // Set default pen to allow for text width calculation
202 // Iterate through the rows
203 int y = get_visual_y();
204 pair<uint64_t, uint64_t> sample_range = get_sample_range(
205 pp.left(), pp.right());
207 const vector<Row> rows = decode_signal_->visible_rows();
209 visible_rows_.clear();
210 for (const Row& row : rows) {
211 // Cache the row title widths
214 row_title_width = row_title_widths_.at(row);
215 } catch (out_of_range) {
216 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
218 row_title_widths_[row] = w;
222 // Determine the row's color
223 size_t base_colour = 0x13579BDF;
224 boost::hash_combine(base_colour, this);
225 boost::hash_combine(base_colour, row.decoder());
226 boost::hash_combine(base_colour, row.row());
229 vector<Annotation> annotations;
230 decode_signal_->get_annotation_subset(annotations, row,
231 sample_range.first, sample_range.second);
232 if (!annotations.empty()) {
233 draw_annotations(annotations, p, annotation_height, pp, y,
234 base_colour, row_title_width);
238 visible_rows_.push_back(row);
243 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
245 if ((int)visible_rows_.size() > max_visible_rows_)
246 owner_->extents_changed(false, true);
248 // Update the maximum row count if needed
249 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
252 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
256 for (size_t i = 0; i < visible_rows_.size(); i++) {
257 const int y = i * row_height_ + get_visual_y();
259 p.setPen(QPen(Qt::NoPen));
260 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
263 const QPointF points[] = {
264 QPointF(pp.left(), y - ArrowSize),
265 QPointF(pp.left() + ArrowSize, y),
266 QPointF(pp.left(), y + ArrowSize)
268 p.drawPolygon(points, countof(points));
271 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
272 pp.right() - pp.left(), row_height_);
273 const QString h(visible_rows_[i].title());
274 const int f = Qt::AlignLeft | Qt::AlignVCenter |
278 p.setPen(QApplication::palette().color(QPalette::Base));
279 for (int dx = -1; dx <= 1; dx++)
280 for (int dy = -1; dy <= 1; dy++)
281 if (dx != 0 && dy != 0)
282 p.drawText(r.translated(dx, dy), f, h);
285 p.setPen(QApplication::palette().color(QPalette::WindowText));
290 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
292 using pv::data::decode::Decoder;
296 // Add the standard options
297 Trace::populate_popup_form(parent, form);
299 // Add the decoder options
301 channel_id_map_.clear();
302 init_state_map_.clear();
303 decoder_forms_.clear();
305 const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
308 QLabel *const l = new QLabel(
309 tr("<p><i>No decoders in the stack</i></p>"));
310 l->setAlignment(Qt::AlignCenter);
313 auto iter = stack.cbegin();
314 for (int i = 0; i < (int)stack.size(); i++, iter++) {
315 shared_ptr<Decoder> dec(*iter);
316 create_decoder_form(i, dec, parent, form);
319 form->addRow(new QLabel(
320 tr("<i>* Required channels</i>"), parent));
323 // Add stacking button
324 pv::widgets::DecoderMenu *const decoder_menu =
325 new pv::widgets::DecoderMenu(parent);
326 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
327 this, SLOT(on_stack_decoder(srd_decoder*)));
329 QPushButton *const stack_button =
330 new QPushButton(tr("Stack Decoder"), parent);
331 stack_button->setMenu(decoder_menu);
332 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
334 QHBoxLayout *stack_button_box = new QHBoxLayout;
335 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
336 form->addRow(stack_button_box);
339 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
341 QMenu *const menu = Trace::create_context_menu(parent);
343 menu->addSeparator();
345 QAction *const del = new QAction(tr("Delete"), this);
346 del->setShortcuts(QKeySequence::Delete);
347 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
348 menu->addAction(del);
353 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
354 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
355 size_t base_colour, int row_title_width)
357 using namespace pv::data::decode;
359 vector<Annotation> a_block;
362 double samples_per_pixel, pixels_offset;
363 tie(pixels_offset, samples_per_pixel) =
364 get_pixels_offset_samples_per_pixel();
366 // Sort the annotations by start sample so that decoders
367 // can't confuse us by creating annotations out of order
368 stable_sort(annotations.begin(), annotations.end(),
369 [](const Annotation &a, const Annotation &b) {
370 return a.start_sample() < b.start_sample(); });
372 // Gather all annotations that form a visual "block" and draw them as such
373 for (const Annotation &a : annotations) {
375 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
376 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
377 const int a_width = a_end - a_start;
379 const int delta = a_end - p_end;
381 bool a_is_separate = false;
383 // Annotation wider than the threshold for a useful label width?
384 if (a_width >= min_useful_label_width_) {
385 for (const QString &ann_text : a.annotations()) {
386 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
387 // Annotation wide enough to fit a label? Don't put it in a block then
389 a_is_separate = true;
395 // Were the previous and this annotation more than a pixel apart?
396 if ((abs(delta) > 1) || a_is_separate) {
397 // Block was broken, draw annotations that form the current block
398 if (a_block.size() == 1) {
399 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
403 draw_annotation_block(a_block, p, h, y, base_colour);
409 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
410 // Next annotation must start a new block. delta will be > 1
411 // because we set p_end to INT_MIN but that's okay since
412 // a_block will be empty, so nothing will be drawn
415 a_block.push_back(a);
420 if (a_block.size() == 1)
421 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
424 draw_annotation_block(a_block, p, h, y, base_colour);
427 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
428 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
429 size_t base_colour, int row_title_width) const
431 double samples_per_pixel, pixels_offset;
432 tie(pixels_offset, samples_per_pixel) =
433 get_pixels_offset_samples_per_pixel();
435 const double start = a.start_sample() / samples_per_pixel -
437 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
439 const size_t colour = (base_colour + a.format()) % countof(Colours);
440 p.setPen(OutlineColours[colour]);
441 p.setBrush(Colours[colour]);
443 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
446 if (a.start_sample() == a.end_sample())
447 draw_instant(a, p, h, start, y);
449 draw_range(a, p, h, start, end, y, pp, row_title_width);
452 void DecodeTrace::draw_annotation_block(
453 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
454 int y, size_t base_colour) const
456 using namespace pv::data::decode;
458 if (annotations.empty())
461 double samples_per_pixel, pixels_offset;
462 tie(pixels_offset, samples_per_pixel) =
463 get_pixels_offset_samples_per_pixel();
465 const double start = annotations.front().start_sample() /
466 samples_per_pixel - pixels_offset;
467 const double end = annotations.back().end_sample() /
468 samples_per_pixel - pixels_offset;
470 const double top = y + .5 - h / 2;
471 const double bottom = y + .5 + h / 2;
473 const size_t colour = (base_colour + annotations.front().format()) %
476 // Check if all annotations are of the same type (i.e. we can use one color)
477 // or if we should use a neutral color (i.e. gray)
478 const int format = annotations.front().format();
479 const bool single_format = all_of(
480 annotations.begin(), annotations.end(),
481 [&](const Annotation &a) { return a.format() == format; });
483 const QRectF rect(start, top, end - start, bottom - top);
486 p.setPen(QPen(Qt::NoPen));
487 p.setBrush(Qt::white);
488 p.drawRoundedRect(rect, r, r);
490 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
491 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
493 p.drawRoundedRect(rect, r, r);
496 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
497 int h, double x, int y) const
499 const QString text = a.annotations().empty() ?
500 QString() : a.annotations().back();
501 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
503 const QRectF rect(x - w / 2, y - h / 2, w, h);
505 p.drawRoundedRect(rect, h / 2, h / 2);
508 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
511 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
512 int h, double start, double end, int y, const ViewItemPaintParams &pp,
513 int row_title_width) const
515 const double top = y + .5 - h / 2;
516 const double bottom = y + .5 + h / 2;
517 const vector<QString> annotations = a.annotations();
519 // If the two ends are within 1 pixel, draw a vertical line
520 if (start + 1.0 > end) {
521 p.drawLine(QPointF(start, top), QPointF(start, bottom));
525 const double cap_width = min((end - start) / 4, EndCapWidth);
528 QPointF(start, y + .5f),
529 QPointF(start + cap_width, top),
530 QPointF(end - cap_width, top),
531 QPointF(end, y + .5f),
532 QPointF(end - cap_width, bottom),
533 QPointF(start + cap_width, bottom)
536 p.drawConvexPolygon(pts, countof(pts));
538 if (annotations.empty())
541 const int ann_start = start + cap_width;
542 const int ann_end = end - cap_width;
544 const int real_start = max(ann_start, pp.left() + row_title_width);
545 const int real_end = min(ann_end, pp.right());
546 const int real_width = real_end - real_start;
548 QRectF rect(real_start, y - h / 2, real_width, h);
549 if (rect.width() <= 4)
554 // Try to find an annotation that will fit
555 QString best_annotation;
558 for (const QString &a : annotations) {
559 const int w = p.boundingRect(QRectF(), 0, a).width();
560 if (w <= rect.width() && w > best_width)
561 best_annotation = a, best_width = w;
564 if (best_annotation.isEmpty())
565 best_annotation = annotations.back();
567 // If not ellide the last in the list
568 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
569 best_annotation, Qt::ElideRight, rect.width()));
572 void DecodeTrace::draw_error(QPainter &p, const QString &message,
573 const ViewItemPaintParams &pp)
575 const int y = get_visual_y();
577 p.setPen(ErrorBgColour.darker());
578 p.setBrush(ErrorBgColour);
580 const QRectF bounding_rect =
581 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
582 const QRectF text_rect = p.boundingRect(bounding_rect,
583 Qt::AlignCenter, message);
584 const float r = text_rect.height() / 4;
586 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
590 p.drawText(text_rect, message);
593 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
595 using namespace pv::data;
596 using pv::data::decode::Decoder;
598 double samples_per_pixel, pixels_offset;
600 const int64_t sample_count = decode_signal_->get_working_sample_count();
601 if (sample_count == 0)
604 const int64_t samples_decoded = decode_signal_->get_decoded_sample_count();
605 if (sample_count == samples_decoded)
608 const int y = get_visual_y();
610 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
612 const double start = max(samples_decoded /
613 samples_per_pixel - pixels_offset, left - 1.0);
614 const double end = min(sample_count / samples_per_pixel -
615 pixels_offset, right + 1.0);
616 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
618 p.setPen(QPen(Qt::NoPen));
619 p.setBrush(Qt::white);
620 p.drawRect(no_decode_rect);
622 p.setPen(NoDecodeColour);
623 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
624 p.drawRect(no_decode_rect);
627 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
631 const View *view = owner_->view();
634 const double scale = view->scale();
637 const double pixels_offset =
638 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
640 double samplerate = decode_signal_->samplerate();
642 // Show sample rate as 1Hz when it is unknown
643 if (samplerate == 0.0)
646 return make_pair(pixels_offset, samplerate * scale);
649 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
650 int x_start, int x_end) const
652 double samples_per_pixel, pixels_offset;
653 tie(pixels_offset, samples_per_pixel) =
654 get_pixels_offset_samples_per_pixel();
656 const uint64_t start = (uint64_t)max(
657 (x_start + pixels_offset) * samples_per_pixel, 0.0);
658 const uint64_t end = (uint64_t)max(
659 (x_end + pixels_offset) * samples_per_pixel, 0.0);
661 return make_pair(start, end);
664 int DecodeTrace::get_row_at_point(const QPoint &point)
669 const int y = (point.y() - get_visual_y() + row_height_ / 2);
671 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
675 const int row = y / row_height_;
677 if (row >= (int)visible_rows_.size())
683 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
685 using namespace pv::data::decode;
690 const pair<uint64_t, uint64_t> sample_range =
691 get_sample_range(point.x(), point.x() + 1);
692 const int row = get_row_at_point(point);
696 vector<pv::data::decode::Annotation> annotations;
698 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
699 sample_range.first, sample_range.second);
701 return (annotations.empty()) ?
702 QString() : annotations[0].annotations().front();
705 void DecodeTrace::hover_point_changed()
709 const View *const view = owner_->view();
712 QPoint hp = view->hover_point();
713 QString ann = get_annotation_at_point(hp);
717 if (!row_height_ || ann.isEmpty()) {
718 QToolTip::hideText();
722 const int hover_row = get_row_at_point(hp);
724 QFontMetrics m(QToolTip::font());
725 const QRect text_size = m.boundingRect(QRect(), 0, ann);
727 // This is OS-specific and unfortunately we can't query it, so
728 // use an approximation to at least try to minimize the error.
729 const int padding = 8;
731 // Make sure the tool tip doesn't overlap with the mouse cursor.
732 // If it did, the tool tip would constantly hide and re-appear.
733 // We also push it up by one row so that it appears above the
734 // decode trace, not below.
735 hp.setX(hp.x() - (text_size.width() / 2) - padding);
737 hp.setY(get_visual_y() - (row_height_ / 2) +
738 (hover_row * row_height_) -
739 row_height_ - text_size.height() - padding);
741 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
744 void DecodeTrace::create_decoder_form(int index,
745 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
748 GlobalSettings settings;
751 const srd_decoder *const decoder = dec->decoder();
754 const bool decoder_deletable = index > 0;
756 pv::widgets::DecoderGroupBox *const group =
757 new pv::widgets::DecoderGroupBox(
758 QString::fromUtf8(decoder->name),
759 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
760 QString::fromUtf8(decoder->desc)),
761 nullptr, decoder_deletable);
762 group->set_decoder_visible(dec->shown());
764 if (decoder_deletable) {
765 delete_mapper_.setMapping(group, index);
766 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
769 show_hide_mapper_.setMapping(group, index);
770 connect(group, SIGNAL(show_hide_decoder()),
771 &show_hide_mapper_, SLOT(map()));
773 QFormLayout *const decoder_form = new QFormLayout;
774 group->add_layout(decoder_form);
776 const vector<DecodeChannel> channels = decode_signal_->get_channels();
779 for (DecodeChannel ch : channels) {
780 // Ignore channels not part of the decoder we create the form for
781 if (ch.decoder_ != dec)
784 QComboBox *const combo = create_channel_selector(parent, &ch);
785 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
787 channel_id_map_[combo] = ch.id;
788 init_state_map_[combo_init_state] = ch.id;
790 connect(combo, SIGNAL(currentIndexChanged(int)),
791 this, SLOT(on_channel_selected(int)));
792 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
793 this, SLOT(on_init_state_changed(int)));
795 QHBoxLayout *const hlayout = new QHBoxLayout;
796 hlayout->addWidget(combo);
797 hlayout->addWidget(combo_init_state);
799 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
800 combo_init_state->hide();
802 const QString required_flag = ch.is_optional ? QString() : QString("*");
803 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
804 .arg(ch.name, ch.desc, required_flag), hlayout);
808 shared_ptr<binding::Decoder> binding(
809 new binding::Decoder(decode_signal_, dec));
810 binding->add_properties_to_form(decoder_form, true);
812 bindings_.push_back(binding);
815 decoder_forms_.push_back(group);
818 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
820 const auto sigs(session_.signalbases());
822 // Sort signals in natural order
823 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
824 sort(sig_list.begin(), sig_list.end(),
825 [](const shared_ptr<data::SignalBase> &a,
826 const shared_ptr<data::SignalBase> &b) {
827 return strnatcasecmp(a->name().toStdString(),
828 b->name().toStdString()) < 0; });
830 QComboBox *selector = new QComboBox(parent);
832 selector->addItem("-", qVariantFromValue((void*)nullptr));
834 if (!ch->assigned_signal)
835 selector->setCurrentIndex(0);
837 for (const shared_ptr<data::SignalBase> &b : sig_list) {
839 if (b->logic_data() && b->enabled()) {
840 selector->addItem(b->name(),
841 qVariantFromValue((void*)b.get()));
843 if (ch->assigned_signal == b.get())
844 selector->setCurrentIndex(selector->count() - 1);
851 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
852 const DecodeChannel *ch)
854 QComboBox *selector = new QComboBox(parent);
856 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
857 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
858 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
860 selector->setCurrentIndex(ch->initial_pin_state);
862 selector->setToolTip("Initial (assumed) pin value before the first sample");
867 void DecodeTrace::on_new_annotations()
870 owner_->row_item_appearance_changed(false, true);
873 void DecodeTrace::delete_pressed()
878 void DecodeTrace::on_delete()
880 session_.remove_decode_signal(decode_signal_);
883 void DecodeTrace::on_channel_selected(int)
885 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
887 // Determine signal that was selected
888 const data::SignalBase *signal =
889 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
891 // Determine decode channel ID this combo box is the channel selector for
892 const uint16_t id = channel_id_map_.at(cb);
894 decode_signal_->assign_signal(id, signal);
897 void DecodeTrace::on_channels_updated()
900 owner_->row_item_appearance_changed(false, true);
903 void DecodeTrace::on_init_state_changed(int)
905 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
907 // Determine inital pin state that was selected
908 int init_state = cb->itemData(cb->currentIndex()).value<int>();
910 // Determine decode channel ID this combo box is the channel selector for
911 const uint16_t id = init_state_map_.at(cb);
913 decode_signal_->set_initial_pin_state(id, init_state);
916 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
918 decode_signal_->stack_decoder(decoder);
923 void DecodeTrace::on_delete_decoder(int index)
925 decode_signal_->remove_decoder(index);
931 void DecodeTrace::on_show_hide_decoder(int index)
933 const bool state = decode_signal_->toggle_decoder_visibility(index);
935 assert(index < (int)decoder_forms_.size());
936 decoder_forms_[index]->set_decoder_visible(state);
939 owner_->row_item_appearance_changed(false, true);