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>
31 #include <boost/thread/locks.hpp>
32 #include <boost/thread/shared_mutex.hpp>
35 #include <QApplication>
37 #include <QFormLayout>
40 #include <QPushButton>
43 #include "decodetrace.hpp"
45 #include <pv/session.hpp>
46 #include <pv/strnatcmp.hpp>
47 #include <pv/data/decoderstack.hpp>
48 #include <pv/data/decode/decoder.hpp>
49 #include <pv/data/logic.hpp>
50 #include <pv/data/logicsegment.hpp>
51 #include <pv/data/decode/annotation.hpp>
52 #include <pv/view/view.hpp>
53 #include <pv/view/viewport.hpp>
54 #include <pv/widgets/decodergroupbox.hpp>
55 #include <pv/widgets/decodermenu.hpp>
57 using boost::shared_lock;
58 using boost::shared_mutex;
59 using std::dynamic_pointer_cast;
61 using std::lock_guard;
68 using std::shared_ptr;
69 using std::make_shared;
71 using std::unordered_set;
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 QColor DecodeTrace::Colours[16] = {
94 QColor(0xEF, 0x29, 0x29),
95 QColor(0xF6, 0x6A, 0x32),
96 QColor(0xFC, 0xAE, 0x3E),
97 QColor(0xFB, 0xCA, 0x47),
98 QColor(0xFC, 0xE9, 0x4F),
99 QColor(0xCD, 0xF0, 0x40),
100 QColor(0x8A, 0xE2, 0x34),
101 QColor(0x4E, 0xDC, 0x44),
102 QColor(0x55, 0xD7, 0x95),
103 QColor(0x64, 0xD1, 0xD2),
104 QColor(0x72, 0x9F, 0xCF),
105 QColor(0xD4, 0x76, 0xC4),
106 QColor(0x9D, 0x79, 0xB9),
107 QColor(0xAD, 0x7F, 0xA8),
108 QColor(0xC2, 0x62, 0x9B),
109 QColor(0xD7, 0x47, 0x6F)
112 const QColor DecodeTrace::OutlineColours[16] = {
113 QColor(0x77, 0x14, 0x14),
114 QColor(0x7B, 0x35, 0x19),
115 QColor(0x7E, 0x57, 0x1F),
116 QColor(0x7D, 0x65, 0x23),
117 QColor(0x7E, 0x74, 0x27),
118 QColor(0x66, 0x78, 0x20),
119 QColor(0x45, 0x71, 0x1A),
120 QColor(0x27, 0x6E, 0x22),
121 QColor(0x2A, 0x6B, 0x4A),
122 QColor(0x32, 0x68, 0x69),
123 QColor(0x39, 0x4F, 0x67),
124 QColor(0x6A, 0x3B, 0x62),
125 QColor(0x4E, 0x3C, 0x5C),
126 QColor(0x56, 0x3F, 0x54),
127 QColor(0x61, 0x31, 0x4D),
128 QColor(0x6B, 0x23, 0x37)
131 DecodeTrace::DecodeTrace(pv::Session &session,
132 shared_ptr<data::SignalBase> signalbase, int index) :
136 max_visible_rows_(0),
137 delete_mapper_(this),
138 show_hide_mapper_(this)
140 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
141 base_->decoder_stack();
143 // Determine shortest string we want to see displayed in full
144 QFontMetrics m(QApplication::font());
145 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
147 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
148 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
150 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
151 this, SLOT(on_new_decode_data()));
152 connect(&delete_mapper_, SIGNAL(mapped(int)),
153 this, SLOT(on_delete_decoder(int)));
154 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
155 this, SLOT(on_show_hide_decoder(int)));
158 bool DecodeTrace::enabled() const
163 std::shared_ptr<data::SignalBase> DecodeTrace::base() const
168 pair<int, int> DecodeTrace::v_extents() const
170 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
172 // Make an empty decode trace appear symmetrical
173 const int row_count = max(1, max_visible_rows_);
175 return make_pair(-row_height, row_height * row_count);
178 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
180 Trace::paint_back(p, pp);
181 paint_axis(p, pp, get_visual_y());
184 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
186 using namespace pv::data::decode;
188 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
189 base_->decoder_stack();
191 const int text_height = ViewItemPaintParams::text_height();
192 row_height_ = (text_height * 6) / 4;
193 const int annotation_height = (text_height * 5) / 4;
195 assert(decoder_stack);
196 const QString err = decoder_stack->error_message();
197 if (!err.isEmpty()) {
198 draw_unresolved_period(
199 p, annotation_height, pp.left(), pp.right());
200 draw_error(p, err, pp);
204 // Set default pen to allow for text width calculation
207 // Iterate through the rows
208 int y = get_visual_y();
209 pair<uint64_t, uint64_t> sample_range = get_sample_range(
210 pp.left(), pp.right());
212 const vector<Row> rows(decoder_stack->get_visible_rows());
214 visible_rows_.clear();
215 for (const Row& row : rows) {
216 // Cache the row title widths
219 row_title_width = row_title_widths_.at(row);
220 } catch (std::out_of_range) {
221 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
223 row_title_widths_[row] = w;
227 // Determine the row's color
228 size_t base_colour = 0x13579BDF;
229 boost::hash_combine(base_colour, this);
230 boost::hash_combine(base_colour, row.decoder());
231 boost::hash_combine(base_colour, row.row());
234 vector<Annotation> annotations;
235 decoder_stack->get_annotation_subset(annotations, row,
236 sample_range.first, sample_range.second);
237 if (!annotations.empty()) {
238 draw_annotations(annotations, p, annotation_height, pp, y,
239 base_colour, row_title_width);
243 visible_rows_.push_back(row);
248 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
250 if ((int)visible_rows_.size() > max_visible_rows_)
251 owner_->extents_changed(false, true);
253 // Update the maximum row count if needed
254 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
257 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
259 using namespace pv::data::decode;
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;
301 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
302 base_->decoder_stack();
306 assert(decoder_stack);
308 // Add the standard options
309 Trace::populate_popup_form(parent, form);
311 // Add the decoder options
313 channel_selectors_.clear();
314 decoder_forms_.clear();
316 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
319 QLabel *const l = new QLabel(
320 tr("<p><i>No decoders in the stack</i></p>"));
321 l->setAlignment(Qt::AlignCenter);
324 auto iter = stack.cbegin();
325 for (int i = 0; i < (int)stack.size(); i++, iter++) {
326 shared_ptr<Decoder> dec(*iter);
327 create_decoder_form(i, dec, parent, form);
330 form->addRow(new QLabel(
331 tr("<i>* Required channels</i>"), parent));
334 // Add stacking button
335 pv::widgets::DecoderMenu *const decoder_menu =
336 new pv::widgets::DecoderMenu(parent);
337 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
338 this, SLOT(on_stack_decoder(srd_decoder*)));
340 QPushButton *const stack_button =
341 new QPushButton(tr("Stack Decoder"), parent);
342 stack_button->setMenu(decoder_menu);
344 QHBoxLayout *stack_button_box = new QHBoxLayout;
345 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
346 form->addRow(stack_button_box);
349 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
351 QMenu *const menu = Trace::create_context_menu(parent);
353 menu->addSeparator();
355 QAction *const del = new QAction(tr("Delete"), this);
356 del->setShortcuts(QKeySequence::Delete);
357 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
358 menu->addAction(del);
363 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
364 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
365 size_t base_colour, int row_title_width)
367 using namespace pv::data::decode;
369 vector<Annotation> a_block;
372 double samples_per_pixel, pixels_offset;
373 tie(pixels_offset, samples_per_pixel) =
374 get_pixels_offset_samples_per_pixel();
376 // Sort the annotations by start sample so that decoders
377 // can't confuse us by creating annotations out of order
378 stable_sort(annotations.begin(), annotations.end(),
379 [](const Annotation &a, const Annotation &b) {
380 return a.start_sample() < b.start_sample(); });
382 // Gather all annotations that form a visual "block" and draw them as such
383 for (const Annotation &a : annotations) {
385 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
386 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
387 const int a_width = a_end - a_start;
389 const int delta = a_end - p_end;
391 bool a_is_separate = false;
393 // Annotation wider than the threshold for a useful label width?
394 if (a_width >= min_useful_label_width_) {
395 for (const QString &ann_text : a.annotations()) {
396 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
397 // Annotation wide enough to fit a label? Don't put it in a block then
399 a_is_separate = true;
405 // Were the previous and this annotation more than a pixel apart?
406 if ((abs(delta) > 1) || a_is_separate) {
407 // Block was broken, draw annotations that form the current block
408 if (a_block.size() == 1) {
409 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
413 draw_annotation_block(a_block, p, h, y, base_colour);
419 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
420 // Next annotation must start a new block. delta will be > 1
421 // because we set p_end to INT_MIN but that's okay since
422 // a_block will be empty, so nothing will be drawn
425 a_block.push_back(a);
430 if (a_block.size() == 1)
431 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
434 draw_annotation_block(a_block, p, h, y, base_colour);
437 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
438 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
439 size_t base_colour, int row_title_width) const
441 double samples_per_pixel, pixels_offset;
442 tie(pixels_offset, samples_per_pixel) =
443 get_pixels_offset_samples_per_pixel();
445 const double start = a.start_sample() / samples_per_pixel -
447 const double end = a.end_sample() / samples_per_pixel -
450 const size_t colour = (base_colour + a.format()) % countof(Colours);
451 p.setPen(OutlineColours[colour]);
452 p.setBrush(Colours[colour]);
454 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
457 if (a.start_sample() == a.end_sample())
458 draw_instant(a, p, h, start, y);
460 draw_range(a, p, h, start, end, y, pp,
464 void DecodeTrace::draw_annotation_block(
465 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
466 int y, size_t base_colour) const
468 using namespace pv::data::decode;
470 if (annotations.empty())
473 double samples_per_pixel, pixels_offset;
474 tie(pixels_offset, samples_per_pixel) =
475 get_pixels_offset_samples_per_pixel();
477 const double start = annotations.front().start_sample() /
478 samples_per_pixel - pixels_offset;
479 const double end = annotations.back().end_sample() /
480 samples_per_pixel - pixels_offset;
482 const double top = y + .5 - h / 2;
483 const double bottom = y + .5 + h / 2;
485 const size_t colour = (base_colour + annotations.front().format()) %
488 // Check if all annotations are of the same type (i.e. we can use one color)
489 // or if we should use a neutral color (i.e. gray)
490 const int format = annotations.front().format();
491 const bool single_format = std::all_of(
492 annotations.begin(), annotations.end(),
493 [&](const Annotation &a) { return a.format() == format; });
495 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
496 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
499 QRectF(start, top, end - start, bottom - top), h/4, h/4);
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 = std::max(ann_start, pp.left() + row_title_width);
551 const int real_end = std::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.width(), INT_MIN / 2 + y, pp.width(), 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,
602 using namespace pv::data;
603 using pv::data::decode::Decoder;
605 double samples_per_pixel, pixels_offset;
607 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
608 base_->decoder_stack();
610 assert(decoder_stack);
612 shared_ptr<Logic> data;
613 shared_ptr<data::SignalBase> signalbase;
615 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
617 // We get the logic data of the first channel in the list.
618 // This works because we are currently assuming all
619 // LogicSignals have the same data/segment
620 for (const shared_ptr<Decoder> &dec : stack)
621 if (dec && !dec->channels().empty() &&
622 ((signalbase = (*dec->channels().begin()).second)) &&
623 ((data = signalbase->logic_data())))
626 if (!data || data->logic_segments().empty())
629 const shared_ptr<LogicSegment> segment =
630 data->logic_segments().front();
632 const int64_t sample_count = (int64_t)segment->get_sample_count();
633 if (sample_count == 0)
636 const int64_t samples_decoded = decoder_stack->samples_decoded();
637 if (sample_count == samples_decoded)
640 const int y = get_visual_y();
642 tie(pixels_offset, samples_per_pixel) =
643 get_pixels_offset_samples_per_pixel();
645 const double start = max(samples_decoded /
646 samples_per_pixel - pixels_offset, left - 1.0);
647 const double end = min(sample_count / samples_per_pixel -
648 pixels_offset, right + 1.0);
649 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
651 p.setPen(QPen(Qt::NoPen));
652 p.setBrush(Qt::white);
653 p.drawRect(no_decode_rect);
655 p.setPen(NoDecodeColour);
656 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
657 p.drawRect(no_decode_rect);
660 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
662 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
663 base_->decoder_stack();
666 assert(decoder_stack);
668 const View *view = owner_->view();
671 const double scale = view->scale();
674 const double pixels_offset =
675 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
677 double samplerate = decoder_stack->samplerate();
679 // Show sample rate as 1Hz when it is unknown
680 if (samplerate == 0.0)
683 return make_pair(pixels_offset, samplerate * scale);
686 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
687 int x_start, int x_end) const
689 double samples_per_pixel, pixels_offset;
690 tie(pixels_offset, samples_per_pixel) =
691 get_pixels_offset_samples_per_pixel();
693 const uint64_t start = (uint64_t)max(
694 (x_start + pixels_offset) * samples_per_pixel, 0.0);
695 const uint64_t end = (uint64_t)max(
696 (x_end + pixels_offset) * samples_per_pixel, 0.0);
698 return make_pair(start, end);
701 int DecodeTrace::get_row_at_point(const QPoint &point)
706 const int y = (point.y() - get_visual_y() + row_height_ / 2);
708 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
712 const int row = y / row_height_;
714 if (row >= (int)visible_rows_.size())
720 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
722 using namespace pv::data::decode;
727 const pair<uint64_t, uint64_t> sample_range =
728 get_sample_range(point.x(), point.x() + 1);
729 const int row = get_row_at_point(point);
733 vector<pv::data::decode::Annotation> annotations;
735 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
736 base_->decoder_stack();
738 assert(decoder_stack);
739 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
740 sample_range.first, sample_range.second);
742 return (annotations.empty()) ?
743 QString() : annotations[0].annotations().front();
746 void DecodeTrace::hover_point_changed()
750 const View *const view = owner_->view();
753 QPoint hp = view->hover_point();
754 QString ann = get_annotation_at_point(hp);
758 if (!row_height_ || ann.isEmpty()) {
759 QToolTip::hideText();
763 const int hover_row = get_row_at_point(hp);
765 QFontMetrics m(QToolTip::font());
766 const QRect text_size = m.boundingRect(QRect(), 0, ann);
768 // This is OS-specific and unfortunately we can't query it, so
769 // use an approximation to at least try to minimize the error.
770 const int padding = 8;
772 // Make sure the tool tip doesn't overlap with the mouse cursor.
773 // If it did, the tool tip would constantly hide and re-appear.
774 // We also push it up by one row so that it appears above the
775 // decode trace, not below.
776 hp.setX(hp.x() - (text_size.width() / 2) - padding);
778 hp.setY(get_visual_y() - (row_height_ / 2) +
779 (hover_row * row_height_) -
780 row_height_ - text_size.height() - padding);
782 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
785 void DecodeTrace::create_decoder_form(int index,
786 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
792 const srd_decoder *const decoder = dec->decoder();
795 const bool decoder_deletable = index > 0;
797 pv::widgets::DecoderGroupBox *const group =
798 new pv::widgets::DecoderGroupBox(
799 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
800 group->set_decoder_visible(dec->shown());
802 if (decoder_deletable) {
803 delete_mapper_.setMapping(group, index);
804 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
807 show_hide_mapper_.setMapping(group, index);
808 connect(group, SIGNAL(show_hide_decoder()),
809 &show_hide_mapper_, SLOT(map()));
811 QFormLayout *const decoder_form = new QFormLayout;
812 group->add_layout(decoder_form);
814 // Add the mandatory channels
815 for (l = decoder->channels; l; l = l->next) {
816 const struct srd_channel *const pdch =
817 (struct srd_channel *)l->data;
818 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
819 connect(combo, SIGNAL(currentIndexChanged(int)),
820 this, SLOT(on_channel_selected(int)));
821 decoder_form->addRow(tr("<b>%1</b> (%2) *")
822 .arg(QString::fromUtf8(pdch->name),
823 QString::fromUtf8(pdch->desc)), combo);
825 const ChannelSelector s = {combo, dec, pdch};
826 channel_selectors_.push_back(s);
829 // Add the optional channels
830 for (l = decoder->opt_channels; l; l = l->next) {
831 const struct srd_channel *const pdch =
832 (struct srd_channel *)l->data;
833 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
834 connect(combo, SIGNAL(currentIndexChanged(int)),
835 this, SLOT(on_channel_selected(int)));
836 decoder_form->addRow(tr("<b>%1</b> (%2)")
837 .arg(QString::fromUtf8(pdch->name),
838 QString::fromUtf8(pdch->desc)), combo);
840 const ChannelSelector s = {combo, dec, pdch};
841 channel_selectors_.push_back(s);
844 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
845 base_->decoder_stack();
848 shared_ptr<binding::Decoder> binding(
849 new binding::Decoder(decoder_stack, dec));
850 binding->add_properties_to_form(decoder_form, true);
852 bindings_.push_back(binding);
855 decoder_forms_.push_back(group);
858 QComboBox* DecodeTrace::create_channel_selector(
859 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
860 const srd_channel *const pdch)
864 const auto sigs(session_.signalbases());
866 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
867 std::sort(sig_list.begin(), sig_list.end(),
868 [](const shared_ptr<data::SignalBase> &a,
869 const shared_ptr<data::SignalBase> &b) {
870 return strnatcasecmp(a->name().toStdString(),
871 b->name().toStdString()) < 0; });
873 const auto channel_iter = dec->channels().find(pdch);
875 QComboBox *selector = new QComboBox(parent);
877 selector->addItem("-", qVariantFromValue((void*)nullptr));
879 if (channel_iter == dec->channels().end())
880 selector->setCurrentIndex(0);
882 for (const shared_ptr<data::SignalBase> &b : sig_list) {
884 if (b->type() == sigrok::ChannelType::LOGIC && b->enabled()) {
885 selector->addItem(b->name(),
886 qVariantFromValue((void*)b.get()));
888 if (channel_iter != dec->channels().end() &&
889 (*channel_iter).second == b)
890 selector->setCurrentIndex(
891 selector->count() - 1);
898 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
902 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
904 const unordered_set< shared_ptr<data::SignalBase> >
905 sigs(session_.signalbases());
907 for (const ChannelSelector &s : channel_selectors_) {
908 if (s.decoder_ != dec)
911 const data::SignalBase *const selection =
912 (data::SignalBase*)s.combo_->itemData(
913 s.combo_->currentIndex()).value<void*>();
915 for (shared_ptr<data::SignalBase> sig : sigs)
916 if (sig.get() == selection) {
917 channel_map[s.pdch_] = sig;
922 dec->set_channels(channel_map);
925 void DecodeTrace::commit_channels()
927 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
928 base_->decoder_stack();
930 assert(decoder_stack);
931 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
932 commit_decoder_channels(dec);
934 decoder_stack->begin_decode();
937 void DecodeTrace::on_new_decode_data()
940 owner_->row_item_appearance_changed(false, true);
943 void DecodeTrace::delete_pressed()
948 void DecodeTrace::on_delete()
950 session_.remove_decode_signal(base_);
953 void DecodeTrace::on_channel_selected(int)
958 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
960 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
961 base_->decoder_stack();
964 assert(decoder_stack);
965 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
966 decoder_stack->begin_decode();
971 void DecodeTrace::on_delete_decoder(int index)
973 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
974 base_->decoder_stack();
976 decoder_stack->remove(index);
981 decoder_stack->begin_decode();
984 void DecodeTrace::on_show_hide_decoder(int index)
986 using pv::data::decode::Decoder;
988 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
989 base_->decoder_stack();
991 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
993 // Find the decoder in the stack
994 auto iter = stack.cbegin();
995 for (int i = 0; i < index; i++, iter++)
996 assert(iter != stack.end());
998 shared_ptr<Decoder> dec = *iter;
1001 const bool show = !dec->shown();
1004 assert(index < (int)decoder_forms_.size());
1005 decoder_forms_[index]->set_decoder_visible(show);
1008 owner_->row_item_appearance_changed(false, true);
1011 } // namespace TraceView
1012 } // namespace views