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(channels_updated()),
153 this, SLOT(on_channels_updated()));
155 connect(&delete_mapper_, SIGNAL(mapped(int)),
156 this, SLOT(on_delete_decoder(int)));
157 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
158 this, SLOT(on_show_hide_decoder(int)));
160 connect(&delayed_trace_updater_, SIGNAL(timeout()),
161 this, SLOT(on_delayed_trace_update()));
162 delayed_trace_updater_.setSingleShot(true);
163 delayed_trace_updater_.setInterval(1000 / MaxTraceUpdateRate);
166 bool DecodeTrace::enabled() const
171 shared_ptr<data::SignalBase> DecodeTrace::base() const
176 pair<int, int> DecodeTrace::v_extents() const
178 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
180 // Make an empty decode trace appear symmetrical
181 const int row_count = max(1, max_visible_rows_);
183 return make_pair(-row_height, row_height * row_count);
186 void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
188 Trace::paint_back(p, pp);
189 paint_axis(p, pp, get_visual_y());
192 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
194 const int text_height = ViewItemPaintParams::text_height();
195 row_height_ = (text_height * 6) / 4;
196 const int annotation_height = (text_height * 5) / 4;
198 const QString err = decode_signal_->error_message();
199 if (!err.isEmpty()) {
200 draw_unresolved_period(
201 p, annotation_height, pp.left(), pp.right());
202 draw_error(p, err, pp);
206 // Set default pen to allow for text width calculation
209 // Iterate through the rows
210 int y = get_visual_y();
211 pair<uint64_t, uint64_t> sample_range = get_sample_range(
212 pp.left(), pp.right());
214 const vector<Row> rows = decode_signal_->visible_rows();
216 visible_rows_.clear();
217 for (const Row& row : rows) {
218 // Cache the row title widths
221 row_title_width = row_title_widths_.at(row);
222 } catch (out_of_range) {
223 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
225 row_title_widths_[row] = w;
229 // Determine the row's color
230 size_t base_colour = 0x13579BDF;
231 boost::hash_combine(base_colour, this);
232 boost::hash_combine(base_colour, row.decoder());
233 boost::hash_combine(base_colour, row.row());
236 vector<Annotation> annotations;
237 decode_signal_->get_annotation_subset(annotations, row,
238 sample_range.first, sample_range.second);
239 if (!annotations.empty()) {
240 draw_annotations(annotations, p, annotation_height, pp, y,
241 base_colour, row_title_width);
245 visible_rows_.push_back(row);
250 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
252 if ((int)visible_rows_.size() > max_visible_rows_)
253 owner_->extents_changed(false, true);
255 // Update the maximum row count if needed
256 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
259 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
263 for (size_t i = 0; i < visible_rows_.size(); i++) {
264 const int y = i * row_height_ + get_visual_y();
266 p.setPen(QPen(Qt::NoPen));
267 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
270 const QPointF points[] = {
271 QPointF(pp.left(), y - ArrowSize),
272 QPointF(pp.left() + ArrowSize, y),
273 QPointF(pp.left(), y + ArrowSize)
275 p.drawPolygon(points, countof(points));
278 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
279 pp.right() - pp.left(), row_height_);
280 const QString h(visible_rows_[i].title());
281 const int f = Qt::AlignLeft | Qt::AlignVCenter |
285 p.setPen(QApplication::palette().color(QPalette::Base));
286 for (int dx = -1; dx <= 1; dx++)
287 for (int dy = -1; dy <= 1; dy++)
288 if (dx != 0 && dy != 0)
289 p.drawText(r.translated(dx, dy), f, h);
292 p.setPen(QApplication::palette().color(QPalette::WindowText));
297 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
299 using pv::data::decode::Decoder;
303 // Add the standard options
304 Trace::populate_popup_form(parent, form);
306 // Add the decoder options
308 channel_id_map_.clear();
309 init_state_map_.clear();
310 decoder_forms_.clear();
312 const vector< shared_ptr<Decoder> > &stack = decode_signal_->decoder_stack();
315 QLabel *const l = new QLabel(
316 tr("<p><i>No decoders in the stack</i></p>"));
317 l->setAlignment(Qt::AlignCenter);
320 auto iter = stack.cbegin();
321 for (int i = 0; i < (int)stack.size(); i++, iter++) {
322 shared_ptr<Decoder> dec(*iter);
323 create_decoder_form(i, dec, parent, form);
326 form->addRow(new QLabel(
327 tr("<i>* Required channels</i>"), parent));
330 // Add stacking button
331 pv::widgets::DecoderMenu *const decoder_menu =
332 new pv::widgets::DecoderMenu(parent);
333 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
334 this, SLOT(on_stack_decoder(srd_decoder*)));
336 QPushButton *const stack_button =
337 new QPushButton(tr("Stack Decoder"), parent);
338 stack_button->setMenu(decoder_menu);
339 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
341 QHBoxLayout *stack_button_box = new QHBoxLayout;
342 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
343 form->addRow(stack_button_box);
346 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
348 QMenu *const menu = Trace::create_context_menu(parent);
350 menu->addSeparator();
352 QAction *const del = new QAction(tr("Delete"), this);
353 del->setShortcuts(QKeySequence::Delete);
354 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
355 menu->addAction(del);
360 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
361 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
362 size_t base_colour, int row_title_width)
364 using namespace pv::data::decode;
366 vector<Annotation> a_block;
369 double samples_per_pixel, pixels_offset;
370 tie(pixels_offset, samples_per_pixel) =
371 get_pixels_offset_samples_per_pixel();
373 // Sort the annotations by start sample so that decoders
374 // can't confuse us by creating annotations out of order
375 stable_sort(annotations.begin(), annotations.end(),
376 [](const Annotation &a, const Annotation &b) {
377 return a.start_sample() < b.start_sample(); });
379 // Gather all annotations that form a visual "block" and draw them as such
380 for (const Annotation &a : annotations) {
382 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
383 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
384 const int a_width = a_end - a_start;
386 const int delta = a_end - p_end;
388 bool a_is_separate = false;
390 // Annotation wider than the threshold for a useful label width?
391 if (a_width >= min_useful_label_width_) {
392 for (const QString &ann_text : a.annotations()) {
393 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
394 // Annotation wide enough to fit a label? Don't put it in a block then
396 a_is_separate = true;
402 // Were the previous and this annotation more than a pixel apart?
403 if ((abs(delta) > 1) || a_is_separate) {
404 // Block was broken, draw annotations that form the current block
405 if (a_block.size() == 1) {
406 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
410 draw_annotation_block(a_block, p, h, y, base_colour);
416 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
417 // Next annotation must start a new block. delta will be > 1
418 // because we set p_end to INT_MIN but that's okay since
419 // a_block will be empty, so nothing will be drawn
422 a_block.push_back(a);
427 if (a_block.size() == 1)
428 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
431 draw_annotation_block(a_block, p, h, y, base_colour);
434 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
435 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
436 size_t base_colour, int row_title_width) const
438 double samples_per_pixel, pixels_offset;
439 tie(pixels_offset, samples_per_pixel) =
440 get_pixels_offset_samples_per_pixel();
442 const double start = a.start_sample() / samples_per_pixel -
444 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
446 const size_t colour = (base_colour + a.format()) % countof(Colours);
447 p.setPen(OutlineColours[colour]);
448 p.setBrush(Colours[colour]);
450 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
453 if (a.start_sample() == a.end_sample())
454 draw_instant(a, p, h, start, y);
456 draw_range(a, p, h, start, end, y, pp, row_title_width);
459 void DecodeTrace::draw_annotation_block(
460 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
461 int y, size_t base_colour) const
463 using namespace pv::data::decode;
465 if (annotations.empty())
468 double samples_per_pixel, pixels_offset;
469 tie(pixels_offset, samples_per_pixel) =
470 get_pixels_offset_samples_per_pixel();
472 const double start = annotations.front().start_sample() /
473 samples_per_pixel - pixels_offset;
474 const double end = annotations.back().end_sample() /
475 samples_per_pixel - pixels_offset;
477 const double top = y + .5 - h / 2;
478 const double bottom = y + .5 + h / 2;
480 const size_t colour = (base_colour + annotations.front().format()) %
483 // Check if all annotations are of the same type (i.e. we can use one color)
484 // or if we should use a neutral color (i.e. gray)
485 const int format = annotations.front().format();
486 const bool single_format = all_of(
487 annotations.begin(), annotations.end(),
488 [&](const Annotation &a) { return a.format() == format; });
490 const QRectF rect(start, top, end - start, bottom - top);
493 p.setPen(QPen(Qt::NoPen));
494 p.setBrush(Qt::white);
495 p.drawRoundedRect(rect, r, r);
497 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
498 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
500 p.drawRoundedRect(rect, r, r);
503 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
504 int h, double x, int y) const
506 const QString text = a.annotations().empty() ?
507 QString() : a.annotations().back();
508 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
510 const QRectF rect(x - w / 2, y - h / 2, w, h);
512 p.drawRoundedRect(rect, h / 2, h / 2);
515 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
518 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
519 int h, double start, double end, int y, const ViewItemPaintParams &pp,
520 int row_title_width) const
522 const double top = y + .5 - h / 2;
523 const double bottom = y + .5 + h / 2;
524 const vector<QString> annotations = a.annotations();
526 // If the two ends are within 1 pixel, draw a vertical line
527 if (start + 1.0 > end) {
528 p.drawLine(QPointF(start, top), QPointF(start, bottom));
532 const double cap_width = min((end - start) / 4, EndCapWidth);
535 QPointF(start, y + .5f),
536 QPointF(start + cap_width, top),
537 QPointF(end - cap_width, top),
538 QPointF(end, y + .5f),
539 QPointF(end - cap_width, bottom),
540 QPointF(start + cap_width, bottom)
543 p.drawConvexPolygon(pts, countof(pts));
545 if (annotations.empty())
548 const int ann_start = start + cap_width;
549 const int ann_end = end - cap_width;
551 const int real_start = max(ann_start, pp.left() + row_title_width);
552 const int real_end = min(ann_end, pp.right());
553 const int real_width = real_end - real_start;
555 QRectF rect(real_start, y - h / 2, real_width, h);
556 if (rect.width() <= 4)
561 // Try to find an annotation that will fit
562 QString best_annotation;
565 for (const QString &a : annotations) {
566 const int w = p.boundingRect(QRectF(), 0, a).width();
567 if (w <= rect.width() && w > best_width)
568 best_annotation = a, best_width = w;
571 if (best_annotation.isEmpty())
572 best_annotation = annotations.back();
574 // If not ellide the last in the list
575 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
576 best_annotation, Qt::ElideRight, rect.width()));
579 void DecodeTrace::draw_error(QPainter &p, const QString &message,
580 const ViewItemPaintParams &pp)
582 const int y = get_visual_y();
584 p.setPen(ErrorBgColour.darker());
585 p.setBrush(ErrorBgColour);
587 const QRectF bounding_rect =
588 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
589 const QRectF text_rect = p.boundingRect(bounding_rect,
590 Qt::AlignCenter, message);
591 const float r = text_rect.height() / 4;
593 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
597 p.drawText(text_rect, message);
600 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right) const
602 using namespace pv::data;
603 using pv::data::decode::Decoder;
605 double samples_per_pixel, pixels_offset;
607 const int64_t sample_count = decode_signal_->get_working_sample_count();
608 if (sample_count == 0)
611 const int64_t samples_decoded = decode_signal_->get_decoded_sample_count();
612 if (sample_count == samples_decoded)
615 const int y = get_visual_y();
617 tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
619 const double start = max(samples_decoded /
620 samples_per_pixel - pixels_offset, left - 1.0);
621 const double end = min(sample_count / samples_per_pixel -
622 pixels_offset, right + 1.0);
623 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
625 p.setPen(QPen(Qt::NoPen));
626 p.setBrush(Qt::white);
627 p.drawRect(no_decode_rect);
629 p.setPen(NoDecodeColour);
630 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
631 p.drawRect(no_decode_rect);
634 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
638 const View *view = owner_->view();
641 const double scale = view->scale();
644 const double pixels_offset =
645 ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
647 double samplerate = decode_signal_->samplerate();
649 // Show sample rate as 1Hz when it is unknown
650 if (samplerate == 0.0)
653 return make_pair(pixels_offset, samplerate * scale);
656 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
657 int x_start, int x_end) const
659 double samples_per_pixel, pixels_offset;
660 tie(pixels_offset, samples_per_pixel) =
661 get_pixels_offset_samples_per_pixel();
663 const uint64_t start = (uint64_t)max(
664 (x_start + pixels_offset) * samples_per_pixel, 0.0);
665 const uint64_t end = (uint64_t)max(
666 (x_end + pixels_offset) * samples_per_pixel, 0.0);
668 return make_pair(start, end);
671 int DecodeTrace::get_row_at_point(const QPoint &point)
676 const int y = (point.y() - get_visual_y() + row_height_ / 2);
678 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
682 const int row = y / row_height_;
684 if (row >= (int)visible_rows_.size())
690 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
692 using namespace pv::data::decode;
697 const pair<uint64_t, uint64_t> sample_range =
698 get_sample_range(point.x(), point.x() + 1);
699 const int row = get_row_at_point(point);
703 vector<pv::data::decode::Annotation> annotations;
705 decode_signal_->get_annotation_subset(annotations, visible_rows_[row],
706 sample_range.first, sample_range.second);
708 return (annotations.empty()) ?
709 QString() : annotations[0].annotations().front();
712 void DecodeTrace::hover_point_changed()
716 const View *const view = owner_->view();
719 QPoint hp = view->hover_point();
720 QString ann = get_annotation_at_point(hp);
724 if (!row_height_ || ann.isEmpty()) {
725 QToolTip::hideText();
729 const int hover_row = get_row_at_point(hp);
731 QFontMetrics m(QToolTip::font());
732 const QRect text_size = m.boundingRect(QRect(), 0, ann);
734 // This is OS-specific and unfortunately we can't query it, so
735 // use an approximation to at least try to minimize the error.
736 const int padding = 8;
738 // Make sure the tool tip doesn't overlap with the mouse cursor.
739 // If it did, the tool tip would constantly hide and re-appear.
740 // We also push it up by one row so that it appears above the
741 // decode trace, not below.
742 hp.setX(hp.x() - (text_size.width() / 2) - padding);
744 hp.setY(get_visual_y() - (row_height_ / 2) +
745 (hover_row * row_height_) -
746 row_height_ - text_size.height() - padding);
748 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
751 void DecodeTrace::create_decoder_form(int index,
752 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
755 GlobalSettings settings;
758 const srd_decoder *const decoder = dec->decoder();
761 const bool decoder_deletable = index > 0;
763 pv::widgets::DecoderGroupBox *const group =
764 new pv::widgets::DecoderGroupBox(
765 QString::fromUtf8(decoder->name),
766 tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
767 QString::fromUtf8(decoder->desc)),
768 nullptr, decoder_deletable);
769 group->set_decoder_visible(dec->shown());
771 if (decoder_deletable) {
772 delete_mapper_.setMapping(group, index);
773 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
776 show_hide_mapper_.setMapping(group, index);
777 connect(group, SIGNAL(show_hide_decoder()),
778 &show_hide_mapper_, SLOT(map()));
780 QFormLayout *const decoder_form = new QFormLayout;
781 group->add_layout(decoder_form);
783 const vector<DecodeChannel> channels = decode_signal_->get_channels();
786 for (DecodeChannel ch : channels) {
787 // Ignore channels not part of the decoder we create the form for
788 if (ch.decoder_ != dec)
791 QComboBox *const combo = create_channel_selector(parent, &ch);
792 QComboBox *const combo_init_state = create_channel_selector_init_state(parent, &ch);
794 channel_id_map_[combo] = ch.id;
795 init_state_map_[combo_init_state] = ch.id;
797 connect(combo, SIGNAL(currentIndexChanged(int)),
798 this, SLOT(on_channel_selected(int)));
799 connect(combo_init_state, SIGNAL(currentIndexChanged(int)),
800 this, SLOT(on_init_state_changed(int)));
802 QHBoxLayout *const hlayout = new QHBoxLayout;
803 hlayout->addWidget(combo);
804 hlayout->addWidget(combo_init_state);
806 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
807 combo_init_state->hide();
809 const QString required_flag = ch.is_optional ? QString() : QString("*");
810 decoder_form->addRow(tr("<b>%1</b> (%2) %3")
811 .arg(ch.name, ch.desc, required_flag), hlayout);
815 shared_ptr<binding::Decoder> binding(
816 new binding::Decoder(decode_signal_, dec));
817 binding->add_properties_to_form(decoder_form, true);
819 bindings_.push_back(binding);
822 decoder_forms_.push_back(group);
825 QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeChannel *ch)
827 const auto sigs(session_.signalbases());
829 // Sort signals in natural order
830 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
831 sort(sig_list.begin(), sig_list.end(),
832 [](const shared_ptr<data::SignalBase> &a,
833 const shared_ptr<data::SignalBase> &b) {
834 return strnatcasecmp(a->name().toStdString(),
835 b->name().toStdString()) < 0; });
837 QComboBox *selector = new QComboBox(parent);
839 selector->addItem("-", qVariantFromValue((void*)nullptr));
841 if (!ch->assigned_signal)
842 selector->setCurrentIndex(0);
844 for (const shared_ptr<data::SignalBase> &b : sig_list) {
846 if (b->logic_data() && b->enabled()) {
847 selector->addItem(b->name(),
848 qVariantFromValue((void*)b.get()));
850 if (ch->assigned_signal == b.get())
851 selector->setCurrentIndex(selector->count() - 1);
858 QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
859 const DecodeChannel *ch)
861 QComboBox *selector = new QComboBox(parent);
863 selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
864 selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
865 selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
867 selector->setCurrentIndex(ch->initial_pin_state);
869 selector->setToolTip("Initial (assumed) pin value before the first sample");
874 void DecodeTrace::on_new_annotations()
876 if (!delayed_trace_updater_.isActive())
877 delayed_trace_updater_.start();
880 void DecodeTrace::on_delayed_trace_update()
883 owner_->row_item_appearance_changed(false, true);
886 void DecodeTrace::delete_pressed()
891 void DecodeTrace::on_delete()
893 session_.remove_decode_signal(decode_signal_);
896 void DecodeTrace::on_channel_selected(int)
898 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
900 // Determine signal that was selected
901 const data::SignalBase *signal =
902 (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
904 // Determine decode channel ID this combo box is the channel selector for
905 const uint16_t id = channel_id_map_.at(cb);
907 decode_signal_->assign_signal(id, signal);
910 void DecodeTrace::on_channels_updated()
913 owner_->row_item_appearance_changed(false, true);
916 void DecodeTrace::on_init_state_changed(int)
918 QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
920 // Determine inital pin state that was selected
921 int init_state = cb->itemData(cb->currentIndex()).value<int>();
923 // Determine decode channel ID this combo box is the channel selector for
924 const uint16_t id = init_state_map_.at(cb);
926 decode_signal_->set_initial_pin_state(id, init_state);
929 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
931 decode_signal_->stack_decoder(decoder);
936 void DecodeTrace::on_delete_decoder(int index)
938 decode_signal_->remove_decoder(index);
944 void DecodeTrace::on_show_hide_decoder(int index)
946 const bool state = decode_signal_->toggle_decoder_visibility(index);
948 assert(index < (int)decoder_forms_.size());
949 decoder_forms_[index]->set_decoder_visible(state);
952 owner_->row_item_appearance_changed(false, true);