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 <pv/data/decode/annotation.hpp>
44 #include <pv/data/decode/decoder.hpp>
45 #include <pv/data/decoderstack.hpp>
46 #include <pv/data/logic.hpp>
47 #include <pv/data/logicsegment.hpp>
48 #include <pv/session.hpp>
49 #include <pv/strnatcmp.hpp>
50 #include <pv/view/view.hpp>
51 #include <pv/view/viewport.hpp>
52 #include <pv/widgets/decodergroupbox.hpp>
53 #include <pv/widgets/decodermenu.hpp>
62 using std::out_of_range;
64 using std::shared_ptr;
65 using std::make_shared;
67 using std::unordered_set;
74 const QColor DecodeTrace::DecodeColours[4] = {
75 QColor(0xEF, 0x29, 0x29), // Red
76 QColor(0xFC, 0xE9, 0x4F), // Yellow
77 QColor(0x8A, 0xE2, 0x34), // Green
78 QColor(0x72, 0x9F, 0xCF) // Blue
81 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
82 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
84 const int DecodeTrace::ArrowSize = 4;
85 const double DecodeTrace::EndCapWidth = 5;
86 const int DecodeTrace::RowTitleMargin = 10;
87 const int DecodeTrace::DrawPadding = 100;
89 const QColor DecodeTrace::Colours[16] = {
90 QColor(0xEF, 0x29, 0x29),
91 QColor(0xF6, 0x6A, 0x32),
92 QColor(0xFC, 0xAE, 0x3E),
93 QColor(0xFB, 0xCA, 0x47),
94 QColor(0xFC, 0xE9, 0x4F),
95 QColor(0xCD, 0xF0, 0x40),
96 QColor(0x8A, 0xE2, 0x34),
97 QColor(0x4E, 0xDC, 0x44),
98 QColor(0x55, 0xD7, 0x95),
99 QColor(0x64, 0xD1, 0xD2),
100 QColor(0x72, 0x9F, 0xCF),
101 QColor(0xD4, 0x76, 0xC4),
102 QColor(0x9D, 0x79, 0xB9),
103 QColor(0xAD, 0x7F, 0xA8),
104 QColor(0xC2, 0x62, 0x9B),
105 QColor(0xD7, 0x47, 0x6F)
108 const QColor DecodeTrace::OutlineColours[16] = {
109 QColor(0x77, 0x14, 0x14),
110 QColor(0x7B, 0x35, 0x19),
111 QColor(0x7E, 0x57, 0x1F),
112 QColor(0x7D, 0x65, 0x23),
113 QColor(0x7E, 0x74, 0x27),
114 QColor(0x66, 0x78, 0x20),
115 QColor(0x45, 0x71, 0x1A),
116 QColor(0x27, 0x6E, 0x22),
117 QColor(0x2A, 0x6B, 0x4A),
118 QColor(0x32, 0x68, 0x69),
119 QColor(0x39, 0x4F, 0x67),
120 QColor(0x6A, 0x3B, 0x62),
121 QColor(0x4E, 0x3C, 0x5C),
122 QColor(0x56, 0x3F, 0x54),
123 QColor(0x61, 0x31, 0x4D),
124 QColor(0x6B, 0x23, 0x37)
127 DecodeTrace::DecodeTrace(pv::Session &session,
128 shared_ptr<data::SignalBase> signalbase, int index) :
132 max_visible_rows_(0),
133 delete_mapper_(this),
134 show_hide_mapper_(this)
136 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
138 // Determine shortest string we want to see displayed in full
139 QFontMetrics m(QApplication::font());
140 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
142 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
143 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
145 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
146 this, SLOT(on_new_decode_data()));
147 connect(&delete_mapper_, SIGNAL(mapped(int)),
148 this, SLOT(on_delete_decoder(int)));
149 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
150 this, SLOT(on_show_hide_decoder(int)));
153 bool DecodeTrace::enabled() const
158 shared_ptr<data::SignalBase> DecodeTrace::base() const
163 pair<int, int> DecodeTrace::v_extents() const
165 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
167 // Make an empty decode trace appear symmetrical
168 const int row_count = max(1, max_visible_rows_);
170 return make_pair(-row_height, row_height * row_count);
173 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
175 Trace::paint_back(p, pp);
176 paint_axis(p, pp, get_visual_y());
179 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
181 using namespace pv::data::decode;
183 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
185 const int text_height = ViewItemPaintParams::text_height();
186 row_height_ = (text_height * 6) / 4;
187 const int annotation_height = (text_height * 5) / 4;
189 assert(decoder_stack);
190 const QString err = decoder_stack->error_message();
191 if (!err.isEmpty()) {
192 draw_unresolved_period(
193 p, annotation_height, pp.left(), pp.right());
194 draw_error(p, err, pp);
198 // Set default pen to allow for text width calculation
201 // Iterate through the rows
202 int y = get_visual_y();
203 pair<uint64_t, uint64_t> sample_range = get_sample_range(
204 pp.left(), pp.right());
206 const vector<Row> rows(decoder_stack->get_visible_rows());
208 visible_rows_.clear();
209 for (const Row& row : rows) {
210 // Cache the row title widths
213 row_title_width = row_title_widths_.at(row);
214 } catch (out_of_range) {
215 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
217 row_title_widths_[row] = w;
221 // Determine the row's color
222 size_t base_colour = 0x13579BDF;
223 boost::hash_combine(base_colour, this);
224 boost::hash_combine(base_colour, row.decoder());
225 boost::hash_combine(base_colour, row.row());
228 vector<Annotation> annotations;
229 decoder_stack->get_annotation_subset(annotations, row,
230 sample_range.first, sample_range.second);
231 if (!annotations.empty()) {
232 draw_annotations(annotations, p, annotation_height, pp, y,
233 base_colour, row_title_width);
237 visible_rows_.push_back(row);
242 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
244 if ((int)visible_rows_.size() > max_visible_rows_)
245 owner_->extents_changed(false, true);
247 // Update the maximum row count if needed
248 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
251 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
253 using namespace pv::data::decode;
257 for (size_t i = 0; i < visible_rows_.size(); i++) {
258 const int y = i * row_height_ + get_visual_y();
260 p.setPen(QPen(Qt::NoPen));
261 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
264 const QPointF points[] = {
265 QPointF(pp.left(), y - ArrowSize),
266 QPointF(pp.left() + ArrowSize, y),
267 QPointF(pp.left(), y + ArrowSize)
269 p.drawPolygon(points, countof(points));
272 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
273 pp.right() - pp.left(), row_height_);
274 const QString h(visible_rows_[i].title());
275 const int f = Qt::AlignLeft | Qt::AlignVCenter |
279 p.setPen(QApplication::palette().color(QPalette::Base));
280 for (int dx = -1; dx <= 1; dx++)
281 for (int dy = -1; dy <= 1; dy++)
282 if (dx != 0 && dy != 0)
283 p.drawText(r.translated(dx, dy), f, h);
286 p.setPen(QApplication::palette().color(QPalette::WindowText));
291 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
293 using pv::data::decode::Decoder;
295 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
299 assert(decoder_stack);
301 // Add the standard options
302 Trace::populate_popup_form(parent, form);
304 // Add the decoder options
306 channel_selectors_.clear();
307 decoder_forms_.clear();
309 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
312 QLabel *const l = new QLabel(
313 tr("<p><i>No decoders in the stack</i></p>"));
314 l->setAlignment(Qt::AlignCenter);
317 auto iter = stack.cbegin();
318 for (int i = 0; i < (int)stack.size(); i++, iter++) {
319 shared_ptr<Decoder> dec(*iter);
320 create_decoder_form(i, dec, parent, form);
323 form->addRow(new QLabel(
324 tr("<i>* Required channels</i>"), parent));
327 // Add stacking button
328 pv::widgets::DecoderMenu *const decoder_menu =
329 new pv::widgets::DecoderMenu(parent);
330 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
331 this, SLOT(on_stack_decoder(srd_decoder*)));
333 QPushButton *const stack_button =
334 new QPushButton(tr("Stack Decoder"), parent);
335 stack_button->setMenu(decoder_menu);
336 stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
338 QHBoxLayout *stack_button_box = new QHBoxLayout;
339 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
340 form->addRow(stack_button_box);
343 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
345 QMenu *const menu = Trace::create_context_menu(parent);
347 menu->addSeparator();
349 QAction *const del = new QAction(tr("Delete"), this);
350 del->setShortcuts(QKeySequence::Delete);
351 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
352 menu->addAction(del);
357 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
358 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
359 size_t base_colour, int row_title_width)
361 using namespace pv::data::decode;
363 vector<Annotation> a_block;
366 double samples_per_pixel, pixels_offset;
367 tie(pixels_offset, samples_per_pixel) =
368 get_pixels_offset_samples_per_pixel();
370 // Sort the annotations by start sample so that decoders
371 // can't confuse us by creating annotations out of order
372 stable_sort(annotations.begin(), annotations.end(),
373 [](const Annotation &a, const Annotation &b) {
374 return a.start_sample() < b.start_sample(); });
376 // Gather all annotations that form a visual "block" and draw them as such
377 for (const Annotation &a : annotations) {
379 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
380 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
381 const int a_width = a_end - a_start;
383 const int delta = a_end - p_end;
385 bool a_is_separate = false;
387 // Annotation wider than the threshold for a useful label width?
388 if (a_width >= min_useful_label_width_) {
389 for (const QString &ann_text : a.annotations()) {
390 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
391 // Annotation wide enough to fit a label? Don't put it in a block then
393 a_is_separate = true;
399 // Were the previous and this annotation more than a pixel apart?
400 if ((abs(delta) > 1) || a_is_separate) {
401 // Block was broken, draw annotations that form the current block
402 if (a_block.size() == 1) {
403 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
407 draw_annotation_block(a_block, p, h, y, base_colour);
413 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
414 // Next annotation must start a new block. delta will be > 1
415 // because we set p_end to INT_MIN but that's okay since
416 // a_block will be empty, so nothing will be drawn
419 a_block.push_back(a);
424 if (a_block.size() == 1)
425 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
428 draw_annotation_block(a_block, p, h, y, base_colour);
431 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
432 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
433 size_t base_colour, int row_title_width) const
435 double samples_per_pixel, pixels_offset;
436 tie(pixels_offset, samples_per_pixel) =
437 get_pixels_offset_samples_per_pixel();
439 const double start = a.start_sample() / samples_per_pixel -
441 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
443 const size_t colour = (base_colour + a.format()) % countof(Colours);
444 p.setPen(OutlineColours[colour]);
445 p.setBrush(Colours[colour]);
447 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
450 if (a.start_sample() == a.end_sample())
451 draw_instant(a, p, h, start, y);
453 draw_range(a, p, h, start, end, y, pp, row_title_width);
456 void DecodeTrace::draw_annotation_block(
457 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
458 int y, size_t base_colour) const
460 using namespace pv::data::decode;
462 if (annotations.empty())
465 double samples_per_pixel, pixels_offset;
466 tie(pixels_offset, samples_per_pixel) =
467 get_pixels_offset_samples_per_pixel();
469 const double start = annotations.front().start_sample() /
470 samples_per_pixel - pixels_offset;
471 const double end = annotations.back().end_sample() /
472 samples_per_pixel - pixels_offset;
474 const double top = y + .5 - h / 2;
475 const double bottom = y + .5 + h / 2;
477 const size_t colour = (base_colour + annotations.front().format()) %
480 // Check if all annotations are of the same type (i.e. we can use one color)
481 // or if we should use a neutral color (i.e. gray)
482 const int format = annotations.front().format();
483 const bool single_format = all_of(
484 annotations.begin(), annotations.end(),
485 [&](const Annotation &a) { return a.format() == format; });
487 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
488 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
491 QRectF(start, top, end - start, bottom - top), h / 4, h / 4);
494 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
495 int h, double x, int y) const
497 const QString text = a.annotations().empty() ?
498 QString() : a.annotations().back();
499 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
501 const QRectF rect(x - w / 2, y - h / 2, w, h);
503 p.drawRoundedRect(rect, h / 2, h / 2);
506 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
509 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
510 int h, double start, double end, int y, const ViewItemPaintParams &pp,
511 int row_title_width) const
513 const double top = y + .5 - h / 2;
514 const double bottom = y + .5 + h / 2;
515 const vector<QString> annotations = a.annotations();
517 // If the two ends are within 1 pixel, draw a vertical line
518 if (start + 1.0 > end) {
519 p.drawLine(QPointF(start, top), QPointF(start, bottom));
523 const double cap_width = min((end - start) / 4, EndCapWidth);
526 QPointF(start, y + .5f),
527 QPointF(start + cap_width, top),
528 QPointF(end - cap_width, top),
529 QPointF(end, y + .5f),
530 QPointF(end - cap_width, bottom),
531 QPointF(start + cap_width, bottom)
534 p.drawConvexPolygon(pts, countof(pts));
536 if (annotations.empty())
539 const int ann_start = start + cap_width;
540 const int ann_end = end - cap_width;
542 const int real_start = max(ann_start, pp.left() + row_title_width);
543 const int real_end = min(ann_end, pp.right());
544 const int real_width = real_end - real_start;
546 QRectF rect(real_start, y - h / 2, real_width, h);
547 if (rect.width() <= 4)
552 // Try to find an annotation that will fit
553 QString best_annotation;
556 for (const QString &a : annotations) {
557 const int w = p.boundingRect(QRectF(), 0, a).width();
558 if (w <= rect.width() && w > best_width)
559 best_annotation = a, best_width = w;
562 if (best_annotation.isEmpty())
563 best_annotation = annotations.back();
565 // If not ellide the last in the list
566 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
567 best_annotation, Qt::ElideRight, rect.width()));
570 void DecodeTrace::draw_error(QPainter &p, const QString &message,
571 const ViewItemPaintParams &pp)
573 const int y = get_visual_y();
575 p.setPen(ErrorBgColour.darker());
576 p.setBrush(ErrorBgColour);
578 const QRectF bounding_rect =
579 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
580 const QRectF text_rect = p.boundingRect(bounding_rect,
581 Qt::AlignCenter, message);
582 const float r = text_rect.height() / 4;
584 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
588 p.drawText(text_rect, message);
591 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
594 using namespace pv::data;
595 using pv::data::decode::Decoder;
597 double samples_per_pixel, pixels_offset;
599 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
601 assert(decoder_stack);
603 shared_ptr<Logic> data;
604 shared_ptr<data::SignalBase> signalbase;
606 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
608 // We get the logic data of the first channel in the list.
609 // This works because we are currently assuming all
610 // LogicSignals have the same data/segment
611 for (const shared_ptr<Decoder> &dec : stack)
612 if (dec && !dec->channels().empty() &&
613 ((signalbase = (*dec->channels().begin()).second)) &&
614 ((data = signalbase->logic_data())))
617 if (!data || data->logic_segments().empty())
620 const shared_ptr<LogicSegment> segment = data->logic_segments().front();
622 const int64_t sample_count = (int64_t)segment->get_sample_count();
623 if (sample_count == 0)
626 const int64_t samples_decoded = decoder_stack->samples_decoded();
627 if (sample_count == samples_decoded)
630 const int y = get_visual_y();
632 tie(pixels_offset, samples_per_pixel) =
633 get_pixels_offset_samples_per_pixel();
635 const double start = max(samples_decoded /
636 samples_per_pixel - pixels_offset, left - 1.0);
637 const double end = min(sample_count / samples_per_pixel -
638 pixels_offset, right + 1.0);
639 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
641 p.setPen(QPen(Qt::NoPen));
642 p.setBrush(Qt::white);
643 p.drawRect(no_decode_rect);
645 p.setPen(NoDecodeColour);
646 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
647 p.drawRect(no_decode_rect);
650 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
652 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
655 assert(decoder_stack);
657 const View *view = owner_->view();
660 const double scale = view->scale();
663 const double pixels_offset =
664 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
666 double samplerate = decoder_stack->samplerate();
668 // Show sample rate as 1Hz when it is unknown
669 if (samplerate == 0.0)
672 return make_pair(pixels_offset, samplerate * scale);
675 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
676 int x_start, int x_end) const
678 double samples_per_pixel, pixels_offset;
679 tie(pixels_offset, samples_per_pixel) =
680 get_pixels_offset_samples_per_pixel();
682 const uint64_t start = (uint64_t)max(
683 (x_start + pixels_offset) * samples_per_pixel, 0.0);
684 const uint64_t end = (uint64_t)max(
685 (x_end + pixels_offset) * samples_per_pixel, 0.0);
687 return make_pair(start, end);
690 int DecodeTrace::get_row_at_point(const QPoint &point)
695 const int y = (point.y() - get_visual_y() + row_height_ / 2);
697 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
701 const int row = y / row_height_;
703 if (row >= (int)visible_rows_.size())
709 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
711 using namespace pv::data::decode;
716 const pair<uint64_t, uint64_t> sample_range =
717 get_sample_range(point.x(), point.x() + 1);
718 const int row = get_row_at_point(point);
722 vector<pv::data::decode::Annotation> annotations;
724 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
726 assert(decoder_stack);
727 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
728 sample_range.first, sample_range.second);
730 return (annotations.empty()) ?
731 QString() : annotations[0].annotations().front();
734 void DecodeTrace::hover_point_changed()
738 const View *const view = owner_->view();
741 QPoint hp = view->hover_point();
742 QString ann = get_annotation_at_point(hp);
746 if (!row_height_ || ann.isEmpty()) {
747 QToolTip::hideText();
751 const int hover_row = get_row_at_point(hp);
753 QFontMetrics m(QToolTip::font());
754 const QRect text_size = m.boundingRect(QRect(), 0, ann);
756 // This is OS-specific and unfortunately we can't query it, so
757 // use an approximation to at least try to minimize the error.
758 const int padding = 8;
760 // Make sure the tool tip doesn't overlap with the mouse cursor.
761 // If it did, the tool tip would constantly hide and re-appear.
762 // We also push it up by one row so that it appears above the
763 // decode trace, not below.
764 hp.setX(hp.x() - (text_size.width() / 2) - padding);
766 hp.setY(get_visual_y() - (row_height_ / 2) +
767 (hover_row * row_height_) -
768 row_height_ - text_size.height() - padding);
770 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
773 void DecodeTrace::create_decoder_form(int index,
774 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
780 const srd_decoder *const decoder = dec->decoder();
783 const bool decoder_deletable = index > 0;
785 pv::widgets::DecoderGroupBox *const group =
786 new pv::widgets::DecoderGroupBox(
787 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
788 group->set_decoder_visible(dec->shown());
790 if (decoder_deletable) {
791 delete_mapper_.setMapping(group, index);
792 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
795 show_hide_mapper_.setMapping(group, index);
796 connect(group, SIGNAL(show_hide_decoder()),
797 &show_hide_mapper_, SLOT(map()));
799 QFormLayout *const decoder_form = new QFormLayout;
800 group->add_layout(decoder_form);
802 // Add the mandatory channels
803 for (l = decoder->channels; l; l = l->next) {
804 const struct srd_channel *const pdch =
805 (struct srd_channel *)l->data;
806 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
807 connect(combo, SIGNAL(currentIndexChanged(int)),
808 this, SLOT(on_channel_selected(int)));
809 decoder_form->addRow(tr("<b>%1</b> (%2) *")
810 .arg(QString::fromUtf8(pdch->name),
811 QString::fromUtf8(pdch->desc)), combo);
813 const ChannelSelector s = {combo, dec, pdch};
814 channel_selectors_.push_back(s);
817 // Add the optional channels
818 for (l = decoder->opt_channels; l; l = l->next) {
819 const struct srd_channel *const pdch =
820 (struct srd_channel *)l->data;
821 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
822 connect(combo, SIGNAL(currentIndexChanged(int)),
823 this, SLOT(on_channel_selected(int)));
824 decoder_form->addRow(tr("<b>%1</b> (%2)")
825 .arg(QString::fromUtf8(pdch->name),
826 QString::fromUtf8(pdch->desc)), combo);
828 const ChannelSelector s = {combo, dec, pdch};
829 channel_selectors_.push_back(s);
832 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
835 shared_ptr<binding::Decoder> binding(
836 new binding::Decoder(decoder_stack, dec));
837 binding->add_properties_to_form(decoder_form, true);
839 bindings_.push_back(binding);
842 decoder_forms_.push_back(group);
845 QComboBox* DecodeTrace::create_channel_selector(
846 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
847 const srd_channel *const pdch)
851 const auto sigs(session_.signalbases());
853 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
854 sort(sig_list.begin(), sig_list.end(),
855 [](const shared_ptr<data::SignalBase> &a,
856 const shared_ptr<data::SignalBase> &b) {
857 return strnatcasecmp(a->name().toStdString(),
858 b->name().toStdString()) < 0; });
860 const auto channel_iter = dec->channels().find(pdch);
862 QComboBox *selector = new QComboBox(parent);
864 selector->addItem("-", qVariantFromValue((void*)nullptr));
866 if (channel_iter == dec->channels().end())
867 selector->setCurrentIndex(0);
869 for (const shared_ptr<data::SignalBase> &b : sig_list) {
871 if (b->logic_data() && b->enabled()) {
872 selector->addItem(b->name(),
873 qVariantFromValue((void*)b.get()));
875 if (channel_iter != dec->channels().end() &&
876 (*channel_iter).second == b)
877 selector->setCurrentIndex(
878 selector->count() - 1);
885 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
889 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
891 const unordered_set< shared_ptr<data::SignalBase> >
892 sigs(session_.signalbases());
894 for (const ChannelSelector &s : channel_selectors_) {
895 if (s.decoder_ != dec)
898 const data::SignalBase *const selection =
899 (data::SignalBase*)s.combo_->itemData(
900 s.combo_->currentIndex()).value<void*>();
902 for (shared_ptr<data::SignalBase> sig : sigs)
903 if (sig.get() == selection) {
904 channel_map[s.pdch_] = sig;
909 dec->set_channels(channel_map);
912 void DecodeTrace::commit_channels()
914 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
916 assert(decoder_stack);
917 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
918 commit_decoder_channels(dec);
920 decoder_stack->begin_decode();
923 void DecodeTrace::on_new_decode_data()
926 owner_->row_item_appearance_changed(false, true);
929 void DecodeTrace::delete_pressed()
934 void DecodeTrace::on_delete()
936 session_.remove_decode_signal(base_);
939 void DecodeTrace::on_channel_selected(int)
944 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
946 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
949 assert(decoder_stack);
950 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
951 decoder_stack->begin_decode();
956 void DecodeTrace::on_delete_decoder(int index)
958 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
960 decoder_stack->remove(index);
965 decoder_stack->begin_decode();
968 void DecodeTrace::on_show_hide_decoder(int index)
970 using pv::data::decode::Decoder;
972 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
974 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
976 // Find the decoder in the stack
977 auto iter = stack.cbegin();
978 for (int i = 0; i < index; i++, iter++)
979 assert(iter != stack.end());
981 shared_ptr<Decoder> dec = *iter;
984 const bool show = !dec->shown();
987 assert(index < (int)decoder_forms_.size());
988 decoder_forms_[index]->set_decoder_visible(show);
991 owner_->row_item_appearance_changed(false, true);
994 } // namespace TraceView