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/session.hpp>
44 #include <pv/strnatcmp.hpp>
45 #include <pv/data/decoderstack.hpp>
46 #include <pv/data/decode/decoder.hpp>
47 #include <pv/data/logic.hpp>
48 #include <pv/data/logicsegment.hpp>
49 #include <pv/data/decode/annotation.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>
63 using std::out_of_range;
65 using std::shared_ptr;
66 using std::make_shared;
68 using std::unordered_set;
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 QColor DecodeTrace::Colours[16] = {
91 QColor(0xEF, 0x29, 0x29),
92 QColor(0xF6, 0x6A, 0x32),
93 QColor(0xFC, 0xAE, 0x3E),
94 QColor(0xFB, 0xCA, 0x47),
95 QColor(0xFC, 0xE9, 0x4F),
96 QColor(0xCD, 0xF0, 0x40),
97 QColor(0x8A, 0xE2, 0x34),
98 QColor(0x4E, 0xDC, 0x44),
99 QColor(0x55, 0xD7, 0x95),
100 QColor(0x64, 0xD1, 0xD2),
101 QColor(0x72, 0x9F, 0xCF),
102 QColor(0xD4, 0x76, 0xC4),
103 QColor(0x9D, 0x79, 0xB9),
104 QColor(0xAD, 0x7F, 0xA8),
105 QColor(0xC2, 0x62, 0x9B),
106 QColor(0xD7, 0x47, 0x6F)
109 const QColor DecodeTrace::OutlineColours[16] = {
110 QColor(0x77, 0x14, 0x14),
111 QColor(0x7B, 0x35, 0x19),
112 QColor(0x7E, 0x57, 0x1F),
113 QColor(0x7D, 0x65, 0x23),
114 QColor(0x7E, 0x74, 0x27),
115 QColor(0x66, 0x78, 0x20),
116 QColor(0x45, 0x71, 0x1A),
117 QColor(0x27, 0x6E, 0x22),
118 QColor(0x2A, 0x6B, 0x4A),
119 QColor(0x32, 0x68, 0x69),
120 QColor(0x39, 0x4F, 0x67),
121 QColor(0x6A, 0x3B, 0x62),
122 QColor(0x4E, 0x3C, 0x5C),
123 QColor(0x56, 0x3F, 0x54),
124 QColor(0x61, 0x31, 0x4D),
125 QColor(0x6B, 0x23, 0x37)
128 DecodeTrace::DecodeTrace(pv::Session &session,
129 shared_ptr<data::SignalBase> signalbase, int index) :
133 max_visible_rows_(0),
134 delete_mapper_(this),
135 show_hide_mapper_(this)
137 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
139 // Determine shortest string we want to see displayed in full
140 QFontMetrics m(QApplication::font());
141 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
143 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
144 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
146 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
147 this, SLOT(on_new_decode_data()));
148 connect(&delete_mapper_, SIGNAL(mapped(int)),
149 this, SLOT(on_delete_decoder(int)));
150 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
151 this, SLOT(on_show_hide_decoder(int)));
154 bool DecodeTrace::enabled() const
159 shared_ptr<data::SignalBase> DecodeTrace::base() const
164 pair<int, int> DecodeTrace::v_extents() const
166 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
168 // Make an empty decode trace appear symmetrical
169 const int row_count = max(1, max_visible_rows_);
171 return make_pair(-row_height, row_height * row_count);
174 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
176 Trace::paint_back(p, pp);
177 paint_axis(p, pp, get_visual_y());
180 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
182 using namespace pv::data::decode;
184 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
186 const int text_height = ViewItemPaintParams::text_height();
187 row_height_ = (text_height * 6) / 4;
188 const int annotation_height = (text_height * 5) / 4;
190 assert(decoder_stack);
191 const QString err = decoder_stack->error_message();
192 if (!err.isEmpty()) {
193 draw_unresolved_period(
194 p, annotation_height, pp.left(), pp.right());
195 draw_error(p, err, pp);
199 // Set default pen to allow for text width calculation
202 // Iterate through the rows
203 int y = get_visual_y();
204 pair<uint64_t, uint64_t> sample_range = get_sample_range(
205 pp.left(), pp.right());
207 const vector<Row> rows(decoder_stack->get_visible_rows());
209 visible_rows_.clear();
210 for (const Row& row : rows) {
211 // Cache the row title widths
214 row_title_width = row_title_widths_.at(row);
215 } catch (out_of_range) {
216 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
218 row_title_widths_[row] = w;
222 // Determine the row's color
223 size_t base_colour = 0x13579BDF;
224 boost::hash_combine(base_colour, this);
225 boost::hash_combine(base_colour, row.decoder());
226 boost::hash_combine(base_colour, row.row());
229 vector<Annotation> annotations;
230 decoder_stack->get_annotation_subset(annotations, row,
231 sample_range.first, sample_range.second);
232 if (!annotations.empty()) {
233 draw_annotations(annotations, p, annotation_height, pp, y,
234 base_colour, row_title_width);
238 visible_rows_.push_back(row);
243 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
245 if ((int)visible_rows_.size() > max_visible_rows_)
246 owner_->extents_changed(false, true);
248 // Update the maximum row count if needed
249 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
252 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
254 using namespace pv::data::decode;
258 for (size_t i = 0; i < visible_rows_.size(); i++) {
259 const int y = i * row_height_ + get_visual_y();
261 p.setPen(QPen(Qt::NoPen));
262 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
265 const QPointF points[] = {
266 QPointF(pp.left(), y - ArrowSize),
267 QPointF(pp.left() + ArrowSize, y),
268 QPointF(pp.left(), y + ArrowSize)
270 p.drawPolygon(points, countof(points));
273 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
274 pp.right() - pp.left(), row_height_);
275 const QString h(visible_rows_[i].title());
276 const int f = Qt::AlignLeft | Qt::AlignVCenter |
280 p.setPen(QApplication::palette().color(QPalette::Base));
281 for (int dx = -1; dx <= 1; dx++)
282 for (int dy = -1; dy <= 1; dy++)
283 if (dx != 0 && dy != 0)
284 p.drawText(r.translated(dx, dy), f, h);
287 p.setPen(QApplication::palette().color(QPalette::WindowText));
292 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
294 using pv::data::decode::Decoder;
296 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
300 assert(decoder_stack);
302 // Add the standard options
303 Trace::populate_popup_form(parent, form);
305 // Add the decoder options
307 channel_selectors_.clear();
308 decoder_forms_.clear();
310 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
313 QLabel *const l = new QLabel(
314 tr("<p><i>No decoders in the stack</i></p>"));
315 l->setAlignment(Qt::AlignCenter);
318 auto iter = stack.cbegin();
319 for (int i = 0; i < (int)stack.size(); i++, iter++) {
320 shared_ptr<Decoder> dec(*iter);
321 create_decoder_form(i, dec, parent, form);
324 form->addRow(new QLabel(
325 tr("<i>* Required channels</i>"), parent));
328 // Add stacking button
329 pv::widgets::DecoderMenu *const decoder_menu =
330 new pv::widgets::DecoderMenu(parent);
331 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
332 this, SLOT(on_stack_decoder(srd_decoder*)));
334 QPushButton *const stack_button =
335 new QPushButton(tr("Stack Decoder"), parent);
336 stack_button->setMenu(decoder_menu);
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 -
444 const size_t colour = (base_colour + a.format()) % countof(Colours);
445 p.setPen(OutlineColours[colour]);
446 p.setBrush(Colours[colour]);
448 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
451 if (a.start_sample() == a.end_sample())
452 draw_instant(a, p, h, start, y);
454 draw_range(a, p, h, start, end, y, pp,
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 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
490 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
493 QRectF(start, top, end - start, bottom - top), h/4, h/4);
496 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
497 int h, double x, int y) const
499 const QString text = a.annotations().empty() ?
500 QString() : a.annotations().back();
501 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
503 const QRectF rect(x - w / 2, y - h / 2, w, h);
505 p.drawRoundedRect(rect, h / 2, h / 2);
508 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
511 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
512 int h, double start, double end, int y, const ViewItemPaintParams &pp,
513 int row_title_width) const
515 const double top = y + .5 - h / 2;
516 const double bottom = y + .5 + h / 2;
517 const vector<QString> annotations = a.annotations();
519 // If the two ends are within 1 pixel, draw a vertical line
520 if (start + 1.0 > end) {
521 p.drawLine(QPointF(start, top), QPointF(start, bottom));
525 const double cap_width = min((end - start) / 4, EndCapWidth);
528 QPointF(start, y + .5f),
529 QPointF(start + cap_width, top),
530 QPointF(end - cap_width, top),
531 QPointF(end, y + .5f),
532 QPointF(end - cap_width, bottom),
533 QPointF(start + cap_width, bottom)
536 p.drawConvexPolygon(pts, countof(pts));
538 if (annotations.empty())
541 const int ann_start = start + cap_width;
542 const int ann_end = end - cap_width;
544 const int real_start = max(ann_start, pp.left() + row_title_width);
545 const int real_end = min(ann_end, pp.right());
546 const int real_width = real_end - real_start;
548 QRectF rect(real_start, y - h / 2, real_width, h);
549 if (rect.width() <= 4)
554 // Try to find an annotation that will fit
555 QString best_annotation;
558 for (const QString &a : annotations) {
559 const int w = p.boundingRect(QRectF(), 0, a).width();
560 if (w <= rect.width() && w > best_width)
561 best_annotation = a, best_width = w;
564 if (best_annotation.isEmpty())
565 best_annotation = annotations.back();
567 // If not ellide the last in the list
568 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
569 best_annotation, Qt::ElideRight, rect.width()));
572 void DecodeTrace::draw_error(QPainter &p, const QString &message,
573 const ViewItemPaintParams &pp)
575 const int y = get_visual_y();
577 p.setPen(ErrorBgColour.darker());
578 p.setBrush(ErrorBgColour);
580 const QRectF bounding_rect =
581 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
582 const QRectF text_rect = p.boundingRect(bounding_rect,
583 Qt::AlignCenter, message);
584 const float r = text_rect.height() / 4;
586 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
590 p.drawText(text_rect, message);
593 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
596 using namespace pv::data;
597 using pv::data::decode::Decoder;
599 double samples_per_pixel, pixels_offset;
601 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
603 assert(decoder_stack);
605 shared_ptr<Logic> data;
606 shared_ptr<data::SignalBase> signalbase;
608 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
610 // We get the logic data of the first channel in the list.
611 // This works because we are currently assuming all
612 // LogicSignals have the same data/segment
613 for (const shared_ptr<Decoder> &dec : stack)
614 if (dec && !dec->channels().empty() &&
615 ((signalbase = (*dec->channels().begin()).second)) &&
616 ((data = signalbase->logic_data())))
619 if (!data || data->logic_segments().empty())
622 const shared_ptr<LogicSegment> segment =
623 data->logic_segments().front();
625 const int64_t sample_count = (int64_t)segment->get_sample_count();
626 if (sample_count == 0)
629 const int64_t samples_decoded = decoder_stack->samples_decoded();
630 if (sample_count == samples_decoded)
633 const int y = get_visual_y();
635 tie(pixels_offset, samples_per_pixel) =
636 get_pixels_offset_samples_per_pixel();
638 const double start = max(samples_decoded /
639 samples_per_pixel - pixels_offset, left - 1.0);
640 const double end = min(sample_count / samples_per_pixel -
641 pixels_offset, right + 1.0);
642 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
644 p.setPen(QPen(Qt::NoPen));
645 p.setBrush(Qt::white);
646 p.drawRect(no_decode_rect);
648 p.setPen(NoDecodeColour);
649 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
650 p.drawRect(no_decode_rect);
653 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
655 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
658 assert(decoder_stack);
660 const View *view = owner_->view();
663 const double scale = view->scale();
666 const double pixels_offset =
667 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
669 double samplerate = decoder_stack->samplerate();
671 // Show sample rate as 1Hz when it is unknown
672 if (samplerate == 0.0)
675 return make_pair(pixels_offset, samplerate * scale);
678 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
679 int x_start, int x_end) const
681 double samples_per_pixel, pixels_offset;
682 tie(pixels_offset, samples_per_pixel) =
683 get_pixels_offset_samples_per_pixel();
685 const uint64_t start = (uint64_t)max(
686 (x_start + pixels_offset) * samples_per_pixel, 0.0);
687 const uint64_t end = (uint64_t)max(
688 (x_end + pixels_offset) * samples_per_pixel, 0.0);
690 return make_pair(start, end);
693 int DecodeTrace::get_row_at_point(const QPoint &point)
698 const int y = (point.y() - get_visual_y() + row_height_ / 2);
700 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
704 const int row = y / row_height_;
706 if (row >= (int)visible_rows_.size())
712 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
714 using namespace pv::data::decode;
719 const pair<uint64_t, uint64_t> sample_range =
720 get_sample_range(point.x(), point.x() + 1);
721 const int row = get_row_at_point(point);
725 vector<pv::data::decode::Annotation> annotations;
727 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
729 assert(decoder_stack);
730 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
731 sample_range.first, sample_range.second);
733 return (annotations.empty()) ?
734 QString() : annotations[0].annotations().front();
737 void DecodeTrace::hover_point_changed()
741 const View *const view = owner_->view();
744 QPoint hp = view->hover_point();
745 QString ann = get_annotation_at_point(hp);
749 if (!row_height_ || ann.isEmpty()) {
750 QToolTip::hideText();
754 const int hover_row = get_row_at_point(hp);
756 QFontMetrics m(QToolTip::font());
757 const QRect text_size = m.boundingRect(QRect(), 0, ann);
759 // This is OS-specific and unfortunately we can't query it, so
760 // use an approximation to at least try to minimize the error.
761 const int padding = 8;
763 // Make sure the tool tip doesn't overlap with the mouse cursor.
764 // If it did, the tool tip would constantly hide and re-appear.
765 // We also push it up by one row so that it appears above the
766 // decode trace, not below.
767 hp.setX(hp.x() - (text_size.width() / 2) - padding);
769 hp.setY(get_visual_y() - (row_height_ / 2) +
770 (hover_row * row_height_) -
771 row_height_ - text_size.height() - padding);
773 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
776 void DecodeTrace::create_decoder_form(int index,
777 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
783 const srd_decoder *const decoder = dec->decoder();
786 const bool decoder_deletable = index > 0;
788 pv::widgets::DecoderGroupBox *const group =
789 new pv::widgets::DecoderGroupBox(
790 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
791 group->set_decoder_visible(dec->shown());
793 if (decoder_deletable) {
794 delete_mapper_.setMapping(group, index);
795 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
798 show_hide_mapper_.setMapping(group, index);
799 connect(group, SIGNAL(show_hide_decoder()),
800 &show_hide_mapper_, SLOT(map()));
802 QFormLayout *const decoder_form = new QFormLayout;
803 group->add_layout(decoder_form);
805 // Add the mandatory channels
806 for (l = decoder->channels; l; l = l->next) {
807 const struct srd_channel *const pdch =
808 (struct srd_channel *)l->data;
809 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
810 connect(combo, SIGNAL(currentIndexChanged(int)),
811 this, SLOT(on_channel_selected(int)));
812 decoder_form->addRow(tr("<b>%1</b> (%2) *")
813 .arg(QString::fromUtf8(pdch->name),
814 QString::fromUtf8(pdch->desc)), combo);
816 const ChannelSelector s = {combo, dec, pdch};
817 channel_selectors_.push_back(s);
820 // Add the optional channels
821 for (l = decoder->opt_channels; l; l = l->next) {
822 const struct srd_channel *const pdch =
823 (struct srd_channel *)l->data;
824 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
825 connect(combo, SIGNAL(currentIndexChanged(int)),
826 this, SLOT(on_channel_selected(int)));
827 decoder_form->addRow(tr("<b>%1</b> (%2)")
828 .arg(QString::fromUtf8(pdch->name),
829 QString::fromUtf8(pdch->desc)), combo);
831 const ChannelSelector s = {combo, dec, pdch};
832 channel_selectors_.push_back(s);
835 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
838 shared_ptr<binding::Decoder> binding(
839 new binding::Decoder(decoder_stack, dec));
840 binding->add_properties_to_form(decoder_form, true);
842 bindings_.push_back(binding);
845 decoder_forms_.push_back(group);
848 QComboBox* DecodeTrace::create_channel_selector(
849 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
850 const srd_channel *const pdch)
854 const auto sigs(session_.signalbases());
856 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
857 sort(sig_list.begin(), sig_list.end(),
858 [](const shared_ptr<data::SignalBase> &a,
859 const shared_ptr<data::SignalBase> &b) {
860 return strnatcasecmp(a->name().toStdString(),
861 b->name().toStdString()) < 0; });
863 const auto channel_iter = dec->channels().find(pdch);
865 QComboBox *selector = new QComboBox(parent);
867 selector->addItem("-", qVariantFromValue((void*)nullptr));
869 if (channel_iter == dec->channels().end())
870 selector->setCurrentIndex(0);
872 for (const shared_ptr<data::SignalBase> &b : sig_list) {
874 if (b->logic_data() && b->enabled()) {
875 selector->addItem(b->name(),
876 qVariantFromValue((void*)b.get()));
878 if (channel_iter != dec->channels().end() &&
879 (*channel_iter).second == b)
880 selector->setCurrentIndex(
881 selector->count() - 1);
888 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
892 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
894 const unordered_set< shared_ptr<data::SignalBase> >
895 sigs(session_.signalbases());
897 for (const ChannelSelector &s : channel_selectors_) {
898 if (s.decoder_ != dec)
901 const data::SignalBase *const selection =
902 (data::SignalBase*)s.combo_->itemData(
903 s.combo_->currentIndex()).value<void*>();
905 for (shared_ptr<data::SignalBase> sig : sigs)
906 if (sig.get() == selection) {
907 channel_map[s.pdch_] = sig;
912 dec->set_channels(channel_map);
915 void DecodeTrace::commit_channels()
917 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
919 assert(decoder_stack);
920 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
921 commit_decoder_channels(dec);
923 decoder_stack->begin_decode();
926 void DecodeTrace::on_new_decode_data()
929 owner_->row_item_appearance_changed(false, true);
932 void DecodeTrace::delete_pressed()
937 void DecodeTrace::on_delete()
939 session_.remove_decode_signal(base_);
942 void DecodeTrace::on_channel_selected(int)
947 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
949 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
952 assert(decoder_stack);
953 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
954 decoder_stack->begin_decode();
959 void DecodeTrace::on_delete_decoder(int index)
961 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
963 decoder_stack->remove(index);
968 decoder_stack->begin_decode();
971 void DecodeTrace::on_show_hide_decoder(int index)
973 using pv::data::decode::Decoder;
975 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
977 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
979 // Find the decoder in the stack
980 auto iter = stack.cbegin();
981 for (int i = 0; i < index; i++, iter++)
982 assert(iter != stack.end());
984 shared_ptr<Decoder> dec = *iter;
987 const bool show = !dec->shown();
990 assert(index < (int)decoder_forms_.size());
991 decoder_forms_[index]->set_decoder_visible(show);
994 owner_->row_item_appearance_changed(false, true);
997 } // namespace TraceView