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);
337 QHBoxLayout *stack_button_box = new QHBoxLayout;
338 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
339 form->addRow(stack_button_box);
342 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
344 QMenu *const menu = Trace::create_context_menu(parent);
346 menu->addSeparator();
348 QAction *const del = new QAction(tr("Delete"), this);
349 del->setShortcuts(QKeySequence::Delete);
350 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
351 menu->addAction(del);
356 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
357 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
358 size_t base_colour, int row_title_width)
360 using namespace pv::data::decode;
362 vector<Annotation> a_block;
365 double samples_per_pixel, pixels_offset;
366 tie(pixels_offset, samples_per_pixel) =
367 get_pixels_offset_samples_per_pixel();
369 // Sort the annotations by start sample so that decoders
370 // can't confuse us by creating annotations out of order
371 stable_sort(annotations.begin(), annotations.end(),
372 [](const Annotation &a, const Annotation &b) {
373 return a.start_sample() < b.start_sample(); });
375 // Gather all annotations that form a visual "block" and draw them as such
376 for (const Annotation &a : annotations) {
378 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
379 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
380 const int a_width = a_end - a_start;
382 const int delta = a_end - p_end;
384 bool a_is_separate = false;
386 // Annotation wider than the threshold for a useful label width?
387 if (a_width >= min_useful_label_width_) {
388 for (const QString &ann_text : a.annotations()) {
389 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
390 // Annotation wide enough to fit a label? Don't put it in a block then
392 a_is_separate = true;
398 // Were the previous and this annotation more than a pixel apart?
399 if ((abs(delta) > 1) || a_is_separate) {
400 // Block was broken, draw annotations that form the current block
401 if (a_block.size() == 1) {
402 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
406 draw_annotation_block(a_block, p, h, y, base_colour);
412 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
413 // Next annotation must start a new block. delta will be > 1
414 // because we set p_end to INT_MIN but that's okay since
415 // a_block will be empty, so nothing will be drawn
418 a_block.push_back(a);
423 if (a_block.size() == 1)
424 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
427 draw_annotation_block(a_block, p, h, y, base_colour);
430 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
431 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
432 size_t base_colour, int row_title_width) const
434 double samples_per_pixel, pixels_offset;
435 tie(pixels_offset, samples_per_pixel) =
436 get_pixels_offset_samples_per_pixel();
438 const double start = a.start_sample() / samples_per_pixel -
440 const double end = a.end_sample() / samples_per_pixel - pixels_offset;
442 const size_t colour = (base_colour + a.format()) % countof(Colours);
443 p.setPen(OutlineColours[colour]);
444 p.setBrush(Colours[colour]);
446 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
449 if (a.start_sample() == a.end_sample())
450 draw_instant(a, p, h, start, y);
452 draw_range(a, p, h, start, end, y, pp, row_title_width);
455 void DecodeTrace::draw_annotation_block(
456 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
457 int y, size_t base_colour) const
459 using namespace pv::data::decode;
461 if (annotations.empty())
464 double samples_per_pixel, pixels_offset;
465 tie(pixels_offset, samples_per_pixel) =
466 get_pixels_offset_samples_per_pixel();
468 const double start = annotations.front().start_sample() /
469 samples_per_pixel - pixels_offset;
470 const double end = annotations.back().end_sample() /
471 samples_per_pixel - pixels_offset;
473 const double top = y + .5 - h / 2;
474 const double bottom = y + .5 + h / 2;
476 const size_t colour = (base_colour + annotations.front().format()) %
479 // Check if all annotations are of the same type (i.e. we can use one color)
480 // or if we should use a neutral color (i.e. gray)
481 const int format = annotations.front().format();
482 const bool single_format = all_of(
483 annotations.begin(), annotations.end(),
484 [&](const Annotation &a) { return a.format() == format; });
486 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
487 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
490 QRectF(start, top, end - start, bottom - top), h / 4, h / 4);
493 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
494 int h, double x, int y) const
496 const QString text = a.annotations().empty() ?
497 QString() : a.annotations().back();
498 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
500 const QRectF rect(x - w / 2, y - h / 2, w, h);
502 p.drawRoundedRect(rect, h / 2, h / 2);
505 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
508 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
509 int h, double start, double end, int y, const ViewItemPaintParams &pp,
510 int row_title_width) const
512 const double top = y + .5 - h / 2;
513 const double bottom = y + .5 + h / 2;
514 const vector<QString> annotations = a.annotations();
516 // If the two ends are within 1 pixel, draw a vertical line
517 if (start + 1.0 > end) {
518 p.drawLine(QPointF(start, top), QPointF(start, bottom));
522 const double cap_width = min((end - start) / 4, EndCapWidth);
525 QPointF(start, y + .5f),
526 QPointF(start + cap_width, top),
527 QPointF(end - cap_width, top),
528 QPointF(end, y + .5f),
529 QPointF(end - cap_width, bottom),
530 QPointF(start + cap_width, bottom)
533 p.drawConvexPolygon(pts, countof(pts));
535 if (annotations.empty())
538 const int ann_start = start + cap_width;
539 const int ann_end = end - cap_width;
541 const int real_start = max(ann_start, pp.left() + row_title_width);
542 const int real_end = min(ann_end, pp.right());
543 const int real_width = real_end - real_start;
545 QRectF rect(real_start, y - h / 2, real_width, h);
546 if (rect.width() <= 4)
551 // Try to find an annotation that will fit
552 QString best_annotation;
555 for (const QString &a : annotations) {
556 const int w = p.boundingRect(QRectF(), 0, a).width();
557 if (w <= rect.width() && w > best_width)
558 best_annotation = a, best_width = w;
561 if (best_annotation.isEmpty())
562 best_annotation = annotations.back();
564 // If not ellide the last in the list
565 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
566 best_annotation, Qt::ElideRight, rect.width()));
569 void DecodeTrace::draw_error(QPainter &p, const QString &message,
570 const ViewItemPaintParams &pp)
572 const int y = get_visual_y();
574 p.setPen(ErrorBgColour.darker());
575 p.setBrush(ErrorBgColour);
577 const QRectF bounding_rect =
578 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
579 const QRectF text_rect = p.boundingRect(bounding_rect,
580 Qt::AlignCenter, message);
581 const float r = text_rect.height() / 4;
583 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
587 p.drawText(text_rect, message);
590 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
593 using namespace pv::data;
594 using pv::data::decode::Decoder;
596 double samples_per_pixel, pixels_offset;
598 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
600 assert(decoder_stack);
602 shared_ptr<Logic> data;
603 shared_ptr<data::SignalBase> signalbase;
605 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
607 // We get the logic data of the first channel in the list.
608 // This works because we are currently assuming all
609 // LogicSignals have the same data/segment
610 for (const shared_ptr<Decoder> &dec : stack)
611 if (dec && !dec->channels().empty() &&
612 ((signalbase = (*dec->channels().begin()).second)) &&
613 ((data = signalbase->logic_data())))
616 if (!data || data->logic_segments().empty())
619 const shared_ptr<LogicSegment> segment = data->logic_segments().front();
621 const int64_t sample_count = (int64_t)segment->get_sample_count();
622 if (sample_count == 0)
625 const int64_t samples_decoded = decoder_stack->samples_decoded();
626 if (sample_count == samples_decoded)
629 const int y = get_visual_y();
631 tie(pixels_offset, samples_per_pixel) =
632 get_pixels_offset_samples_per_pixel();
634 const double start = max(samples_decoded /
635 samples_per_pixel - pixels_offset, left - 1.0);
636 const double end = min(sample_count / samples_per_pixel -
637 pixels_offset, right + 1.0);
638 const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
640 p.setPen(QPen(Qt::NoPen));
641 p.setBrush(Qt::white);
642 p.drawRect(no_decode_rect);
644 p.setPen(NoDecodeColour);
645 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
646 p.drawRect(no_decode_rect);
649 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
651 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
654 assert(decoder_stack);
656 const View *view = owner_->view();
659 const double scale = view->scale();
662 const double pixels_offset =
663 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
665 double samplerate = decoder_stack->samplerate();
667 // Show sample rate as 1Hz when it is unknown
668 if (samplerate == 0.0)
671 return make_pair(pixels_offset, samplerate * scale);
674 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
675 int x_start, int x_end) const
677 double samples_per_pixel, pixels_offset;
678 tie(pixels_offset, samples_per_pixel) =
679 get_pixels_offset_samples_per_pixel();
681 const uint64_t start = (uint64_t)max(
682 (x_start + pixels_offset) * samples_per_pixel, 0.0);
683 const uint64_t end = (uint64_t)max(
684 (x_end + pixels_offset) * samples_per_pixel, 0.0);
686 return make_pair(start, end);
689 int DecodeTrace::get_row_at_point(const QPoint &point)
694 const int y = (point.y() - get_visual_y() + row_height_ / 2);
696 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
700 const int row = y / row_height_;
702 if (row >= (int)visible_rows_.size())
708 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
710 using namespace pv::data::decode;
715 const pair<uint64_t, uint64_t> sample_range =
716 get_sample_range(point.x(), point.x() + 1);
717 const int row = get_row_at_point(point);
721 vector<pv::data::decode::Annotation> annotations;
723 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
725 assert(decoder_stack);
726 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
727 sample_range.first, sample_range.second);
729 return (annotations.empty()) ?
730 QString() : annotations[0].annotations().front();
733 void DecodeTrace::hover_point_changed()
737 const View *const view = owner_->view();
740 QPoint hp = view->hover_point();
741 QString ann = get_annotation_at_point(hp);
745 if (!row_height_ || ann.isEmpty()) {
746 QToolTip::hideText();
750 const int hover_row = get_row_at_point(hp);
752 QFontMetrics m(QToolTip::font());
753 const QRect text_size = m.boundingRect(QRect(), 0, ann);
755 // This is OS-specific and unfortunately we can't query it, so
756 // use an approximation to at least try to minimize the error.
757 const int padding = 8;
759 // Make sure the tool tip doesn't overlap with the mouse cursor.
760 // If it did, the tool tip would constantly hide and re-appear.
761 // We also push it up by one row so that it appears above the
762 // decode trace, not below.
763 hp.setX(hp.x() - (text_size.width() / 2) - padding);
765 hp.setY(get_visual_y() - (row_height_ / 2) +
766 (hover_row * row_height_) -
767 row_height_ - text_size.height() - padding);
769 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
772 void DecodeTrace::create_decoder_form(int index,
773 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
779 const srd_decoder *const decoder = dec->decoder();
782 const bool decoder_deletable = index > 0;
784 pv::widgets::DecoderGroupBox *const group =
785 new pv::widgets::DecoderGroupBox(
786 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
787 group->set_decoder_visible(dec->shown());
789 if (decoder_deletable) {
790 delete_mapper_.setMapping(group, index);
791 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
794 show_hide_mapper_.setMapping(group, index);
795 connect(group, SIGNAL(show_hide_decoder()),
796 &show_hide_mapper_, SLOT(map()));
798 QFormLayout *const decoder_form = new QFormLayout;
799 group->add_layout(decoder_form);
801 // Add the mandatory channels
802 for (l = decoder->channels; l; l = l->next) {
803 const struct srd_channel *const pdch =
804 (struct srd_channel *)l->data;
805 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
806 connect(combo, SIGNAL(currentIndexChanged(int)),
807 this, SLOT(on_channel_selected(int)));
808 decoder_form->addRow(tr("<b>%1</b> (%2) *")
809 .arg(QString::fromUtf8(pdch->name),
810 QString::fromUtf8(pdch->desc)), combo);
812 const ChannelSelector s = {combo, dec, pdch};
813 channel_selectors_.push_back(s);
816 // Add the optional channels
817 for (l = decoder->opt_channels; l; l = l->next) {
818 const struct srd_channel *const pdch =
819 (struct srd_channel *)l->data;
820 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
821 connect(combo, SIGNAL(currentIndexChanged(int)),
822 this, SLOT(on_channel_selected(int)));
823 decoder_form->addRow(tr("<b>%1</b> (%2)")
824 .arg(QString::fromUtf8(pdch->name),
825 QString::fromUtf8(pdch->desc)), combo);
827 const ChannelSelector s = {combo, dec, pdch};
828 channel_selectors_.push_back(s);
831 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
834 shared_ptr<binding::Decoder> binding(
835 new binding::Decoder(decoder_stack, dec));
836 binding->add_properties_to_form(decoder_form, true);
838 bindings_.push_back(binding);
841 decoder_forms_.push_back(group);
844 QComboBox* DecodeTrace::create_channel_selector(
845 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
846 const srd_channel *const pdch)
850 const auto sigs(session_.signalbases());
852 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
853 sort(sig_list.begin(), sig_list.end(),
854 [](const shared_ptr<data::SignalBase> &a,
855 const shared_ptr<data::SignalBase> &b) {
856 return strnatcasecmp(a->name().toStdString(),
857 b->name().toStdString()) < 0; });
859 const auto channel_iter = dec->channels().find(pdch);
861 QComboBox *selector = new QComboBox(parent);
863 selector->addItem("-", qVariantFromValue((void*)nullptr));
865 if (channel_iter == dec->channels().end())
866 selector->setCurrentIndex(0);
868 for (const shared_ptr<data::SignalBase> &b : sig_list) {
870 if (b->logic_data() && b->enabled()) {
871 selector->addItem(b->name(),
872 qVariantFromValue((void*)b.get()));
874 if (channel_iter != dec->channels().end() &&
875 (*channel_iter).second == b)
876 selector->setCurrentIndex(
877 selector->count() - 1);
884 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
888 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
890 const unordered_set< shared_ptr<data::SignalBase> >
891 sigs(session_.signalbases());
893 for (const ChannelSelector &s : channel_selectors_) {
894 if (s.decoder_ != dec)
897 const data::SignalBase *const selection =
898 (data::SignalBase*)s.combo_->itemData(
899 s.combo_->currentIndex()).value<void*>();
901 for (shared_ptr<data::SignalBase> sig : sigs)
902 if (sig.get() == selection) {
903 channel_map[s.pdch_] = sig;
908 dec->set_channels(channel_map);
911 void DecodeTrace::commit_channels()
913 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
915 assert(decoder_stack);
916 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
917 commit_decoder_channels(dec);
919 decoder_stack->begin_decode();
922 void DecodeTrace::on_new_decode_data()
925 owner_->row_item_appearance_changed(false, true);
928 void DecodeTrace::delete_pressed()
933 void DecodeTrace::on_delete()
935 session_.remove_decode_signal(base_);
938 void DecodeTrace::on_channel_selected(int)
943 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
945 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
948 assert(decoder_stack);
949 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
950 decoder_stack->begin_decode();
955 void DecodeTrace::on_delete_decoder(int index)
957 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
959 decoder_stack->remove(index);
964 decoder_stack->begin_decode();
967 void DecodeTrace::on_show_hide_decoder(int index)
969 using pv::data::decode::Decoder;
971 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
973 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
975 // Find the decoder in the stack
976 auto iter = stack.cbegin();
977 for (int i = 0; i < index; i++, iter++)
978 assert(iter != stack.end());
980 shared_ptr<Decoder> dec = *iter;
983 const bool show = !dec->shown();
986 assert(index < (int)decoder_forms_.size());
987 decoder_forms_[index]->set_decoder_visible(show);
990 owner_->row_item_appearance_changed(false, true);
993 } // namespace TraceView