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 max_visible_rows_ = (int)visible_rows_.size();
254 // Call order is important, otherwise the lazy event handler won't work
255 owner_->extents_changed(false, true);
256 owner_->row_item_appearance_changed(false, true);
260 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
264 for (size_t i = 0; i < visible_rows_.size(); i++) {
265 const int y = i * row_height_ + get_visual_y();
267 p.setPen(QPen(Qt::NoPen));
268 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
271 const QPointF points[] = {
272 QPointF(pp.left(), y - ArrowSize),
273 QPointF(pp.left() + ArrowSize, y),
274 QPointF(pp.left(), y + ArrowSize)
276 p.drawPolygon(points, countof(points));
279 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
280 pp.right() - pp.left(), row_height_);
281 const QString h(visible_rows_[i].title());
282 const int f = Qt::AlignLeft | Qt::AlignVCenter |
286 p.setPen(QApplication::palette().color(QPalette::Base));
287 for (int dx = -1; dx <= 1; dx++)
288 for (int dy = -1; dy <= 1; dy++)
289 if (dx != 0 && dy != 0)
290 p.drawText(r.translated(dx, dy), f, h);
293 p.setPen(QApplication::palette().color(QPalette::WindowText));
298 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
300 using pv::data::decode::Decoder;
304 // Add the standard options
305 Trace::populate_popup_form(parent, form);
307 // Add the decoder options
309 channel_id_map_.clear();
310 init_state_map_.clear();
311 decoder_forms_.clear();
313 const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
316 QLabel *const l = new QLabel(
317 tr("<p><i>No decoders in the stack</i></p>"));
318 l->setAlignment(Qt::AlignCenter);
321 auto iter = stack.cbegin();
322 for (int i = 0; i < (int)stack.size(); i++, iter++) {
323 shared_ptr<Decoder> dec(*iter);
324 create_decoder_form(i, dec, parent, form);
327 form->addRow(new QLabel(
328 tr("<i>* Required channels</i>"), parent));
331 // Add stacking button
332 pv::widgets::DecoderMenu *const decoder_menu =
333 new pv::widgets::DecoderMenu(parent);
334 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
335 this, SLOT(on_stack_decoder(srd_decoder*)));
337 QPushButton *const stack_button =
338 new QPushButton(tr("Stack Decoder"), parent);
339 stack_button->setMenu(decoder_menu);
340 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
342 QHBoxLayout *stack_button_box = new QHBoxLayout;
343 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
344 form->addRow(stack_button_box);
347 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
349 QMenu *const menu = Trace::create_context_menu(parent);
351 menu->addSeparator();
353 QAction *const del = new QAction(tr("Delete"), this);
354 del->setShortcuts(QKeySequence::Delete);
355 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
356 menu->addAction(del);
361 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
362 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
363 size_t base_colour, int row_title_width)
365 using namespace pv::data::decode;
367 vector<Annotation> a_block;
370 double samples_per_pixel, pixels_offset;
371 tie(pixels_offset, samples_per_pixel) =
372 get_pixels_offset_samples_per_pixel();
374 // Sort the annotations by start sample so that decoders
375 // can't confuse us by creating annotations out of order
376 stable_sort(annotations.begin(), annotations.end(),
377 [](const Annotation &a, const Annotation &b) {
378 return a.start_sample() < b.start_sample(); });
380 // Gather all annotations that form a visual "block" and draw them as such
381 for (const Annotation &a : annotations) {
383 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
384 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
385 const int a_width = a_end - a_start;
387 const int delta = a_end - p_end;
389 bool a_is_separate = false;
391 // Annotation wider than the threshold for a useful label width?
392 if (a_width >= min_useful_label_width_) {
393 for (const QString &ann_text : a.annotations()) {
394 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
395 // Annotation wide enough to fit a label? Don't put it in a block then
397 a_is_separate = true;
403 // Were the previous and this annotation more than a pixel apart?
404 if ((abs(delta) > 1) || a_is_separate) {
405 // Block was broken, draw annotations that form the current block
406 if (a_block.size() == 1) {
407 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
411 draw_annotation_block(a_block, p, h, y, base_colour);
417 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
418 // Next annotation must start a new block. delta will be > 1
419 // because we set p_end to INT_MIN but that's okay since
420 // a_block will be empty, so nothing will be drawn
423 a_block.push_back(a);
428 if (a_block.size() == 1)
429 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
432 draw_annotation_block(a_block, p, h, y, base_colour);
435 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
436 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
437 size_t base_colour, int row_title_width) const
439 double samples_per_pixel, pixels_offset;
440 tie(pixels_offset, samples_per_pixel) =
441 get_pixels_offset_samples_per_pixel();
443 const double start = a.start_sample() / samples_per_pixel -
445 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
447 const size_t colour = (base_colour + a.format()) % countof(Colours);
448 p.setPen(OutlineColours[colour]);
449 p.setBrush(Colours[colour]);
451 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
454 if (a.start_sample() == a.end_sample())
455 draw_instant(a, p, h, start, y);
457 draw_range(a, p, h, start, end, y, pp, row_title_width);
460 void DecodeTrace::draw_annotation_block(
461 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
462 int y, size_t base_colour) const
464 using namespace pv::data::decode;
466 if (annotations.empty())
469 double samples_per_pixel, pixels_offset;
470 tie(pixels_offset, samples_per_pixel) =
471 get_pixels_offset_samples_per_pixel();
473 const double start = annotations.front().start_sample() /
474 samples_per_pixel - pixels_offset;
475 const double end = annotations.back().end_sample() /
476 samples_per_pixel - pixels_offset;
478 const double top = y + .5 - h / 2;
479 const double bottom = y + .5 + h / 2;
481 const size_t colour = (base_colour + annotations.front().format()) %
484 // Check if all annotations are of the same type (i.e. we can use one color)
485 // or if we should use a neutral color (i.e. gray)
486 const int format = annotations.front().format();
487 const bool single_format = all_of(
488 annotations.begin(), annotations.end(),
489 [&](const Annotation &a) { return a.format() == format; });
491 const QRectF rect(start, top, end - start, bottom - top);
494 p.setPen(QPen(Qt::NoPen));
495 p.setBrush(Qt::white);
496 p.drawRoundedRect(rect, r, r);
498 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
499 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
501 p.drawRoundedRect(rect, r, r);
504 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
505 int h, double x, int y) const
507 const QString text = a.annotations().empty() ?
508 QString() : a.annotations().back();
509 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
511 const QRectF rect(x - w / 2, y - h / 2, w, h);
513 p.drawRoundedRect(rect, h / 2, h / 2);
516 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
519 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
520 int h, double start, double end, int y, const ViewItemPaintParams &pp,
521 int row_title_width) const
523 const double top = y + .5 - h / 2;
524 const double bottom = y + .5 + h / 2;
525 const vector<QString> annotations = a.annotations();
527 // If the two ends are within 1 pixel, draw a vertical line
528 if (start + 1.0 > end) {
529 p.drawLine(QPointF(start, top), QPointF(start, bottom));
533 const double cap_width = min((end - start) / 4, EndCapWidth);
536 QPointF(start, y + .5f),
537 QPointF(start + cap_width, top),
538 QPointF(end - cap_width, top),
539 QPointF(end, y + .5f),
540 QPointF(end - cap_width, bottom),
541 QPointF(start + cap_width, bottom)
544 p.drawConvexPolygon(pts, countof(pts));
546 if (annotations.empty())
549 const int ann_start = start + cap_width;
550 const int ann_end = end - cap_width;
552 const int real_start = max(ann_start, pp.left() + row_title_width);
553 const int real_end = min(ann_end, pp.right());
554 const int real_width = real_end - real_start;
556 QRectF rect(real_start, y - h / 2, real_width, h);
557 if (rect.width() <= 4)
562 // Try to find an annotation that will fit
563 QString best_annotation;
566 for (const QString &a : annotations) {
567 const int w = p.boundingRect(QRectF(), 0, a).width();
568 if (w <= rect.width() && w > best_width)
569 best_annotation = a, best_width = w;
572 if (best_annotation.isEmpty())
573 best_annotation = annotations.back();
575 // If not ellide the last in the list
576 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
577 best_annotation, Qt::ElideRight, rect.width()));
580 void DecodeTrace::draw_error(QPainter &p, const QString &message,
581 const ViewItemPaintParams &pp)
583 const int y = get_visual_y();
585 p.setPen(ErrorBgColour.darker());
586 p.setBrush(ErrorBgColour);
588 const QRectF bounding_rect =
589 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
590 const QRectF text_rect = p.boundingRect(bounding_rect,
591 Qt::AlignCenter, message);
592 const float r = text_rect.height() / 4;
594 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
598 p.drawText(text_rect, message);
601 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
603 using namespace pv::data;
604 using pv::data::decode::Decoder;
606 double samples_per_pixel, pixels_offset;
608 const int64_t sample_count = decode_signal_->get_working_sample_count();
609 if (sample_count == 0)
612 const int64_t samples_decoded = decode_signal_->get_decoded_sample_count();
613 if (sample_count == samples_decoded)
616 const int y = get_visual_y();
618 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
620 const double start = max(samples_decoded /
621 samples_per_pixel - pixels_offset, left - 1.0);
622 const double end = min(sample_count / samples_per_pixel -
623 pixels_offset, right + 1.0);
624 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
626 p.setPen(QPen(Qt::NoPen));
627 p.setBrush(Qt::white);
628 p.drawRect(no_decode_rect);
630 p.setPen(NoDecodeColour);
631 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
632 p.drawRect(no_decode_rect);
635 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
639 const View *view = owner_->view();
642 const double scale = view->scale();
645 const double pixels_offset =
646 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
648 double samplerate = decode_signal_->samplerate();
650 // Show sample rate as 1Hz when it is unknown
651 if (samplerate == 0.0)
654 return make_pair(pixels_offset, samplerate * scale);
657 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
658 int x_start, int x_end) const
660 double samples_per_pixel, pixels_offset;
661 tie(pixels_offset, samples_per_pixel) =
662 get_pixels_offset_samples_per_pixel();
664 const uint64_t start = (uint64_t)max(
665 (x_start + pixels_offset) * samples_per_pixel, 0.0);
666 const uint64_t end = (uint64_t)max(
667 (x_end + pixels_offset) * samples_per_pixel, 0.0);
669 return make_pair(start, end);
672 int DecodeTrace::get_row_at_point(const QPoint &point)
677 const int y = (point.y() - get_visual_y() + row_height_ / 2);
679 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
683 const int row = y / row_height_;
685 if (row >= (int)visible_rows_.size())
691 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
693 using namespace pv::data::decode;
698 const pair<uint64_t, uint64_t> sample_range =
699 get_sample_range(point.x(), point.x() + 1);
700 const int row = get_row_at_point(point);
704 vector<pv::data::decode::Annotation> annotations;
706 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
707 sample_range.first, sample_range.second);
709 return (annotations.empty()) ?
710 QString() : annotations[0].annotations().front();
713 void DecodeTrace::hover_point_changed(const QPoint &hp)
717 const View *const view = owner_->view();
721 QToolTip::hideText();
725 QString ann = get_annotation_at_point(hp);
729 if (!row_height_ || ann.isEmpty()) {
730 QToolTip::hideText();
734 const int hover_row = get_row_at_point(hp);
736 QFontMetrics m(QToolTip::font());
737 const QRect text_size = m.boundingRect(QRect(), 0, ann);
739 // This is OS-specific and unfortunately we can't query it, so
740 // use an approximation to at least try to minimize the error.
741 const int padding = 8;
743 // Make sure the tool tip doesn't overlap with the mouse cursor.
744 // If it did, the tool tip would constantly hide and re-appear.
745 // We also push it up by one row so that it appears above the
746 // decode trace, not below.
748 p.setX(hp.x() - (text_size.width() / 2) - padding);
750 p.setY(get_visual_y() - (row_height_ / 2) +
751 (hover_row * row_height_) -
752 row_height_ - text_size.height() - padding);
754 QToolTip::showText(view->viewport()->mapToGlobal(p), ann);
757 void DecodeTrace::create_decoder_form(int index,
758 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
761 GlobalSettings settings;
764 const srd_decoder *const decoder = dec->decoder();
767 const bool decoder_deletable = index > 0;
769 pv::widgets::DecoderGroupBox *const group =
770 new pv::widgets::DecoderGroupBox(
771 QString::fromUtf8(decoder->name),
772 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
773 QString::fromUtf8(decoder->desc)),
774 nullptr, decoder_deletable);
775 group->set_decoder_visible(dec->shown());
777 if (decoder_deletable) {
778 delete_mapper_.setMapping(group, index);
779 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
782 show_hide_mapper_.setMapping(group, index);
783 connect(group, SIGNAL(show_hide_decoder()),
784 &show_hide_mapper_, SLOT(map()));
786 QFormLayout *const decoder_form = new QFormLayout;
787 group->add_layout(decoder_form);
789 const vector<DecodeChannel> channels = decode_signal_->get_channels();
792 for (DecodeChannel ch : channels) {
793 // Ignore channels not part of the decoder we create the form for
794 if (ch.decoder_ != dec)
797 QComboBox *const combo = create_channel_selector(parent, &ch);
798 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
800 channel_id_map_[combo] = ch.id;
801 init_state_map_[combo_init_state] = ch.id;
803 connect(combo, SIGNAL(currentIndexChanged(int)),
804 this, SLOT(on_channel_selected(int)));
805 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
806 this, SLOT(on_init_state_changed(int)));
808 QHBoxLayout *const hlayout = new QHBoxLayout;
809 hlayout->addWidget(combo);
810 hlayout->addWidget(combo_init_state);
812 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
813 combo_init_state->hide();
815 const QString required_flag = ch.is_optional ? QString() : QString("*");
816 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
817 .arg(ch.name, ch.desc, required_flag), hlayout);
821 shared_ptr<binding::Decoder> binding(
822 new binding::Decoder(decode_signal_, dec));
823 binding->add_properties_to_form(decoder_form, true);
825 bindings_.push_back(binding);
828 decoder_forms_.push_back(group);
831 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
833 const auto sigs(session_.signalbases());
835 // Sort signals in natural order
836 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
837 sort(sig_list.begin(), sig_list.end(),
838 [](const shared_ptr<data::SignalBase> &a,
839 const shared_ptr<data::SignalBase> &b) {
840 return strnatcasecmp(a->name().toStdString(),
841 b->name().toStdString()) < 0; });
843 QComboBox *selector = new QComboBox(parent);
845 selector->addItem("-", qVariantFromValue((void*)nullptr));
847 if (!ch->assigned_signal)
848 selector->setCurrentIndex(0);
850 for (const shared_ptr<data::SignalBase> &b : sig_list) {
852 if (b->logic_data() && b->enabled()) {
853 selector->addItem(b->name(),
854 qVariantFromValue((void*)b.get()));
856 if (ch->assigned_signal == b.get())
857 selector->setCurrentIndex(selector->count() - 1);
864 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
865 const DecodeChannel *ch)
867 QComboBox *selector = new QComboBox(parent);
869 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
870 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
871 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
873 selector->setCurrentIndex(ch->initial_pin_state);
875 selector->setToolTip("Initial (assumed) pin value before the first sample");
880 void DecodeTrace::on_new_annotations()
882 if (!delayed_trace_updater_.isActive())
883 delayed_trace_updater_.start();
886 void DecodeTrace::on_delayed_trace_update()
889 owner_->row_item_appearance_changed(false, true);
892 void DecodeTrace::on_decode_finished()
895 owner_->row_item_appearance_changed(false, true);
898 void DecodeTrace::delete_pressed()
903 void DecodeTrace::on_delete()
905 session_.remove_decode_signal(decode_signal_);
908 void DecodeTrace::on_channel_selected(int)
910 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
912 // Determine signal that was selected
913 const data::SignalBase *signal =
914 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
916 // Determine decode channel ID this combo box is the channel selector for
917 const uint16_t id = channel_id_map_.at(cb);
919 decode_signal_->assign_signal(id, signal);
922 void DecodeTrace::on_channels_updated()
925 owner_->row_item_appearance_changed(false, true);
928 void DecodeTrace::on_init_state_changed(int)
930 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
932 // Determine inital pin state that was selected
933 int init_state = cb->itemData(cb->currentIndex()).value<int>();
935 // Determine decode channel ID this combo box is the channel selector for
936 const uint16_t id = init_state_map_.at(cb);
938 decode_signal_->set_initial_pin_state(id, init_state);
941 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
943 decode_signal_->stack_decoder(decoder);
948 void DecodeTrace::on_delete_decoder(int index)
950 decode_signal_->remove_decoder(index);
952 // Force re-calculation of the trace height, see paint_mid()
953 max_visible_rows_ = 0;
954 owner_->extents_changed(false, true);
960 void DecodeTrace::on_show_hide_decoder(int index)
962 const bool state = decode_signal_->toggle_decoder_visibility(index);
964 assert(index < (int)decoder_forms_.size());
965 decoder_forms_[index]->set_decoder_visible(state);
968 // Force re-calculation of the trace height, see paint_mid()
969 max_visible_rows_ = 0;
970 owner_->extents_changed(false, true);
974 owner_->row_item_appearance_changed(false, true);