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();
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.
747 hp.setX(hp.x() - (text_size.width() / 2) - padding);
749 hp.setY(get_visual_y() - (row_height_ / 2) +
750 (hover_row * row_height_) -
751 row_height_ - text_size.height() - padding);
753 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
756 void DecodeTrace::create_decoder_form(int index,
757 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
760 GlobalSettings settings;
763 const srd_decoder *const decoder = dec->decoder();
766 const bool decoder_deletable = index > 0;
768 pv::widgets::DecoderGroupBox *const group =
769 new pv::widgets::DecoderGroupBox(
770 QString::fromUtf8(decoder->name),
771 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
772 QString::fromUtf8(decoder->desc)),
773 nullptr, decoder_deletable);
774 group->set_decoder_visible(dec->shown());
776 if (decoder_deletable) {
777 delete_mapper_.setMapping(group, index);
778 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
781 show_hide_mapper_.setMapping(group, index);
782 connect(group, SIGNAL(show_hide_decoder()),
783 &show_hide_mapper_, SLOT(map()));
785 QFormLayout *const decoder_form = new QFormLayout;
786 group->add_layout(decoder_form);
788 const vector<DecodeChannel> channels = decode_signal_->get_channels();
791 for (DecodeChannel ch : channels) {
792 // Ignore channels not part of the decoder we create the form for
793 if (ch.decoder_ != dec)
796 QComboBox *const combo = create_channel_selector(parent, &ch);
797 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
799 channel_id_map_[combo] = ch.id;
800 init_state_map_[combo_init_state] = ch.id;
802 connect(combo, SIGNAL(currentIndexChanged(int)),
803 this, SLOT(on_channel_selected(int)));
804 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
805 this, SLOT(on_init_state_changed(int)));
807 QHBoxLayout *const hlayout = new QHBoxLayout;
808 hlayout->addWidget(combo);
809 hlayout->addWidget(combo_init_state);
811 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
812 combo_init_state->hide();
814 const QString required_flag = ch.is_optional ? QString() : QString("*");
815 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
816 .arg(ch.name, ch.desc, required_flag), hlayout);
820 shared_ptr<binding::Decoder> binding(
821 new binding::Decoder(decode_signal_, dec));
822 binding->add_properties_to_form(decoder_form, true);
824 bindings_.push_back(binding);
827 decoder_forms_.push_back(group);
830 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
832 const auto sigs(session_.signalbases());
834 // Sort signals in natural order
835 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
836 sort(sig_list.begin(), sig_list.end(),
837 [](const shared_ptr<data::SignalBase> &a,
838 const shared_ptr<data::SignalBase> &b) {
839 return strnatcasecmp(a->name().toStdString(),
840 b->name().toStdString()) < 0; });
842 QComboBox *selector = new QComboBox(parent);
844 selector->addItem("-", qVariantFromValue((void*)nullptr));
846 if (!ch->assigned_signal)
847 selector->setCurrentIndex(0);
849 for (const shared_ptr<data::SignalBase> &b : sig_list) {
851 if (b->logic_data() && b->enabled()) {
852 selector->addItem(b->name(),
853 qVariantFromValue((void*)b.get()));
855 if (ch->assigned_signal == b.get())
856 selector->setCurrentIndex(selector->count() - 1);
863 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
864 const DecodeChannel *ch)
866 QComboBox *selector = new QComboBox(parent);
868 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
869 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
870 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
872 selector->setCurrentIndex(ch->initial_pin_state);
874 selector->setToolTip("Initial (assumed) pin value before the first sample");
879 void DecodeTrace::on_new_annotations()
881 if (!delayed_trace_updater_.isActive())
882 delayed_trace_updater_.start();
885 void DecodeTrace::on_delayed_trace_update()
888 owner_->row_item_appearance_changed(false, true);
891 void DecodeTrace::on_decode_finished()
894 owner_->row_item_appearance_changed(false, true);
897 void DecodeTrace::delete_pressed()
902 void DecodeTrace::on_delete()
904 session_.remove_decode_signal(decode_signal_);
907 void DecodeTrace::on_channel_selected(int)
909 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
911 // Determine signal that was selected
912 const data::SignalBase *signal =
913 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
915 // Determine decode channel ID this combo box is the channel selector for
916 const uint16_t id = channel_id_map_.at(cb);
918 decode_signal_->assign_signal(id, signal);
921 void DecodeTrace::on_channels_updated()
924 owner_->row_item_appearance_changed(false, true);
927 void DecodeTrace::on_init_state_changed(int)
929 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
931 // Determine inital pin state that was selected
932 int init_state = cb->itemData(cb->currentIndex()).value<int>();
934 // Determine decode channel ID this combo box is the channel selector for
935 const uint16_t id = init_state_map_.at(cb);
937 decode_signal_->set_initial_pin_state(id, init_state);
940 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
942 decode_signal_->stack_decoder(decoder);
947 void DecodeTrace::on_delete_decoder(int index)
949 decode_signal_->remove_decoder(index);
955 void DecodeTrace::on_show_hide_decoder(int index)
957 const bool state = decode_signal_->toggle_decoder_visibility(index);
959 assert(index < (int)decoder_forms_.size());
960 decoder_forms_[index]->set_decoder_visible(state);
963 owner_->row_item_appearance_changed(false, true);