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>
60 using std::out_of_range;
62 using std::shared_ptr;
66 using pv::data::decode::Annotation;
67 using pv::data::decode::Row;
68 using pv::data::DecodeChannel;
69 using pv::data::DecodeSignal;
75 const QColor DecodeTrace::DecodeColours[4] = {
76 QColor(0xEF, 0x29, 0x29), // Red
77 QColor(0xFC, 0xE9, 0x4F), // Yellow
78 QColor(0x8A, 0xE2, 0x34), // Green
79 QColor(0x72, 0x9F, 0xCF) // Blue
82 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
83 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
85 const int DecodeTrace::ArrowSize = 4;
86 const double DecodeTrace::EndCapWidth = 5;
87 const int DecodeTrace::RowTitleMargin = 10;
88 const int DecodeTrace::DrawPadding = 100;
90 const int DecodeTrace::MaxTraceUpdateRate = 1; // No more than 1 Hz
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, int index) :
135 max_visible_rows_(0),
136 delete_mapper_(this),
137 show_hide_mapper_(this)
139 decode_signal_ = dynamic_pointer_cast<data::DecodeSignal>(base_);
141 // Determine shortest string we want to see displayed in full
142 QFontMetrics m(QApplication::font());
143 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
145 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
147 connect(decode_signal_.get(), SIGNAL(new_annotations()),
148 this, SLOT(on_new_annotations()));
149 connect(decode_signal_.get(), SIGNAL(decode_finished()),
150 this, SLOT(on_decode_finished()));
151 connect(decode_signal_.get(), SIGNAL(channels_updated()),
152 this, SLOT(on_channels_updated()));
154 connect(&delete_mapper_, SIGNAL(mapped(int)),
155 this, SLOT(on_delete_decoder(int)));
156 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
157 this, SLOT(on_show_hide_decoder(int)));
159 connect(&delayed_trace_updater_, SIGNAL(timeout()),
160 this, SLOT(on_delayed_trace_update()));
161 delayed_trace_updater_.setSingleShot(true);
162 delayed_trace_updater_.setInterval(1000 / MaxTraceUpdateRate);
165 bool DecodeTrace::enabled() const
170 shared_ptr<data::SignalBase> DecodeTrace::base() const
175 pair<int, int> DecodeTrace::v_extents() const
177 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
179 // Make an empty decode trace appear symmetrical
180 const int row_count = max(1, max_visible_rows_);
182 return make_pair(-row_height, row_height * row_count);
185 void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
187 Trace::paint_back(p, pp);
188 paint_axis(p, pp, get_visual_y());
191 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
193 const int text_height = ViewItemPaintParams::text_height();
194 row_height_ = (text_height * 6) / 4;
195 const int annotation_height = (text_height * 5) / 4;
197 const QString err = decode_signal_->error_message();
198 if (!err.isEmpty()) {
199 draw_unresolved_period(
200 p, annotation_height, pp.left(), pp.right());
201 draw_error(p, err, pp);
205 // Set default pen to allow for text width calculation
208 // Iterate through the rows
209 int y = get_visual_y();
210 pair<uint64_t, uint64_t> sample_range = get_sample_range(
211 pp.left(), pp.right());
213 const vector<Row> rows = decode_signal_->visible_rows();
215 visible_rows_.clear();
216 for (const Row& row : rows) {
217 // Cache the row title widths
220 row_title_width = row_title_widths_.at(row);
221 } catch (out_of_range) {
222 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
224 row_title_widths_[row] = w;
228 // Determine the row's color
229 size_t base_colour = 0x13579BDF;
230 boost::hash_combine(base_colour, this);
231 boost::hash_combine(base_colour, row.decoder());
232 boost::hash_combine(base_colour, row.row());
235 vector<Annotation> annotations;
236 decode_signal_->get_annotation_subset(annotations, row,
237 sample_range.first, sample_range.second);
238 if (!annotations.empty()) {
239 draw_annotations(annotations, p, annotation_height, pp, y,
240 base_colour, row_title_width);
244 visible_rows_.push_back(row);
249 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
251 if ((int)visible_rows_.size() > max_visible_rows_)
252 owner_->extents_changed(false, true);
254 // Update the maximum row count if needed
255 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
258 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
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 // Add the standard options
303 Trace::populate_popup_form(parent, form);
305 // Add the decoder options
307 channel_id_map_.clear();
308 init_state_map_.clear();
309 decoder_forms_.clear();
311 const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
314 QLabel *const l = new QLabel(
315 tr("<p><i>No decoders in the stack</i></p>"));
316 l->setAlignment(Qt::AlignCenter);
319 auto iter = stack.cbegin();
320 for (int i = 0; i < (int)stack.size(); i++, iter++) {
321 shared_ptr<Decoder> dec(*iter);
322 create_decoder_form(i, dec, parent, form);
325 form->addRow(new QLabel(
326 tr("<i>* Required channels</i>"), parent));
329 // Add stacking button
330 pv::widgets::DecoderMenu *const decoder_menu =
331 new pv::widgets::DecoderMenu(parent);
332 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
333 this, SLOT(on_stack_decoder(srd_decoder*)));
335 QPushButton *const stack_button =
336 new QPushButton(tr("Stack Decoder"), parent);
337 stack_button->setMenu(decoder_menu);
338 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
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 - pixels_offset;
445 const size_t colour = (base_colour + a.format()) % countof(Colours);
446 p.setPen(OutlineColours[colour]);
447 p.setBrush(Colours[colour]);
449 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
452 if (a.start_sample() == a.end_sample())
453 draw_instant(a, p, h, start, y);
455 draw_range(a, p, h, start, end, y, pp, row_title_width);
458 void DecodeTrace::draw_annotation_block(
459 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
460 int y, size_t base_colour) const
462 using namespace pv::data::decode;
464 if (annotations.empty())
467 double samples_per_pixel, pixels_offset;
468 tie(pixels_offset, samples_per_pixel) =
469 get_pixels_offset_samples_per_pixel();
471 const double start = annotations.front().start_sample() /
472 samples_per_pixel - pixels_offset;
473 const double end = annotations.back().end_sample() /
474 samples_per_pixel - pixels_offset;
476 const double top = y + .5 - h / 2;
477 const double bottom = y + .5 + h / 2;
479 const size_t colour = (base_colour + annotations.front().format()) %
482 // Check if all annotations are of the same type (i.e. we can use one color)
483 // or if we should use a neutral color (i.e. gray)
484 const int format = annotations.front().format();
485 const bool single_format = all_of(
486 annotations.begin(), annotations.end(),
487 [&](const Annotation &a) { return a.format() == format; });
489 const QRectF rect(start, top, end - start, bottom - top);
492 p.setPen(QPen(Qt::NoPen));
493 p.setBrush(Qt::white);
494 p.drawRoundedRect(rect, r, r);
496 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
497 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
499 p.drawRoundedRect(rect, r, r);
502 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
503 int h, double x, int y) const
505 const QString text = a.annotations().empty() ?
506 QString() : a.annotations().back();
507 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
509 const QRectF rect(x - w / 2, y - h / 2, w, h);
511 p.drawRoundedRect(rect, h / 2, h / 2);
514 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
517 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
518 int h, double start, double end, int y, const ViewItemPaintParams &pp,
519 int row_title_width) const
521 const double top = y + .5 - h / 2;
522 const double bottom = y + .5 + h / 2;
523 const vector<QString> annotations = a.annotations();
525 // If the two ends are within 1 pixel, draw a vertical line
526 if (start + 1.0 > end) {
527 p.drawLine(QPointF(start, top), QPointF(start, bottom));
531 const double cap_width = min((end - start) / 4, EndCapWidth);
534 QPointF(start, y + .5f),
535 QPointF(start + cap_width, top),
536 QPointF(end - cap_width, top),
537 QPointF(end, y + .5f),
538 QPointF(end - cap_width, bottom),
539 QPointF(start + cap_width, bottom)
542 p.drawConvexPolygon(pts, countof(pts));
544 if (annotations.empty())
547 const int ann_start = start + cap_width;
548 const int ann_end = end - cap_width;
550 const int real_start = max(ann_start, pp.left() + row_title_width);
551 const int real_end = min(ann_end, pp.right());
552 const int real_width = real_end - real_start;
554 QRectF rect(real_start, y - h / 2, real_width, h);
555 if (rect.width() <= 4)
560 // Try to find an annotation that will fit
561 QString best_annotation;
564 for (const QString &a : annotations) {
565 const int w = p.boundingRect(QRectF(), 0, a).width();
566 if (w <= rect.width() && w > best_width)
567 best_annotation = a, best_width = w;
570 if (best_annotation.isEmpty())
571 best_annotation = annotations.back();
573 // If not ellide the last in the list
574 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
575 best_annotation, Qt::ElideRight, rect.width()));
578 void DecodeTrace::draw_error(QPainter &p, const QString &message,
579 const ViewItemPaintParams &pp)
581 const int y = get_visual_y();
583 p.setPen(ErrorBgColour.darker());
584 p.setBrush(ErrorBgColour);
586 const QRectF bounding_rect =
587 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
588 const QRectF text_rect = p.boundingRect(bounding_rect,
589 Qt::AlignCenter, message);
590 const float r = text_rect.height() / 4;
592 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
596 p.drawText(text_rect, message);
599 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
601 using namespace pv::data;
602 using pv::data::decode::Decoder;
604 double samples_per_pixel, pixels_offset;
606 const int64_t sample_count = decode_signal_->get_working_sample_count();
607 if (sample_count == 0)
610 const int64_t samples_decoded = decode_signal_->get_decoded_sample_count();
611 if (sample_count == samples_decoded)
614 const int y = get_visual_y();
616 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
618 const double start = max(samples_decoded /
619 samples_per_pixel - pixels_offset, left - 1.0);
620 const double end = min(sample_count / samples_per_pixel -
621 pixels_offset, right + 1.0);
622 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
624 p.setPen(QPen(Qt::NoPen));
625 p.setBrush(Qt::white);
626 p.drawRect(no_decode_rect);
628 p.setPen(NoDecodeColour);
629 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
630 p.drawRect(no_decode_rect);
633 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
637 const View *view = owner_->view();
640 const double scale = view->scale();
643 const double pixels_offset =
644 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
646 double samplerate = decode_signal_->samplerate();
648 // Show sample rate as 1Hz when it is unknown
649 if (samplerate == 0.0)
652 return make_pair(pixels_offset, samplerate * scale);
655 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
656 int x_start, int x_end) const
658 double samples_per_pixel, pixels_offset;
659 tie(pixels_offset, samples_per_pixel) =
660 get_pixels_offset_samples_per_pixel();
662 const uint64_t start = (uint64_t)max(
663 (x_start + pixels_offset) * samples_per_pixel, 0.0);
664 const uint64_t end = (uint64_t)max(
665 (x_end + pixels_offset) * samples_per_pixel, 0.0);
667 return make_pair(start, end);
670 int DecodeTrace::get_row_at_point(const QPoint &point)
675 const int y = (point.y() - get_visual_y() + row_height_ / 2);
677 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
681 const int row = y / row_height_;
683 if (row >= (int)visible_rows_.size())
689 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
691 using namespace pv::data::decode;
696 const pair<uint64_t, uint64_t> sample_range =
697 get_sample_range(point.x(), point.x() + 1);
698 const int row = get_row_at_point(point);
702 vector<pv::data::decode::Annotation> annotations;
704 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
705 sample_range.first, sample_range.second);
707 return (annotations.empty()) ?
708 QString() : annotations[0].annotations().front();
711 void DecodeTrace::hover_point_changed()
715 const View *const view = owner_->view();
718 QPoint hp = view->hover_point();
719 QString ann = get_annotation_at_point(hp);
723 if (!row_height_ || ann.isEmpty()) {
724 QToolTip::hideText();
728 const int hover_row = get_row_at_point(hp);
730 QFontMetrics m(QToolTip::font());
731 const QRect text_size = m.boundingRect(QRect(), 0, ann);
733 // This is OS-specific and unfortunately we can't query it, so
734 // use an approximation to at least try to minimize the error.
735 const int padding = 8;
737 // Make sure the tool tip doesn't overlap with the mouse cursor.
738 // If it did, the tool tip would constantly hide and re-appear.
739 // We also push it up by one row so that it appears above the
740 // decode trace, not below.
741 hp.setX(hp.x() - (text_size.width() / 2) - padding);
743 hp.setY(get_visual_y() - (row_height_ / 2) +
744 (hover_row * row_height_) -
745 row_height_ - text_size.height() - padding);
747 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
750 void DecodeTrace::create_decoder_form(int index,
751 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
754 GlobalSettings settings;
757 const srd_decoder *const decoder = dec->decoder();
760 const bool decoder_deletable = index > 0;
762 pv::widgets::DecoderGroupBox *const group =
763 new pv::widgets::DecoderGroupBox(
764 QString::fromUtf8(decoder->name),
765 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
766 QString::fromUtf8(decoder->desc)),
767 nullptr, decoder_deletable);
768 group->set_decoder_visible(dec->shown());
770 if (decoder_deletable) {
771 delete_mapper_.setMapping(group, index);
772 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
775 show_hide_mapper_.setMapping(group, index);
776 connect(group, SIGNAL(show_hide_decoder()),
777 &show_hide_mapper_, SLOT(map()));
779 QFormLayout *const decoder_form = new QFormLayout;
780 group->add_layout(decoder_form);
782 const vector<DecodeChannel> channels = decode_signal_->get_channels();
785 for (DecodeChannel ch : channels) {
786 // Ignore channels not part of the decoder we create the form for
787 if (ch.decoder_ != dec)
790 QComboBox *const combo = create_channel_selector(parent, &ch);
791 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
793 channel_id_map_[combo] = ch.id;
794 init_state_map_[combo_init_state] = ch.id;
796 connect(combo, SIGNAL(currentIndexChanged(int)),
797 this, SLOT(on_channel_selected(int)));
798 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
799 this, SLOT(on_init_state_changed(int)));
801 QHBoxLayout *const hlayout = new QHBoxLayout;
802 hlayout->addWidget(combo);
803 hlayout->addWidget(combo_init_state);
805 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
806 combo_init_state->hide();
808 const QString required_flag = ch.is_optional ? QString() : QString("*");
809 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
810 .arg(ch.name, ch.desc, required_flag), hlayout);
814 shared_ptr<binding::Decoder> binding(
815 new binding::Decoder(decode_signal_, dec));
816 binding->add_properties_to_form(decoder_form, true);
818 bindings_.push_back(binding);
821 decoder_forms_.push_back(group);
824 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
826 const auto sigs(session_.signalbases());
828 // Sort signals in natural order
829 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
830 sort(sig_list.begin(), sig_list.end(),
831 [](const shared_ptr<data::SignalBase> &a,
832 const shared_ptr<data::SignalBase> &b) {
833 return strnatcasecmp(a->name().toStdString(),
834 b->name().toStdString()) < 0; });
836 QComboBox *selector = new QComboBox(parent);
838 selector->addItem("-", qVariantFromValue((void*)nullptr));
840 if (!ch->assigned_signal)
841 selector->setCurrentIndex(0);
843 for (const shared_ptr<data::SignalBase> &b : sig_list) {
845 if (b->logic_data() && b->enabled()) {
846 selector->addItem(b->name(),
847 qVariantFromValue((void*)b.get()));
849 if (ch->assigned_signal == b.get())
850 selector->setCurrentIndex(selector->count() - 1);
857 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
858 const DecodeChannel *ch)
860 QComboBox *selector = new QComboBox(parent);
862 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
863 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
864 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
866 selector->setCurrentIndex(ch->initial_pin_state);
868 selector->setToolTip("Initial (assumed) pin value before the first sample");
873 void DecodeTrace::on_new_annotations()
875 if (!delayed_trace_updater_.isActive())
876 delayed_trace_updater_.start();
879 void DecodeTrace::on_delayed_trace_update()
882 owner_->row_item_appearance_changed(false, true);
885 void DecodeTrace::on_decode_finished()
888 owner_->row_item_appearance_changed(false, true);
891 void DecodeTrace::delete_pressed()
896 void DecodeTrace::on_delete()
898 session_.remove_decode_signal(decode_signal_);
901 void DecodeTrace::on_channel_selected(int)
903 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
905 // Determine signal that was selected
906 const data::SignalBase *signal =
907 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
909 // Determine decode channel ID this combo box is the channel selector for
910 const uint16_t id = channel_id_map_.at(cb);
912 decode_signal_->assign_signal(id, signal);
915 void DecodeTrace::on_channels_updated()
918 owner_->row_item_appearance_changed(false, true);
921 void DecodeTrace::on_init_state_changed(int)
923 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
925 // Determine inital pin state that was selected
926 int init_state = cb->itemData(cb->currentIndex()).value<int>();
928 // Determine decode channel ID this combo box is the channel selector for
929 const uint16_t id = init_state_map_.at(cb);
931 decode_signal_->set_initial_pin_state(id, init_state);
934 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
936 decode_signal_->stack_decoder(decoder);
941 void DecodeTrace::on_delete_decoder(int index)
943 decode_signal_->remove_decoder(index);
949 void DecodeTrace::on_show_hide_decoder(int index)
951 const bool state = decode_signal_->toggle_decoder_visibility(index);
953 assert(index < (int)decoder_forms_.size());
954 decoder_forms_[index]->set_decoder_visible(state);
957 owner_->row_item_appearance_changed(false, true);