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::DrawPadding = 100;
91 const QColor DecodeTrace::Colours[16] = {
92 QColor(0xEF, 0x29, 0x29),
93 QColor(0xF6, 0x6A, 0x32),
94 QColor(0xFC, 0xAE, 0x3E),
95 QColor(0xFB, 0xCA, 0x47),
96 QColor(0xFC, 0xE9, 0x4F),
97 QColor(0xCD, 0xF0, 0x40),
98 QColor(0x8A, 0xE2, 0x34),
99 QColor(0x4E, 0xDC, 0x44),
100 QColor(0x55, 0xD7, 0x95),
101 QColor(0x64, 0xD1, 0xD2),
102 QColor(0x72, 0x9F, 0xCF),
103 QColor(0xD4, 0x76, 0xC4),
104 QColor(0x9D, 0x79, 0xB9),
105 QColor(0xAD, 0x7F, 0xA8),
106 QColor(0xC2, 0x62, 0x9B),
107 QColor(0xD7, 0x47, 0x6F)
110 const QColor DecodeTrace::OutlineColours[16] = {
111 QColor(0x77, 0x14, 0x14),
112 QColor(0x7B, 0x35, 0x19),
113 QColor(0x7E, 0x57, 0x1F),
114 QColor(0x7D, 0x65, 0x23),
115 QColor(0x7E, 0x74, 0x27),
116 QColor(0x66, 0x78, 0x20),
117 QColor(0x45, 0x71, 0x1A),
118 QColor(0x27, 0x6E, 0x22),
119 QColor(0x2A, 0x6B, 0x4A),
120 QColor(0x32, 0x68, 0x69),
121 QColor(0x39, 0x4F, 0x67),
122 QColor(0x6A, 0x3B, 0x62),
123 QColor(0x4E, 0x3C, 0x5C),
124 QColor(0x56, 0x3F, 0x54),
125 QColor(0x61, 0x31, 0x4D),
126 QColor(0x6B, 0x23, 0x37)
129 DecodeTrace::DecodeTrace(pv::Session &session,
130 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
131 Trace(QString::fromUtf8(
132 decoder_stack->stack().front()->decoder()->name)),
134 decoder_stack_(decoder_stack),
136 delete_mapper_(this),
137 show_hide_mapper_(this)
139 assert(decoder_stack_);
141 set_colour(DecodeColours[index % countof(DecodeColours)]);
143 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
144 this, SLOT(on_new_decode_data()));
145 connect(&delete_mapper_, SIGNAL(mapped(int)),
146 this, SLOT(on_delete_decoder(int)));
147 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
148 this, SLOT(on_show_hide_decoder(int)));
151 bool DecodeTrace::enabled() const
156 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
158 return decoder_stack_;
161 pair<int, int> DecodeTrace::v_extents() const
163 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
164 const int rows = visible_rows_.size();
166 return make_pair(-row_height, row_height * rows);
169 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
171 Trace::paint_back(p, pp);
172 paint_axis(p, pp, get_visual_y());
175 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
177 using namespace pv::data::decode;
179 const int text_height = ViewItemPaintParams::text_height();
180 row_height_ = (text_height * 6) / 4;
181 const int annotation_height = (text_height * 5) / 4;
183 assert(decoder_stack_);
184 const QString err = decoder_stack_->error_message();
185 if (!err.isEmpty()) {
186 draw_unresolved_period(
187 p, annotation_height, pp.left(), pp.right());
188 draw_error(p, err, pp);
192 // Iterate through the rows
193 int y = get_visual_y();
194 pair<uint64_t, uint64_t> sample_range = get_sample_range(
195 pp.left(), pp.right());
197 assert(decoder_stack_);
198 const vector<Row> rows(decoder_stack_->get_visible_rows());
200 visible_rows_.clear();
201 for (size_t i = 0; i < rows.size(); i++) {
202 const Row &row = rows[i];
204 size_t base_colour = 0x13579BDF;
205 boost::hash_combine(base_colour, this);
206 boost::hash_combine(base_colour, row.decoder());
207 boost::hash_combine(base_colour, row.row());
210 vector<Annotation> annotations;
211 decoder_stack_->get_annotation_subset(annotations, row,
212 sample_range.first, sample_range.second);
213 if (!annotations.empty()) {
214 draw_annotations(annotations, p, annotation_height, pp, y,
219 visible_rows_.push_back(rows[i]);
224 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
227 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
229 using namespace pv::data::decode;
233 for (size_t i = 0; i < visible_rows_.size(); i++) {
234 const int y = i * row_height_ + get_visual_y();
236 p.setPen(QPen(Qt::NoPen));
237 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
240 const QPointF points[] = {
241 QPointF(pp.left(), y - ArrowSize),
242 QPointF(pp.left() + ArrowSize, y),
243 QPointF(pp.left(), y + ArrowSize)
245 p.drawPolygon(points, countof(points));
248 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
249 pp.right() - pp.left(), row_height_);
250 const QString h(visible_rows_[i].title());
251 const int f = Qt::AlignLeft | Qt::AlignVCenter |
255 p.setPen(QApplication::palette().color(QPalette::Base));
256 for (int dx = -1; dx <= 1; dx++)
257 for (int dy = -1; dy <= 1; dy++)
258 if (dx != 0 && dy != 0)
259 p.drawText(r.translated(dx, dy), f, h);
262 p.setPen(QApplication::palette().color(QPalette::WindowText));
267 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
269 using pv::data::decode::Decoder;
273 assert(decoder_stack_);
275 // Add the standard options
276 Trace::populate_popup_form(parent, form);
278 // Add the decoder options
280 channel_selectors_.clear();
281 decoder_forms_.clear();
283 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
286 QLabel *const l = new QLabel(
287 tr("<p><i>No decoders in the stack</i></p>"));
288 l->setAlignment(Qt::AlignCenter);
291 auto iter = stack.cbegin();
292 for (int i = 0; i < (int)stack.size(); i++, iter++) {
293 shared_ptr<Decoder> dec(*iter);
294 create_decoder_form(i, dec, parent, form);
297 form->addRow(new QLabel(
298 tr("<i>* Required channels</i>"), parent));
301 // Add stacking button
302 pv::widgets::DecoderMenu *const decoder_menu =
303 new pv::widgets::DecoderMenu(parent);
304 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
305 this, SLOT(on_stack_decoder(srd_decoder*)));
307 QPushButton *const stack_button =
308 new QPushButton(tr("Stack Decoder"), parent);
309 stack_button->setMenu(decoder_menu);
311 QHBoxLayout *stack_button_box = new QHBoxLayout;
312 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
313 form->addRow(stack_button_box);
316 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
318 QMenu *const menu = Trace::create_context_menu(parent);
320 menu->addSeparator();
322 QAction *const del = new QAction(tr("Delete"), this);
323 del->setShortcuts(QKeySequence::Delete);
324 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
325 menu->addAction(del);
330 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
331 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
334 using namespace pv::data::decode;
336 vector<Annotation> a_block;
337 int prev_ann_pos = INT_MIN;
339 double samples_per_pixel, pixels_offset;
340 tie(pixels_offset, samples_per_pixel) =
341 get_pixels_offset_samples_per_pixel();
343 // Gather all annotations that form a visual "block" and draw them as such
344 for (const Annotation &a : annotations) {
346 const int end = a.end_sample() / samples_per_pixel - pixels_offset;
347 const int delta = end - prev_ann_pos;
349 // Some annotations are in reverse order, so we cannot
350 // simply check for delta > 1
351 if (abs(delta) > 1) {
352 // Block was broken, draw it
353 if (a_block.size() == 1)
354 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
356 if (a_block.size() > 0)
357 draw_annotation_block(a_block, p, h, y, base_colour);
362 a_block.push_back(a);
366 if (a_block.size() == 1)
367 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
369 draw_annotation_block(a_block, p, h, y, base_colour);
372 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
373 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
374 size_t base_colour) const
376 double samples_per_pixel, pixels_offset;
377 tie(pixels_offset, samples_per_pixel) =
378 get_pixels_offset_samples_per_pixel();
380 const double start = a.start_sample() / samples_per_pixel -
382 const double end = a.end_sample() / samples_per_pixel -
385 const size_t colour = (base_colour + a.format()) % countof(Colours);
386 const QColor &fill = Colours[colour];
387 const QColor &outline = OutlineColours[colour];
389 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
392 if (a.start_sample() == a.end_sample())
393 draw_instant(a, p, fill, outline, h, start, y);
395 draw_range(a, p, fill, outline, h, start, end, y);
398 void DecodeTrace::draw_annotation_block(
399 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
400 int y, size_t base_colour) const
402 using namespace pv::data::decode;
404 double samples_per_pixel, pixels_offset;
405 tie(pixels_offset, samples_per_pixel) =
406 get_pixels_offset_samples_per_pixel();
408 const double start = annotations.front().start_sample() /
409 samples_per_pixel - pixels_offset;
410 const double end = annotations.back().end_sample() /
411 samples_per_pixel - pixels_offset;
413 const double top = y + .5 - h / 2;
414 const double bottom = y + .5 + h / 2;
415 const double cap_width = min((end - start) / 4, EndCapWidth);
418 QPointF(start, y + .5f),
419 QPointF(start + cap_width, top),
420 QPointF(end - cap_width, top),
421 QPointF(end, y + .5f),
422 QPointF(end - cap_width, bottom),
423 QPointF(start + cap_width, bottom)
426 const size_t colour = (base_colour + annotations.front().format()) %
429 // Check if all annotations are of the same type (i.e. we can use one color)
430 // or if we should use a neutral color (i.e. gray)
431 bool single_format = true;
432 int format = annotations.front().format();
434 for (const Annotation &a : annotations)
435 if (a.format() != format) {
436 single_format = false;
440 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
441 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
444 p.drawConvexPolygon(pts, countof(pts));
447 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
448 QColor fill, QColor outline, int h, double x, int y) const
450 const QString text = a.annotations().empty() ?
451 QString() : a.annotations().back();
452 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
454 const QRectF rect(x - w / 2, y - h / 2, w, h);
458 p.drawRoundedRect(rect, h / 2, h / 2);
461 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
464 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
465 QColor fill, QColor outline, int h, double start,
466 double end, int y) const
468 const double top = y + .5 - h / 2;
469 const double bottom = y + .5 + h / 2;
470 const vector<QString> annotations = a.annotations();
475 // If the two ends are within 1 pixel, draw a vertical line
476 if (start + 1.0 > end) {
477 p.drawLine(QPointF(start, top), QPointF(start, bottom));
481 const double cap_width = min((end - start) / 4, EndCapWidth);
484 QPointF(start, y + .5f),
485 QPointF(start + cap_width, top),
486 QPointF(end - cap_width, top),
487 QPointF(end, y + .5f),
488 QPointF(end - cap_width, bottom),
489 QPointF(start + cap_width, bottom)
492 p.drawConvexPolygon(pts, countof(pts));
494 if (annotations.empty())
497 QRectF rect(start + cap_width, y - h / 2,
498 end - start - cap_width * 2, h);
499 if (rect.width() <= 4)
504 // Try to find an annotation that will fit
505 QString best_annotation;
508 for (const QString &a : annotations) {
509 const int w = p.boundingRect(QRectF(), 0, a).width();
510 if (w <= rect.width() && w > best_width)
511 best_annotation = a, best_width = w;
514 if (best_annotation.isEmpty())
515 best_annotation = annotations.back();
517 // If not ellide the last in the list
518 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
519 best_annotation, Qt::ElideRight, rect.width()));
522 void DecodeTrace::draw_error(QPainter &p, const QString &message,
523 const ViewItemPaintParams &pp)
525 const int y = get_visual_y();
527 p.setPen(ErrorBgColour.darker());
528 p.setBrush(ErrorBgColour);
530 const QRectF bounding_rect =
531 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
532 const QRectF text_rect = p.boundingRect(bounding_rect,
533 Qt::AlignCenter, message);
534 const float r = text_rect.height() / 4;
536 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
540 p.drawText(text_rect, message);
543 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
546 using namespace pv::data;
547 using pv::data::decode::Decoder;
549 double samples_per_pixel, pixels_offset;
551 assert(decoder_stack_);
553 shared_ptr<Logic> data;
554 shared_ptr<LogicSignal> logic_signal;
556 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
558 // We get the logic data of the first channel in the list.
559 // This works because we are currently assuming all
560 // LogicSignals have the same data/segment
561 for (const shared_ptr<Decoder> &dec : stack)
562 if (dec && !dec->channels().empty() &&
563 ((logic_signal = (*dec->channels().begin()).second)) &&
564 ((data = logic_signal->logic_data())))
567 if (!data || data->logic_segments().empty())
570 const shared_ptr<LogicSegment> segment =
571 data->logic_segments().front();
573 const int64_t sample_count = (int64_t)segment->get_sample_count();
574 if (sample_count == 0)
577 const int64_t samples_decoded = decoder_stack_->samples_decoded();
578 if (sample_count == samples_decoded)
581 const int y = get_visual_y();
583 tie(pixels_offset, samples_per_pixel) =
584 get_pixels_offset_samples_per_pixel();
586 const double start = max(samples_decoded /
587 samples_per_pixel - pixels_offset, left - 1.0);
588 const double end = min(sample_count / samples_per_pixel -
589 pixels_offset, right + 1.0);
590 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
592 p.setPen(QPen(Qt::NoPen));
593 p.setBrush(Qt::white);
594 p.drawRect(no_decode_rect);
596 p.setPen(NoDecodeColour);
597 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
598 p.drawRect(no_decode_rect);
601 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
604 assert(decoder_stack_);
606 const View *view = owner_->view();
609 const double scale = view->scale();
612 const double pixels_offset =
613 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
615 double samplerate = decoder_stack_->samplerate();
617 // Show sample rate as 1Hz when it is unknown
618 if (samplerate == 0.0)
621 return make_pair(pixels_offset, samplerate * scale);
624 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
625 int x_start, int x_end) const
627 double samples_per_pixel, pixels_offset;
628 tie(pixels_offset, samples_per_pixel) =
629 get_pixels_offset_samples_per_pixel();
631 const uint64_t start = (uint64_t)max(
632 (x_start + pixels_offset) * samples_per_pixel, 0.0);
633 const uint64_t end = (uint64_t)max(
634 (x_end + pixels_offset) * samples_per_pixel, 0.0);
636 return make_pair(start, end);
639 int DecodeTrace::get_row_at_point(const QPoint &point)
644 const int y = (point.y() - get_visual_y() + row_height_ / 2);
646 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
650 const int row = y / row_height_;
652 if (row >= (int)visible_rows_.size())
658 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
660 using namespace pv::data::decode;
665 const pair<uint64_t, uint64_t> sample_range =
666 get_sample_range(point.x(), point.x() + 1);
667 const int row = get_row_at_point(point);
671 vector<pv::data::decode::Annotation> annotations;
673 assert(decoder_stack_);
674 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
675 sample_range.first, sample_range.second);
677 return (annotations.empty()) ?
678 QString() : annotations[0].annotations().front();
681 void DecodeTrace::hover_point_changed()
685 const View *const view = owner_->view();
688 QPoint hp = view->hover_point();
689 QString ann = get_annotation_at_point(hp);
693 if (!row_height_ || ann.isEmpty()) {
694 QToolTip::hideText();
698 const int hover_row = get_row_at_point(hp);
700 QFontMetrics m(QToolTip::font());
701 const QRect text_size = m.boundingRect(QRect(), 0, ann);
703 // This is OS-specific and unfortunately we can't query it, so
704 // use an approximation to at least try to minimize the error.
705 const int padding = 8;
707 // Make sure the tool tip doesn't overlap with the mouse cursor.
708 // If it did, the tool tip would constantly hide and re-appear.
709 // We also push it up by one row so that it appears above the
710 // decode trace, not below.
711 hp.setX(hp.x() - (text_size.width() / 2) - padding);
713 hp.setY(get_visual_y() - (row_height_ / 2) +
714 (hover_row * row_height_) -
715 row_height_ - text_size.height() - padding);
717 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
720 void DecodeTrace::create_decoder_form(int index,
721 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
727 const srd_decoder *const decoder = dec->decoder();
730 const bool decoder_deletable = index > 0;
732 pv::widgets::DecoderGroupBox *const group =
733 new pv::widgets::DecoderGroupBox(
734 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
735 group->set_decoder_visible(dec->shown());
737 if (decoder_deletable) {
738 delete_mapper_.setMapping(group, index);
739 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
742 show_hide_mapper_.setMapping(group, index);
743 connect(group, SIGNAL(show_hide_decoder()),
744 &show_hide_mapper_, SLOT(map()));
746 QFormLayout *const decoder_form = new QFormLayout;
747 group->add_layout(decoder_form);
749 // Add the mandatory channels
750 for (l = decoder->channels; l; l = l->next) {
751 const struct srd_channel *const pdch =
752 (struct srd_channel *)l->data;
753 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
754 connect(combo, SIGNAL(currentIndexChanged(int)),
755 this, SLOT(on_channel_selected(int)));
756 decoder_form->addRow(tr("<b>%1</b> (%2) *")
757 .arg(QString::fromUtf8(pdch->name))
758 .arg(QString::fromUtf8(pdch->desc)), combo);
760 const ChannelSelector s = {combo, dec, pdch};
761 channel_selectors_.push_back(s);
764 // Add the optional channels
765 for (l = decoder->opt_channels; l; l = l->next) {
766 const struct srd_channel *const pdch =
767 (struct srd_channel *)l->data;
768 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
769 connect(combo, SIGNAL(currentIndexChanged(int)),
770 this, SLOT(on_channel_selected(int)));
771 decoder_form->addRow(tr("<b>%1</b> (%2)")
772 .arg(QString::fromUtf8(pdch->name))
773 .arg(QString::fromUtf8(pdch->desc)), combo);
775 const ChannelSelector s = {combo, dec, pdch};
776 channel_selectors_.push_back(s);
780 shared_ptr<binding::Decoder> binding(
781 new binding::Decoder(decoder_stack_, dec));
782 binding->add_properties_to_form(decoder_form, true);
784 bindings_.push_back(binding);
787 decoder_forms_.push_back(group);
790 QComboBox* DecodeTrace::create_channel_selector(
791 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
792 const srd_channel *const pdch)
796 const auto sigs(session_.signals());
798 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
799 std::sort(sig_list.begin(), sig_list.end(),
800 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
801 return a->name().compare(b->name()) < 0; });
803 assert(decoder_stack_);
804 const auto channel_iter = dec->channels().find(pdch);
806 QComboBox *selector = new QComboBox(parent);
808 selector->addItem("-", qVariantFromValue((void*)nullptr));
810 if (channel_iter == dec->channels().end())
811 selector->setCurrentIndex(0);
813 for (const shared_ptr<view::Signal> &s : sig_list) {
815 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
816 selector->addItem(s->name(),
817 qVariantFromValue((void*)s.get()));
819 if (channel_iter != dec->channels().end() &&
820 (*channel_iter).second == s)
821 selector->setCurrentIndex(
822 selector->count() - 1);
829 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
833 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
835 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
837 for (const ChannelSelector &s : channel_selectors_) {
838 if (s.decoder_ != dec)
841 const LogicSignal *const selection =
842 (LogicSignal*)s.combo_->itemData(
843 s.combo_->currentIndex()).value<void*>();
845 for (shared_ptr<Signal> sig : sigs)
846 if (sig.get() == selection) {
847 channel_map[s.pdch_] =
848 dynamic_pointer_cast<LogicSignal>(sig);
853 dec->set_channels(channel_map);
856 void DecodeTrace::commit_channels()
858 assert(decoder_stack_);
859 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
860 commit_decoder_channels(dec);
862 decoder_stack_->begin_decode();
865 void DecodeTrace::on_new_decode_data()
868 owner_->row_item_appearance_changed(false, true);
871 void DecodeTrace::delete_pressed()
876 void DecodeTrace::on_delete()
878 session_.remove_decode_signal(this);
881 void DecodeTrace::on_channel_selected(int)
886 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
889 assert(decoder_stack_);
890 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
891 new data::decode::Decoder(decoder)));
892 decoder_stack_->begin_decode();
897 void DecodeTrace::on_delete_decoder(int index)
899 decoder_stack_->remove(index);
904 decoder_stack_->begin_decode();
907 void DecodeTrace::on_show_hide_decoder(int index)
909 using pv::data::decode::Decoder;
911 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
913 // Find the decoder in the stack
914 auto iter = stack.cbegin();
915 for (int i = 0; i < index; i++, iter++)
916 assert(iter != stack.end());
918 shared_ptr<Decoder> dec = *iter;
921 const bool show = !dec->shown();
924 assert(index < (int)decoder_forms_.size());
925 decoder_forms_[index]->set_decoder_visible(show);
928 owner_->row_item_appearance_changed(false, true);