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 int DecodeTrace::MaxTraceUpdateRate = 1; // No more than 1 Hz
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(decode_finished()),
153 this, SLOT(on_decode_finished()));
154 connect(decode_signal_.get(), SIGNAL(channels_updated()),
155 this, SLOT(on_channels_updated()));
157 connect(&delete_mapper_, SIGNAL(mapped(int)),
158 this, SLOT(on_delete_decoder(int)));
159 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
160 this, SLOT(on_show_hide_decoder(int)));
162 connect(&delayed_trace_updater_, SIGNAL(timeout()),
163 this, SLOT(on_delayed_trace_update()));
164 delayed_trace_updater_.setSingleShot(true);
165 delayed_trace_updater_.setInterval(1000 / MaxTraceUpdateRate);
168 bool DecodeTrace::enabled() const
173 shared_ptr<data::SignalBase> DecodeTrace::base() const
178 pair<int, int> DecodeTrace::v_extents() const
180 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
182 // Make an empty decode trace appear symmetrical
183 const int row_count = max(1, max_visible_rows_);
185 return make_pair(-row_height, row_height * row_count);
188 void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
190 Trace::paint_back(p, pp);
191 paint_axis(p, pp, get_visual_y());
194 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
196 const int text_height = ViewItemPaintParams::text_height();
197 row_height_ = (text_height * 6) / 4;
198 const int annotation_height = (text_height * 5) / 4;
200 const QString err = decode_signal_->error_message();
201 if (!err.isEmpty()) {
202 draw_unresolved_period(
203 p, annotation_height, pp.left(), pp.right());
204 draw_error(p, err, pp);
208 // Set default pen to allow for text width calculation
211 // Iterate through the rows
212 int y = get_visual_y();
213 pair<uint64_t, uint64_t> sample_range = get_sample_range(
214 pp.left(), pp.right());
216 const vector<Row> rows = decode_signal_->visible_rows();
218 visible_rows_.clear();
219 for (const Row& row : rows) {
220 // Cache the row title widths
223 row_title_width = row_title_widths_.at(row);
224 } catch (out_of_range) {
225 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
227 row_title_widths_[row] = w;
231 // Determine the row's color
232 size_t base_colour = 0x13579BDF;
233 boost::hash_combine(base_colour, this);
234 boost::hash_combine(base_colour, row.decoder());
235 boost::hash_combine(base_colour, row.row());
238 vector<Annotation> annotations;
239 decode_signal_->get_annotation_subset(annotations, row,
240 sample_range.first, sample_range.second);
241 if (!annotations.empty()) {
242 draw_annotations(annotations, p, annotation_height, pp, y,
243 base_colour, row_title_width);
247 visible_rows_.push_back(row);
252 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
254 if ((int)visible_rows_.size() > max_visible_rows_)
255 owner_->extents_changed(false, true);
257 // Update the maximum row count if needed
258 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
261 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
265 for (size_t i = 0; i < visible_rows_.size(); i++) {
266 const int y = i * row_height_ + get_visual_y();
268 p.setPen(QPen(Qt::NoPen));
269 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
272 const QPointF points[] = {
273 QPointF(pp.left(), y - ArrowSize),
274 QPointF(pp.left() + ArrowSize, y),
275 QPointF(pp.left(), y + ArrowSize)
277 p.drawPolygon(points, countof(points));
280 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
281 pp.right() - pp.left(), row_height_);
282 const QString h(visible_rows_[i].title());
283 const int f = Qt::AlignLeft | Qt::AlignVCenter |
287 p.setPen(QApplication::palette().color(QPalette::Base));
288 for (int dx = -1; dx <= 1; dx++)
289 for (int dy = -1; dy <= 1; dy++)
290 if (dx != 0 && dy != 0)
291 p.drawText(r.translated(dx, dy), f, h);
294 p.setPen(QApplication::palette().color(QPalette::WindowText));
299 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
301 using pv::data::decode::Decoder;
305 // Add the standard options
306 Trace::populate_popup_form(parent, form);
308 // Add the decoder options
310 channel_id_map_.clear();
311 init_state_map_.clear();
312 decoder_forms_.clear();
314 const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
317 QLabel *const l = new QLabel(
318 tr("<p><i>No decoders in the stack</i></p>"));
319 l->setAlignment(Qt::AlignCenter);
322 auto iter = stack.cbegin();
323 for (int i = 0; i < (int)stack.size(); i++, iter++) {
324 shared_ptr<Decoder> dec(*iter);
325 create_decoder_form(i, dec, parent, form);
328 form->addRow(new QLabel(
329 tr("<i>* Required channels</i>"), parent));
332 // Add stacking button
333 pv::widgets::DecoderMenu *const decoder_menu =
334 new pv::widgets::DecoderMenu(parent);
335 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
336 this, SLOT(on_stack_decoder(srd_decoder*)));
338 QPushButton *const stack_button =
339 new QPushButton(tr("Stack Decoder"), parent);
340 stack_button->setMenu(decoder_menu);
341 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
343 QHBoxLayout *stack_button_box = new QHBoxLayout;
344 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
345 form->addRow(stack_button_box);
348 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
350 QMenu *const menu = Trace::create_context_menu(parent);
352 menu->addSeparator();
354 QAction *const del = new QAction(tr("Delete"), this);
355 del->setShortcuts(QKeySequence::Delete);
356 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
357 menu->addAction(del);
362 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
363 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
364 size_t base_colour, int row_title_width)
366 using namespace pv::data::decode;
368 vector<Annotation> a_block;
371 double samples_per_pixel, pixels_offset;
372 tie(pixels_offset, samples_per_pixel) =
373 get_pixels_offset_samples_per_pixel();
375 // Sort the annotations by start sample so that decoders
376 // can't confuse us by creating annotations out of order
377 stable_sort(annotations.begin(), annotations.end(),
378 [](const Annotation &a, const Annotation &b) {
379 return a.start_sample() < b.start_sample(); });
381 // Gather all annotations that form a visual "block" and draw them as such
382 for (const Annotation &a : annotations) {
384 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
385 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
386 const int a_width = a_end - a_start;
388 const int delta = a_end - p_end;
390 bool a_is_separate = false;
392 // Annotation wider than the threshold for a useful label width?
393 if (a_width >= min_useful_label_width_) {
394 for (const QString &ann_text : a.annotations()) {
395 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
396 // Annotation wide enough to fit a label? Don't put it in a block then
398 a_is_separate = true;
404 // Were the previous and this annotation more than a pixel apart?
405 if ((abs(delta) > 1) || a_is_separate) {
406 // Block was broken, draw annotations that form the current block
407 if (a_block.size() == 1) {
408 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
412 draw_annotation_block(a_block, p, h, y, base_colour);
418 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
419 // Next annotation must start a new block. delta will be > 1
420 // because we set p_end to INT_MIN but that's okay since
421 // a_block will be empty, so nothing will be drawn
424 a_block.push_back(a);
429 if (a_block.size() == 1)
430 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
433 draw_annotation_block(a_block, p, h, y, base_colour);
436 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
437 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
438 size_t base_colour, int row_title_width) const
440 double samples_per_pixel, pixels_offset;
441 tie(pixels_offset, samples_per_pixel) =
442 get_pixels_offset_samples_per_pixel();
444 const double start = a.start_sample() / samples_per_pixel -
446 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
448 const size_t colour = (base_colour + a.format()) % countof(Colours);
449 p.setPen(OutlineColours[colour]);
450 p.setBrush(Colours[colour]);
452 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
455 if (a.start_sample() == a.end_sample())
456 draw_instant(a, p, h, start, y);
458 draw_range(a, p, h, start, end, y, pp, row_title_width);
461 void DecodeTrace::draw_annotation_block(
462 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
463 int y, size_t base_colour) const
465 using namespace pv::data::decode;
467 if (annotations.empty())
470 double samples_per_pixel, pixels_offset;
471 tie(pixels_offset, samples_per_pixel) =
472 get_pixels_offset_samples_per_pixel();
474 const double start = annotations.front().start_sample() /
475 samples_per_pixel - pixels_offset;
476 const double end = annotations.back().end_sample() /
477 samples_per_pixel - pixels_offset;
479 const double top = y + .5 - h / 2;
480 const double bottom = y + .5 + h / 2;
482 const size_t colour = (base_colour + annotations.front().format()) %
485 // Check if all annotations are of the same type (i.e. we can use one color)
486 // or if we should use a neutral color (i.e. gray)
487 const int format = annotations.front().format();
488 const bool single_format = all_of(
489 annotations.begin(), annotations.end(),
490 [&](const Annotation &a) { return a.format() == format; });
492 const QRectF rect(start, top, end - start, bottom - top);
495 p.setPen(QPen(Qt::NoPen));
496 p.setBrush(Qt::white);
497 p.drawRoundedRect(rect, r, r);
499 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
500 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
502 p.drawRoundedRect(rect, r, r);
505 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
506 int h, double x, int y) const
508 const QString text = a.annotations().empty() ?
509 QString() : a.annotations().back();
510 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
512 const QRectF rect(x - w / 2, y - h / 2, w, h);
514 p.drawRoundedRect(rect, h / 2, h / 2);
517 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
520 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
521 int h, double start, double end, int y, const ViewItemPaintParams &pp,
522 int row_title_width) const
524 const double top = y + .5 - h / 2;
525 const double bottom = y + .5 + h / 2;
526 const vector<QString> annotations = a.annotations();
528 // If the two ends are within 1 pixel, draw a vertical line
529 if (start + 1.0 > end) {
530 p.drawLine(QPointF(start, top), QPointF(start, bottom));
534 const double cap_width = min((end - start) / 4, EndCapWidth);
537 QPointF(start, y + .5f),
538 QPointF(start + cap_width, top),
539 QPointF(end - cap_width, top),
540 QPointF(end, y + .5f),
541 QPointF(end - cap_width, bottom),
542 QPointF(start + cap_width, bottom)
545 p.drawConvexPolygon(pts, countof(pts));
547 if (annotations.empty())
550 const int ann_start = start + cap_width;
551 const int ann_end = end - cap_width;
553 const int real_start = max(ann_start, pp.left() + row_title_width);
554 const int real_end = min(ann_end, pp.right());
555 const int real_width = real_end - real_start;
557 QRectF rect(real_start, y - h / 2, real_width, h);
558 if (rect.width() <= 4)
563 // Try to find an annotation that will fit
564 QString best_annotation;
567 for (const QString &a : annotations) {
568 const int w = p.boundingRect(QRectF(), 0, a).width();
569 if (w <= rect.width() && w > best_width)
570 best_annotation = a, best_width = w;
573 if (best_annotation.isEmpty())
574 best_annotation = annotations.back();
576 // If not ellide the last in the list
577 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
578 best_annotation, Qt::ElideRight, rect.width()));
581 void DecodeTrace::draw_error(QPainter &p, const QString &message,
582 const ViewItemPaintParams &pp)
584 const int y = get_visual_y();
586 p.setPen(ErrorBgColour.darker());
587 p.setBrush(ErrorBgColour);
589 const QRectF bounding_rect =
590 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
591 const QRectF text_rect = p.boundingRect(bounding_rect,
592 Qt::AlignCenter, message);
593 const float r = text_rect.height() / 4;
595 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
599 p.drawText(text_rect, message);
602 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
604 using namespace pv::data;
605 using pv::data::decode::Decoder;
607 double samples_per_pixel, pixels_offset;
609 const int64_t sample_count = decode_signal_->get_working_sample_count();
610 if (sample_count == 0)
613 const int64_t samples_decoded = decode_signal_->get_decoded_sample_count();
614 if (sample_count == samples_decoded)
617 const int y = get_visual_y();
619 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
621 const double start = max(samples_decoded /
622 samples_per_pixel - pixels_offset, left - 1.0);
623 const double end = min(sample_count / samples_per_pixel -
624 pixels_offset, right + 1.0);
625 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
627 p.setPen(QPen(Qt::NoPen));
628 p.setBrush(Qt::white);
629 p.drawRect(no_decode_rect);
631 p.setPen(NoDecodeColour);
632 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
633 p.drawRect(no_decode_rect);
636 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
640 const View *view = owner_->view();
643 const double scale = view->scale();
646 const double pixels_offset =
647 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
649 double samplerate = decode_signal_->samplerate();
651 // Show sample rate as 1Hz when it is unknown
652 if (samplerate == 0.0)
655 return make_pair(pixels_offset, samplerate * scale);
658 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
659 int x_start, int x_end) const
661 double samples_per_pixel, pixels_offset;
662 tie(pixels_offset, samples_per_pixel) =
663 get_pixels_offset_samples_per_pixel();
665 const uint64_t start = (uint64_t)max(
666 (x_start + pixels_offset) * samples_per_pixel, 0.0);
667 const uint64_t end = (uint64_t)max(
668 (x_end + pixels_offset) * samples_per_pixel, 0.0);
670 return make_pair(start, end);
673 int DecodeTrace::get_row_at_point(const QPoint &point)
678 const int y = (point.y() - get_visual_y() + row_height_ / 2);
680 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
684 const int row = y / row_height_;
686 if (row >= (int)visible_rows_.size())
692 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
694 using namespace pv::data::decode;
699 const pair<uint64_t, uint64_t> sample_range =
700 get_sample_range(point.x(), point.x() + 1);
701 const int row = get_row_at_point(point);
705 vector<pv::data::decode::Annotation> annotations;
707 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
708 sample_range.first, sample_range.second);
710 return (annotations.empty()) ?
711 QString() : annotations[0].annotations().front();
714 void DecodeTrace::hover_point_changed()
718 const View *const view = owner_->view();
721 QPoint hp = view->hover_point();
722 QString ann = get_annotation_at_point(hp);
726 if (!row_height_ || ann.isEmpty()) {
727 QToolTip::hideText();
731 const int hover_row = get_row_at_point(hp);
733 QFontMetrics m(QToolTip::font());
734 const QRect text_size = m.boundingRect(QRect(), 0, ann);
736 // This is OS-specific and unfortunately we can't query it, so
737 // use an approximation to at least try to minimize the error.
738 const int padding = 8;
740 // Make sure the tool tip doesn't overlap with the mouse cursor.
741 // If it did, the tool tip would constantly hide and re-appear.
742 // We also push it up by one row so that it appears above the
743 // decode trace, not below.
744 hp.setX(hp.x() - (text_size.width() / 2) - padding);
746 hp.setY(get_visual_y() - (row_height_ / 2) +
747 (hover_row * row_height_) -
748 row_height_ - text_size.height() - padding);
750 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
753 void DecodeTrace::create_decoder_form(int index,
754 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
757 GlobalSettings settings;
760 const srd_decoder *const decoder = dec->decoder();
763 const bool decoder_deletable = index > 0;
765 pv::widgets::DecoderGroupBox *const group =
766 new pv::widgets::DecoderGroupBox(
767 QString::fromUtf8(decoder->name),
768 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
769 QString::fromUtf8(decoder->desc)),
770 nullptr, decoder_deletable);
771 group->set_decoder_visible(dec->shown());
773 if (decoder_deletable) {
774 delete_mapper_.setMapping(group, index);
775 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
778 show_hide_mapper_.setMapping(group, index);
779 connect(group, SIGNAL(show_hide_decoder()),
780 &show_hide_mapper_, SLOT(map()));
782 QFormLayout *const decoder_form = new QFormLayout;
783 group->add_layout(decoder_form);
785 const vector<DecodeChannel> channels = decode_signal_->get_channels();
788 for (DecodeChannel ch : channels) {
789 // Ignore channels not part of the decoder we create the form for
790 if (ch.decoder_ != dec)
793 QComboBox *const combo = create_channel_selector(parent, &ch);
794 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
796 channel_id_map_[combo] = ch.id;
797 init_state_map_[combo_init_state] = ch.id;
799 connect(combo, SIGNAL(currentIndexChanged(int)),
800 this, SLOT(on_channel_selected(int)));
801 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
802 this, SLOT(on_init_state_changed(int)));
804 QHBoxLayout *const hlayout = new QHBoxLayout;
805 hlayout->addWidget(combo);
806 hlayout->addWidget(combo_init_state);
808 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
809 combo_init_state->hide();
811 const QString required_flag = ch.is_optional ? QString() : QString("*");
812 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
813 .arg(ch.name, ch.desc, required_flag), hlayout);
817 shared_ptr<binding::Decoder> binding(
818 new binding::Decoder(decode_signal_, dec));
819 binding->add_properties_to_form(decoder_form, true);
821 bindings_.push_back(binding);
824 decoder_forms_.push_back(group);
827 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
829 const auto sigs(session_.signalbases());
831 // Sort signals in natural order
832 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
833 sort(sig_list.begin(), sig_list.end(),
834 [](const shared_ptr<data::SignalBase> &a,
835 const shared_ptr<data::SignalBase> &b) {
836 return strnatcasecmp(a->name().toStdString(),
837 b->name().toStdString()) < 0; });
839 QComboBox *selector = new QComboBox(parent);
841 selector->addItem("-", qVariantFromValue((void*)nullptr));
843 if (!ch->assigned_signal)
844 selector->setCurrentIndex(0);
846 for (const shared_ptr<data::SignalBase> &b : sig_list) {
848 if (b->logic_data() && b->enabled()) {
849 selector->addItem(b->name(),
850 qVariantFromValue((void*)b.get()));
852 if (ch->assigned_signal == b.get())
853 selector->setCurrentIndex(selector->count() - 1);
860 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
861 const DecodeChannel *ch)
863 QComboBox *selector = new QComboBox(parent);
865 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
866 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
867 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
869 selector->setCurrentIndex(ch->initial_pin_state);
871 selector->setToolTip("Initial (assumed) pin value before the first sample");
876 void DecodeTrace::on_new_annotations()
878 if (!delayed_trace_updater_.isActive())
879 delayed_trace_updater_.start();
882 void DecodeTrace::on_delayed_trace_update()
885 owner_->row_item_appearance_changed(false, true);
888 void DecodeTrace::on_decode_finished()
891 owner_->row_item_appearance_changed(false, true);
894 void DecodeTrace::delete_pressed()
899 void DecodeTrace::on_delete()
901 session_.remove_decode_signal(decode_signal_);
904 void DecodeTrace::on_channel_selected(int)
906 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
908 // Determine signal that was selected
909 const data::SignalBase *signal =
910 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
912 // Determine decode channel ID this combo box is the channel selector for
913 const uint16_t id = channel_id_map_.at(cb);
915 decode_signal_->assign_signal(id, signal);
918 void DecodeTrace::on_channels_updated()
921 owner_->row_item_appearance_changed(false, true);
924 void DecodeTrace::on_init_state_changed(int)
926 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
928 // Determine inital pin state that was selected
929 int init_state = cb->itemData(cb->currentIndex()).value<int>();
931 // Determine decode channel ID this combo box is the channel selector for
932 const uint16_t id = init_state_map_.at(cb);
934 decode_signal_->set_initial_pin_state(id, init_state);
937 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
939 decode_signal_->stack_decoder(decoder);
944 void DecodeTrace::on_delete_decoder(int index)
946 decode_signal_->remove_decoder(index);
952 void DecodeTrace::on_show_hide_decoder(int index)
954 const bool state = decode_signal_->toggle_decoder_visibility(index);
956 assert(index < (int)decoder_forms_.size());
957 decoder_forms_[index]->set_decoder_visible(state);
960 owner_->row_item_appearance_changed(false, true);