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, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <libsigrokdecode/libsigrokdecode.h>
31 #include <boost/functional/hash.hpp>
32 #include <boost/thread/locks.hpp>
33 #include <boost/thread/shared_mutex.hpp>
36 #include <QApplication>
38 #include <QFormLayout>
41 #include <QPushButton>
44 #include "decodetrace.hpp"
46 #include <pv/session.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/logicsignal.hpp>
53 #include <pv/view/view.hpp>
54 #include <pv/view/viewport.hpp>
55 #include <pv/widgets/decodergroupbox.hpp>
56 #include <pv/widgets/decodermenu.hpp>
58 using boost::shared_lock;
59 using boost::shared_mutex;
60 using std::dynamic_pointer_cast;
62 using std::lock_guard;
69 using std::shared_ptr;
71 using std::unordered_set;
77 const QColor DecodeTrace::DecodeColours[4] = {
78 QColor(0xEF, 0x29, 0x29), // Red
79 QColor(0xFC, 0xE9, 0x4F), // Yellow
80 QColor(0x8A, 0xE2, 0x34), // Green
81 QColor(0x72, 0x9F, 0xCF) // Blue
84 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
85 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
87 const int DecodeTrace::ArrowSize = 4;
88 const double DecodeTrace::EndCapWidth = 5;
89 const int DecodeTrace::RowTitleMargin = 10;
90 const int DecodeTrace::DrawPadding = 100;
92 const QColor DecodeTrace::Colours[16] = {
93 QColor(0xEF, 0x29, 0x29),
94 QColor(0xF6, 0x6A, 0x32),
95 QColor(0xFC, 0xAE, 0x3E),
96 QColor(0xFB, 0xCA, 0x47),
97 QColor(0xFC, 0xE9, 0x4F),
98 QColor(0xCD, 0xF0, 0x40),
99 QColor(0x8A, 0xE2, 0x34),
100 QColor(0x4E, 0xDC, 0x44),
101 QColor(0x55, 0xD7, 0x95),
102 QColor(0x64, 0xD1, 0xD2),
103 QColor(0x72, 0x9F, 0xCF),
104 QColor(0xD4, 0x76, 0xC4),
105 QColor(0x9D, 0x79, 0xB9),
106 QColor(0xAD, 0x7F, 0xA8),
107 QColor(0xC2, 0x62, 0x9B),
108 QColor(0xD7, 0x47, 0x6F)
111 const QColor DecodeTrace::OutlineColours[16] = {
112 QColor(0x77, 0x14, 0x14),
113 QColor(0x7B, 0x35, 0x19),
114 QColor(0x7E, 0x57, 0x1F),
115 QColor(0x7D, 0x65, 0x23),
116 QColor(0x7E, 0x74, 0x27),
117 QColor(0x66, 0x78, 0x20),
118 QColor(0x45, 0x71, 0x1A),
119 QColor(0x27, 0x6E, 0x22),
120 QColor(0x2A, 0x6B, 0x4A),
121 QColor(0x32, 0x68, 0x69),
122 QColor(0x39, 0x4F, 0x67),
123 QColor(0x6A, 0x3B, 0x62),
124 QColor(0x4E, 0x3C, 0x5C),
125 QColor(0x56, 0x3F, 0x54),
126 QColor(0x61, 0x31, 0x4D),
127 QColor(0x6B, 0x23, 0x37)
130 DecodeTrace::DecodeTrace(pv::Session &session,
131 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
132 Trace(QString::fromUtf8(
133 decoder_stack->stack().front()->decoder()->name)),
135 decoder_stack_(decoder_stack),
137 max_visible_rows_(0),
138 delete_mapper_(this),
139 show_hide_mapper_(this)
141 assert(decoder_stack_);
143 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 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
160 return decoder_stack_;
163 pair<int, int> DecodeTrace::v_extents() const
165 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
167 return make_pair(-row_height, row_height * max_visible_rows_);
170 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
172 Trace::paint_back(p, pp);
173 paint_axis(p, pp, get_visual_y());
176 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
178 using namespace pv::data::decode;
180 const int text_height = ViewItemPaintParams::text_height();
181 row_height_ = (text_height * 6) / 4;
182 const int annotation_height = (text_height * 5) / 4;
184 assert(decoder_stack_);
185 const QString err = decoder_stack_->error_message();
186 if (!err.isEmpty()) {
187 draw_unresolved_period(
188 p, annotation_height, pp.left(), pp.right());
189 draw_error(p, err, pp);
193 // Iterate through the rows
194 int y = get_visual_y();
195 pair<uint64_t, uint64_t> sample_range = get_sample_range(
196 pp.left(), pp.right());
198 assert(decoder_stack_);
199 const vector<Row> rows(decoder_stack_->get_visible_rows());
201 visible_rows_.clear();
202 for (size_t i = 0; i < rows.size(); i++) {
203 const Row &row = rows[i];
205 // Cache the row title widths
208 row_title_width = row_title_widths_.at(row);
209 } catch (std::out_of_range) {
210 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
212 row_title_widths_[row] = w;
216 // Determine the row's color
217 size_t base_colour = 0x13579BDF;
218 boost::hash_combine(base_colour, this);
219 boost::hash_combine(base_colour, row.decoder());
220 boost::hash_combine(base_colour, row.row());
223 vector<Annotation> annotations;
224 decoder_stack_->get_annotation_subset(annotations, row,
225 sample_range.first, sample_range.second);
226 if (!annotations.empty()) {
227 draw_annotations(annotations, p, annotation_height, pp, y,
228 base_colour, row_title_width);
232 visible_rows_.push_back(rows[i]);
237 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
239 // Update the maximum row count if needed
240 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
243 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
245 using namespace pv::data::decode;
249 for (size_t i = 0; i < visible_rows_.size(); i++) {
250 const int y = i * row_height_ + get_visual_y();
252 p.setPen(QPen(Qt::NoPen));
253 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
256 const QPointF points[] = {
257 QPointF(pp.left(), y - ArrowSize),
258 QPointF(pp.left() + ArrowSize, y),
259 QPointF(pp.left(), y + ArrowSize)
261 p.drawPolygon(points, countof(points));
264 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
265 pp.right() - pp.left(), row_height_);
266 const QString h(visible_rows_[i].title());
267 const int f = Qt::AlignLeft | Qt::AlignVCenter |
271 p.setPen(QApplication::palette().color(QPalette::Base));
272 for (int dx = -1; dx <= 1; dx++)
273 for (int dy = -1; dy <= 1; dy++)
274 if (dx != 0 && dy != 0)
275 p.drawText(r.translated(dx, dy), f, h);
278 p.setPen(QApplication::palette().color(QPalette::WindowText));
283 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
285 using pv::data::decode::Decoder;
289 assert(decoder_stack_);
291 // Add the standard options
292 Trace::populate_popup_form(parent, form);
294 // Add the decoder options
296 channel_selectors_.clear();
297 decoder_forms_.clear();
299 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
302 QLabel *const l = new QLabel(
303 tr("<p><i>No decoders in the stack</i></p>"));
304 l->setAlignment(Qt::AlignCenter);
307 auto iter = stack.cbegin();
308 for (int i = 0; i < (int)stack.size(); i++, iter++) {
309 shared_ptr<Decoder> dec(*iter);
310 create_decoder_form(i, dec, parent, form);
313 form->addRow(new QLabel(
314 tr("<i>* Required channels</i>"), parent));
317 // Add stacking button
318 pv::widgets::DecoderMenu *const decoder_menu =
319 new pv::widgets::DecoderMenu(parent);
320 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
321 this, SLOT(on_stack_decoder(srd_decoder*)));
323 QPushButton *const stack_button =
324 new QPushButton(tr("Stack Decoder"), parent);
325 stack_button->setMenu(decoder_menu);
327 QHBoxLayout *stack_button_box = new QHBoxLayout;
328 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
329 form->addRow(stack_button_box);
332 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
334 QMenu *const menu = Trace::create_context_menu(parent);
336 menu->addSeparator();
338 QAction *const del = new QAction(tr("Delete"), this);
339 del->setShortcuts(QKeySequence::Delete);
340 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
341 menu->addAction(del);
346 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
347 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
348 size_t base_colour, int row_title_width)
350 using namespace pv::data::decode;
352 vector<Annotation> a_block;
355 double samples_per_pixel, pixels_offset;
356 tie(pixels_offset, samples_per_pixel) =
357 get_pixels_offset_samples_per_pixel();
359 // Sort the annotations by start sample so that decoders
360 // can't confuse us by creating annotations out of order
361 stable_sort(annotations.begin(), annotations.end(),
362 [](const Annotation &a, const Annotation &b) {
363 return a.start_sample() < b.start_sample(); });
365 // Gather all annotations that form a visual "block" and draw them as such
366 for (const Annotation &a : annotations) {
368 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
369 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
370 const int a_width = a_end - a_start;
372 const int delta = a_end - p_end;
374 bool a_is_separate = false;
376 // Annotation wider than the threshold for a useful label width?
378 for (const QString &ann_text : a.annotations()) {
379 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
380 // Annotation wide enough to fit a label? Don't put it in a block then
382 a_is_separate = true;
388 // Were the previous and this annotation more than a pixel apart?
389 if ((abs(delta) > 1) || a_is_separate) {
390 // Block was broken, draw annotations that form the current block
391 if (a_block.size() == 1) {
392 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
396 draw_annotation_block(a_block, p, h, y, base_colour);
402 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
403 // Next annotation must start a new block. delta will be > 1
404 // because we set p_end to INT_MIN but that's okay since
405 // a_block will be empty, so nothing will be drawn
408 a_block.push_back(a);
413 if (a_block.size() == 1)
414 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
417 draw_annotation_block(a_block, p, h, y, base_colour);
420 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
421 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
422 size_t base_colour, int row_title_width) const
424 double samples_per_pixel, pixels_offset;
425 tie(pixels_offset, samples_per_pixel) =
426 get_pixels_offset_samples_per_pixel();
428 const double start = a.start_sample() / samples_per_pixel -
430 const double end = a.end_sample() / samples_per_pixel -
433 const size_t colour = (base_colour + a.format()) % countof(Colours);
434 const QColor &fill = Colours[colour];
435 const QColor &outline = OutlineColours[colour];
437 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
440 if (a.start_sample() == a.end_sample())
441 draw_instant(a, p, fill, outline, h, start, y);
443 draw_range(a, p, fill, outline, h, start, end, y, pp,
447 void DecodeTrace::draw_annotation_block(
448 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
449 int y, size_t base_colour) const
451 using namespace pv::data::decode;
453 if (annotations.empty())
456 double samples_per_pixel, pixels_offset;
457 tie(pixels_offset, samples_per_pixel) =
458 get_pixels_offset_samples_per_pixel();
460 const double start = annotations.front().start_sample() /
461 samples_per_pixel - pixels_offset;
462 const double end = annotations.back().end_sample() /
463 samples_per_pixel - pixels_offset;
465 const double top = y + .5 - h / 2;
466 const double bottom = y + .5 + h / 2;
468 const size_t colour = (base_colour + annotations.front().format()) %
471 // Check if all annotations are of the same type (i.e. we can use one color)
472 // or if we should use a neutral color (i.e. gray)
473 const int format = annotations.front().format();
474 const bool single_format = std::all_of(
475 annotations.begin(), annotations.end(),
476 [&](const Annotation &a) { return a.format() == format; });
478 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
479 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
482 QRectF(start, top, end - start, bottom - top), h/4, h/4);
485 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
486 QColor fill, QColor outline, int h, double x, int y) const
488 const QString text = a.annotations().empty() ?
489 QString() : a.annotations().back();
490 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
492 const QRectF rect(x - w / 2, y - h / 2, w, h);
496 p.drawRoundedRect(rect, h / 2, h / 2);
499 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
502 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
503 QColor fill, QColor outline, int h, double start,
504 double end, int y, const ViewItemPaintParams &pp, int row_title_width) const
506 const double top = y + .5 - h / 2;
507 const double bottom = y + .5 + h / 2;
508 const vector<QString> annotations = a.annotations();
513 // If the two ends are within 1 pixel, draw a vertical line
514 if (start + 1.0 > end) {
515 p.drawLine(QPointF(start, top), QPointF(start, bottom));
519 const double cap_width = min((end - start) / 4, EndCapWidth);
522 QPointF(start, y + .5f),
523 QPointF(start + cap_width, top),
524 QPointF(end - cap_width, top),
525 QPointF(end, y + .5f),
526 QPointF(end - cap_width, bottom),
527 QPointF(start + cap_width, bottom)
530 p.drawConvexPolygon(pts, countof(pts));
532 if (annotations.empty())
535 const int ann_start = start + cap_width;
536 const int ann_end = end - cap_width;
538 const int real_start = std::max(ann_start, pp.left() + row_title_width);
539 const int real_end = std::min(ann_end, pp.right());
540 const int real_width = real_end - real_start;
542 QRectF rect(real_start, y - h / 2, real_width, h);
543 if (rect.width() <= 4)
548 // Try to find an annotation that will fit
549 QString best_annotation;
552 for (const QString &a : annotations) {
553 const int w = p.boundingRect(QRectF(), 0, a).width();
554 if (w <= rect.width() && w > best_width)
555 best_annotation = a, best_width = w;
558 if (best_annotation.isEmpty())
559 best_annotation = annotations.back();
561 // If not ellide the last in the list
562 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
563 best_annotation, Qt::ElideRight, rect.width()));
566 void DecodeTrace::draw_error(QPainter &p, const QString &message,
567 const ViewItemPaintParams &pp)
569 const int y = get_visual_y();
571 p.setPen(ErrorBgColour.darker());
572 p.setBrush(ErrorBgColour);
574 const QRectF bounding_rect =
575 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
576 const QRectF text_rect = p.boundingRect(bounding_rect,
577 Qt::AlignCenter, message);
578 const float r = text_rect.height() / 4;
580 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
584 p.drawText(text_rect, message);
587 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
590 using namespace pv::data;
591 using pv::data::decode::Decoder;
593 double samples_per_pixel, pixels_offset;
595 assert(decoder_stack_);
597 shared_ptr<Logic> data;
598 shared_ptr<LogicSignal> logic_signal;
600 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
602 // We get the logic data of the first channel in the list.
603 // This works because we are currently assuming all
604 // LogicSignals have the same data/segment
605 for (const shared_ptr<Decoder> &dec : stack)
606 if (dec && !dec->channels().empty() &&
607 ((logic_signal = (*dec->channels().begin()).second)) &&
608 ((data = logic_signal->logic_data())))
611 if (!data || data->logic_segments().empty())
614 const shared_ptr<LogicSegment> segment =
615 data->logic_segments().front();
617 const int64_t sample_count = (int64_t)segment->get_sample_count();
618 if (sample_count == 0)
621 const int64_t samples_decoded = decoder_stack_->samples_decoded();
622 if (sample_count == samples_decoded)
625 const int y = get_visual_y();
627 tie(pixels_offset, samples_per_pixel) =
628 get_pixels_offset_samples_per_pixel();
630 const double start = max(samples_decoded /
631 samples_per_pixel - pixels_offset, left - 1.0);
632 const double end = min(sample_count / samples_per_pixel -
633 pixels_offset, right + 1.0);
634 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
636 p.setPen(QPen(Qt::NoPen));
637 p.setBrush(Qt::white);
638 p.drawRect(no_decode_rect);
640 p.setPen(NoDecodeColour);
641 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
642 p.drawRect(no_decode_rect);
645 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
648 assert(decoder_stack_);
650 const View *view = owner_->view();
653 const double scale = view->scale();
656 const double pixels_offset =
657 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
659 double samplerate = decoder_stack_->samplerate();
661 // Show sample rate as 1Hz when it is unknown
662 if (samplerate == 0.0)
665 return make_pair(pixels_offset, samplerate * scale);
668 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
669 int x_start, int x_end) const
671 double samples_per_pixel, pixels_offset;
672 tie(pixels_offset, samples_per_pixel) =
673 get_pixels_offset_samples_per_pixel();
675 const uint64_t start = (uint64_t)max(
676 (x_start + pixels_offset) * samples_per_pixel, 0.0);
677 const uint64_t end = (uint64_t)max(
678 (x_end + pixels_offset) * samples_per_pixel, 0.0);
680 return make_pair(start, end);
683 int DecodeTrace::get_row_at_point(const QPoint &point)
688 const int y = (point.y() - get_visual_y() + row_height_ / 2);
690 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
694 const int row = y / row_height_;
696 if (row >= (int)visible_rows_.size())
702 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
704 using namespace pv::data::decode;
709 const pair<uint64_t, uint64_t> sample_range =
710 get_sample_range(point.x(), point.x() + 1);
711 const int row = get_row_at_point(point);
715 vector<pv::data::decode::Annotation> annotations;
717 assert(decoder_stack_);
718 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
719 sample_range.first, sample_range.second);
721 return (annotations.empty()) ?
722 QString() : annotations[0].annotations().front();
725 void DecodeTrace::hover_point_changed()
729 const View *const view = owner_->view();
732 QPoint hp = view->hover_point();
733 QString ann = get_annotation_at_point(hp);
737 if (!row_height_ || ann.isEmpty()) {
738 QToolTip::hideText();
742 const int hover_row = get_row_at_point(hp);
744 QFontMetrics m(QToolTip::font());
745 const QRect text_size = m.boundingRect(QRect(), 0, ann);
747 // This is OS-specific and unfortunately we can't query it, so
748 // use an approximation to at least try to minimize the error.
749 const int padding = 8;
751 // Make sure the tool tip doesn't overlap with the mouse cursor.
752 // If it did, the tool tip would constantly hide and re-appear.
753 // We also push it up by one row so that it appears above the
754 // decode trace, not below.
755 hp.setX(hp.x() - (text_size.width() / 2) - padding);
757 hp.setY(get_visual_y() - (row_height_ / 2) +
758 (hover_row * row_height_) -
759 row_height_ - text_size.height() - padding);
761 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
764 void DecodeTrace::create_decoder_form(int index,
765 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
771 const srd_decoder *const decoder = dec->decoder();
774 const bool decoder_deletable = index > 0;
776 pv::widgets::DecoderGroupBox *const group =
777 new pv::widgets::DecoderGroupBox(
778 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
779 group->set_decoder_visible(dec->shown());
781 if (decoder_deletable) {
782 delete_mapper_.setMapping(group, index);
783 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
786 show_hide_mapper_.setMapping(group, index);
787 connect(group, SIGNAL(show_hide_decoder()),
788 &show_hide_mapper_, SLOT(map()));
790 QFormLayout *const decoder_form = new QFormLayout;
791 group->add_layout(decoder_form);
793 // Add the mandatory channels
794 for (l = decoder->channels; l; l = l->next) {
795 const struct srd_channel *const pdch =
796 (struct srd_channel *)l->data;
797 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
798 connect(combo, SIGNAL(currentIndexChanged(int)),
799 this, SLOT(on_channel_selected(int)));
800 decoder_form->addRow(tr("<b>%1</b> (%2) *")
801 .arg(QString::fromUtf8(pdch->name))
802 .arg(QString::fromUtf8(pdch->desc)), combo);
804 const ChannelSelector s = {combo, dec, pdch};
805 channel_selectors_.push_back(s);
808 // Add the optional channels
809 for (l = decoder->opt_channels; l; l = l->next) {
810 const struct srd_channel *const pdch =
811 (struct srd_channel *)l->data;
812 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
813 connect(combo, SIGNAL(currentIndexChanged(int)),
814 this, SLOT(on_channel_selected(int)));
815 decoder_form->addRow(tr("<b>%1</b> (%2)")
816 .arg(QString::fromUtf8(pdch->name))
817 .arg(QString::fromUtf8(pdch->desc)), combo);
819 const ChannelSelector s = {combo, dec, pdch};
820 channel_selectors_.push_back(s);
824 shared_ptr<binding::Decoder> binding(
825 new binding::Decoder(decoder_stack_, dec));
826 binding->add_properties_to_form(decoder_form, true);
828 bindings_.push_back(binding);
831 decoder_forms_.push_back(group);
834 QComboBox* DecodeTrace::create_channel_selector(
835 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
836 const srd_channel *const pdch)
840 const auto sigs(session_.signals());
842 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
843 std::sort(sig_list.begin(), sig_list.end(),
844 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
845 return a->name().compare(b->name()) < 0; });
847 assert(decoder_stack_);
848 const auto channel_iter = dec->channels().find(pdch);
850 QComboBox *selector = new QComboBox(parent);
852 selector->addItem("-", qVariantFromValue((void*)nullptr));
854 if (channel_iter == dec->channels().end())
855 selector->setCurrentIndex(0);
857 for (const shared_ptr<view::Signal> &s : sig_list) {
859 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
860 selector->addItem(s->name(),
861 qVariantFromValue((void*)s.get()));
863 if (channel_iter != dec->channels().end() &&
864 (*channel_iter).second == s)
865 selector->setCurrentIndex(
866 selector->count() - 1);
873 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
877 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
879 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
881 for (const ChannelSelector &s : channel_selectors_) {
882 if (s.decoder_ != dec)
885 const LogicSignal *const selection =
886 (LogicSignal*)s.combo_->itemData(
887 s.combo_->currentIndex()).value<void*>();
889 for (shared_ptr<Signal> sig : sigs)
890 if (sig.get() == selection) {
891 channel_map[s.pdch_] =
892 dynamic_pointer_cast<LogicSignal>(sig);
897 dec->set_channels(channel_map);
900 void DecodeTrace::commit_channels()
902 assert(decoder_stack_);
903 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
904 commit_decoder_channels(dec);
906 decoder_stack_->begin_decode();
909 void DecodeTrace::on_new_decode_data()
912 owner_->row_item_appearance_changed(false, true);
915 void DecodeTrace::delete_pressed()
920 void DecodeTrace::on_delete()
922 session_.remove_decode_signal(this);
925 void DecodeTrace::on_channel_selected(int)
930 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
933 assert(decoder_stack_);
934 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
935 new data::decode::Decoder(decoder)));
936 decoder_stack_->begin_decode();
941 void DecodeTrace::on_delete_decoder(int index)
943 decoder_stack_->remove(index);
948 decoder_stack_->begin_decode();
951 void DecodeTrace::on_show_hide_decoder(int index)
953 using pv::data::decode::Decoder;
955 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
957 // Find the decoder in the stack
958 auto iter = stack.cbegin();
959 for (int i = 0; i < index; i++, iter++)
960 assert(iter != stack.end());
962 shared_ptr<Decoder> dec = *iter;
965 const bool show = !dec->shown();
968 assert(index < (int)decoder_forms_.size());
969 decoder_forms_[index]->set_decoder_visible(show);
972 owner_->row_item_appearance_changed(false, true);