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 max_visible_rows_(0),
137 delete_mapper_(this),
138 show_hide_mapper_(this)
140 assert(decoder_stack_);
142 set_colour(DecodeColours[index % countof(DecodeColours)]);
144 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
145 this, SLOT(on_new_decode_data()));
146 connect(&delete_mapper_, SIGNAL(mapped(int)),
147 this, SLOT(on_delete_decoder(int)));
148 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
149 this, SLOT(on_show_hide_decoder(int)));
152 bool DecodeTrace::enabled() const
157 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
159 return decoder_stack_;
162 pair<int, int> DecodeTrace::v_extents() const
164 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
166 return make_pair(-row_height, row_height * max_visible_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());
226 // Update the maximum row count if needed
227 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
230 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
232 using namespace pv::data::decode;
236 for (size_t i = 0; i < visible_rows_.size(); i++) {
237 const int y = i * row_height_ + get_visual_y();
239 p.setPen(QPen(Qt::NoPen));
240 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
243 const QPointF points[] = {
244 QPointF(pp.left(), y - ArrowSize),
245 QPointF(pp.left() + ArrowSize, y),
246 QPointF(pp.left(), y + ArrowSize)
248 p.drawPolygon(points, countof(points));
251 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
252 pp.right() - pp.left(), row_height_);
253 const QString h(visible_rows_[i].title());
254 const int f = Qt::AlignLeft | Qt::AlignVCenter |
258 p.setPen(QApplication::palette().color(QPalette::Base));
259 for (int dx = -1; dx <= 1; dx++)
260 for (int dy = -1; dy <= 1; dy++)
261 if (dx != 0 && dy != 0)
262 p.drawText(r.translated(dx, dy), f, h);
265 p.setPen(QApplication::palette().color(QPalette::WindowText));
270 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
272 using pv::data::decode::Decoder;
276 assert(decoder_stack_);
278 // Add the standard options
279 Trace::populate_popup_form(parent, form);
281 // Add the decoder options
283 channel_selectors_.clear();
284 decoder_forms_.clear();
286 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
289 QLabel *const l = new QLabel(
290 tr("<p><i>No decoders in the stack</i></p>"));
291 l->setAlignment(Qt::AlignCenter);
294 auto iter = stack.cbegin();
295 for (int i = 0; i < (int)stack.size(); i++, iter++) {
296 shared_ptr<Decoder> dec(*iter);
297 create_decoder_form(i, dec, parent, form);
300 form->addRow(new QLabel(
301 tr("<i>* Required channels</i>"), parent));
304 // Add stacking button
305 pv::widgets::DecoderMenu *const decoder_menu =
306 new pv::widgets::DecoderMenu(parent);
307 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
308 this, SLOT(on_stack_decoder(srd_decoder*)));
310 QPushButton *const stack_button =
311 new QPushButton(tr("Stack Decoder"), parent);
312 stack_button->setMenu(decoder_menu);
314 QHBoxLayout *stack_button_box = new QHBoxLayout;
315 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
316 form->addRow(stack_button_box);
319 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
321 QMenu *const menu = Trace::create_context_menu(parent);
323 menu->addSeparator();
325 QAction *const del = new QAction(tr("Delete"), this);
326 del->setShortcuts(QKeySequence::Delete);
327 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
328 menu->addAction(del);
333 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
334 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
337 using namespace pv::data::decode;
339 vector<Annotation> a_block;
340 int prev_ann_pos = INT_MIN;
342 double samples_per_pixel, pixels_offset;
343 tie(pixels_offset, samples_per_pixel) =
344 get_pixels_offset_samples_per_pixel();
346 // Gather all annotations that form a visual "block" and draw them as such
347 for (const Annotation &a : annotations) {
349 const int end = a.end_sample() / samples_per_pixel - pixels_offset;
350 const int delta = end - prev_ann_pos;
352 // Some annotations are in reverse order, so we cannot
353 // simply check for delta > 1
354 if (abs(delta) > 1) {
355 // Block was broken, draw it
356 if (a_block.size() == 1)
357 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
359 if (a_block.size() > 0)
360 draw_annotation_block(a_block, p, h, y, base_colour);
365 a_block.push_back(a);
369 if (a_block.size() == 1)
370 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
372 draw_annotation_block(a_block, p, h, y, base_colour);
375 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
376 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
377 size_t base_colour) const
379 double samples_per_pixel, pixels_offset;
380 tie(pixels_offset, samples_per_pixel) =
381 get_pixels_offset_samples_per_pixel();
383 const double start = a.start_sample() / samples_per_pixel -
385 const double end = a.end_sample() / samples_per_pixel -
388 const size_t colour = (base_colour + a.format()) % countof(Colours);
389 const QColor &fill = Colours[colour];
390 const QColor &outline = OutlineColours[colour];
392 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
395 if (a.start_sample() == a.end_sample())
396 draw_instant(a, p, fill, outline, h, start, y);
398 draw_range(a, p, fill, outline, h, start, end, y);
401 void DecodeTrace::draw_annotation_block(
402 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
403 int y, size_t base_colour) const
405 using namespace pv::data::decode;
407 double samples_per_pixel, pixels_offset;
408 tie(pixels_offset, samples_per_pixel) =
409 get_pixels_offset_samples_per_pixel();
411 const double start = annotations.front().start_sample() /
412 samples_per_pixel - pixels_offset;
413 const double end = annotations.back().end_sample() /
414 samples_per_pixel - pixels_offset;
416 const double top = y + .5 - h / 2;
417 const double bottom = y + .5 + h / 2;
418 const double cap_width = min((end - start) / 4, EndCapWidth);
421 QPointF(start, y + .5f),
422 QPointF(start + cap_width, top),
423 QPointF(end - cap_width, top),
424 QPointF(end, y + .5f),
425 QPointF(end - cap_width, bottom),
426 QPointF(start + cap_width, bottom)
429 const size_t colour = (base_colour + annotations.front().format()) %
432 // Check if all annotations are of the same type (i.e. we can use one color)
433 // or if we should use a neutral color (i.e. gray)
434 const int format = annotations.front().format();
435 const bool single_format = std::all_of(
436 annotations.begin(), annotations.end(),
437 [&](const Annotation &a) { return a.format() == format; });
439 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
440 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
443 p.drawConvexPolygon(pts, countof(pts));
446 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
447 QColor fill, QColor outline, int h, double x, int y) const
449 const QString text = a.annotations().empty() ?
450 QString() : a.annotations().back();
451 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
453 const QRectF rect(x - w / 2, y - h / 2, w, h);
457 p.drawRoundedRect(rect, h / 2, h / 2);
460 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
463 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
464 QColor fill, QColor outline, int h, double start,
465 double end, int y) const
467 const double top = y + .5 - h / 2;
468 const double bottom = y + .5 + h / 2;
469 const vector<QString> annotations = a.annotations();
474 // If the two ends are within 1 pixel, draw a vertical line
475 if (start + 1.0 > end) {
476 p.drawLine(QPointF(start, top), QPointF(start, bottom));
480 const double cap_width = min((end - start) / 4, EndCapWidth);
483 QPointF(start, y + .5f),
484 QPointF(start + cap_width, top),
485 QPointF(end - cap_width, top),
486 QPointF(end, y + .5f),
487 QPointF(end - cap_width, bottom),
488 QPointF(start + cap_width, bottom)
491 p.drawConvexPolygon(pts, countof(pts));
493 if (annotations.empty())
496 QRectF rect(start + cap_width, y - h / 2,
497 end - start - cap_width * 2, h);
498 if (rect.width() <= 4)
503 // Try to find an annotation that will fit
504 QString best_annotation;
507 for (const QString &a : annotations) {
508 const int w = p.boundingRect(QRectF(), 0, a).width();
509 if (w <= rect.width() && w > best_width)
510 best_annotation = a, best_width = w;
513 if (best_annotation.isEmpty())
514 best_annotation = annotations.back();
516 // If not ellide the last in the list
517 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
518 best_annotation, Qt::ElideRight, rect.width()));
521 void DecodeTrace::draw_error(QPainter &p, const QString &message,
522 const ViewItemPaintParams &pp)
524 const int y = get_visual_y();
526 p.setPen(ErrorBgColour.darker());
527 p.setBrush(ErrorBgColour);
529 const QRectF bounding_rect =
530 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
531 const QRectF text_rect = p.boundingRect(bounding_rect,
532 Qt::AlignCenter, message);
533 const float r = text_rect.height() / 4;
535 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
539 p.drawText(text_rect, message);
542 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
545 using namespace pv::data;
546 using pv::data::decode::Decoder;
548 double samples_per_pixel, pixels_offset;
550 assert(decoder_stack_);
552 shared_ptr<Logic> data;
553 shared_ptr<LogicSignal> logic_signal;
555 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
557 // We get the logic data of the first channel in the list.
558 // This works because we are currently assuming all
559 // LogicSignals have the same data/segment
560 for (const shared_ptr<Decoder> &dec : stack)
561 if (dec && !dec->channels().empty() &&
562 ((logic_signal = (*dec->channels().begin()).second)) &&
563 ((data = logic_signal->logic_data())))
566 if (!data || data->logic_segments().empty())
569 const shared_ptr<LogicSegment> segment =
570 data->logic_segments().front();
572 const int64_t sample_count = (int64_t)segment->get_sample_count();
573 if (sample_count == 0)
576 const int64_t samples_decoded = decoder_stack_->samples_decoded();
577 if (sample_count == samples_decoded)
580 const int y = get_visual_y();
582 tie(pixels_offset, samples_per_pixel) =
583 get_pixels_offset_samples_per_pixel();
585 const double start = max(samples_decoded /
586 samples_per_pixel - pixels_offset, left - 1.0);
587 const double end = min(sample_count / samples_per_pixel -
588 pixels_offset, right + 1.0);
589 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
591 p.setPen(QPen(Qt::NoPen));
592 p.setBrush(Qt::white);
593 p.drawRect(no_decode_rect);
595 p.setPen(NoDecodeColour);
596 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
597 p.drawRect(no_decode_rect);
600 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
603 assert(decoder_stack_);
605 const View *view = owner_->view();
608 const double scale = view->scale();
611 const double pixels_offset =
612 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
614 double samplerate = decoder_stack_->samplerate();
616 // Show sample rate as 1Hz when it is unknown
617 if (samplerate == 0.0)
620 return make_pair(pixels_offset, samplerate * scale);
623 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
624 int x_start, int x_end) const
626 double samples_per_pixel, pixels_offset;
627 tie(pixels_offset, samples_per_pixel) =
628 get_pixels_offset_samples_per_pixel();
630 const uint64_t start = (uint64_t)max(
631 (x_start + pixels_offset) * samples_per_pixel, 0.0);
632 const uint64_t end = (uint64_t)max(
633 (x_end + pixels_offset) * samples_per_pixel, 0.0);
635 return make_pair(start, end);
638 int DecodeTrace::get_row_at_point(const QPoint &point)
643 const int y = (point.y() - get_visual_y() + row_height_ / 2);
645 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
649 const int row = y / row_height_;
651 if (row >= (int)visible_rows_.size())
657 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
659 using namespace pv::data::decode;
664 const pair<uint64_t, uint64_t> sample_range =
665 get_sample_range(point.x(), point.x() + 1);
666 const int row = get_row_at_point(point);
670 vector<pv::data::decode::Annotation> annotations;
672 assert(decoder_stack_);
673 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
674 sample_range.first, sample_range.second);
676 return (annotations.empty()) ?
677 QString() : annotations[0].annotations().front();
680 void DecodeTrace::hover_point_changed()
684 const View *const view = owner_->view();
687 QPoint hp = view->hover_point();
688 QString ann = get_annotation_at_point(hp);
692 if (!row_height_ || ann.isEmpty()) {
693 QToolTip::hideText();
697 const int hover_row = get_row_at_point(hp);
699 QFontMetrics m(QToolTip::font());
700 const QRect text_size = m.boundingRect(QRect(), 0, ann);
702 // This is OS-specific and unfortunately we can't query it, so
703 // use an approximation to at least try to minimize the error.
704 const int padding = 8;
706 // Make sure the tool tip doesn't overlap with the mouse cursor.
707 // If it did, the tool tip would constantly hide and re-appear.
708 // We also push it up by one row so that it appears above the
709 // decode trace, not below.
710 hp.setX(hp.x() - (text_size.width() / 2) - padding);
712 hp.setY(get_visual_y() - (row_height_ / 2) +
713 (hover_row * row_height_) -
714 row_height_ - text_size.height() - padding);
716 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
719 void DecodeTrace::create_decoder_form(int index,
720 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
726 const srd_decoder *const decoder = dec->decoder();
729 const bool decoder_deletable = index > 0;
731 pv::widgets::DecoderGroupBox *const group =
732 new pv::widgets::DecoderGroupBox(
733 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
734 group->set_decoder_visible(dec->shown());
736 if (decoder_deletable) {
737 delete_mapper_.setMapping(group, index);
738 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
741 show_hide_mapper_.setMapping(group, index);
742 connect(group, SIGNAL(show_hide_decoder()),
743 &show_hide_mapper_, SLOT(map()));
745 QFormLayout *const decoder_form = new QFormLayout;
746 group->add_layout(decoder_form);
748 // Add the mandatory channels
749 for (l = decoder->channels; l; l = l->next) {
750 const struct srd_channel *const pdch =
751 (struct srd_channel *)l->data;
752 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
753 connect(combo, SIGNAL(currentIndexChanged(int)),
754 this, SLOT(on_channel_selected(int)));
755 decoder_form->addRow(tr("<b>%1</b> (%2) *")
756 .arg(QString::fromUtf8(pdch->name))
757 .arg(QString::fromUtf8(pdch->desc)), combo);
759 const ChannelSelector s = {combo, dec, pdch};
760 channel_selectors_.push_back(s);
763 // Add the optional channels
764 for (l = decoder->opt_channels; l; l = l->next) {
765 const struct srd_channel *const pdch =
766 (struct srd_channel *)l->data;
767 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
768 connect(combo, SIGNAL(currentIndexChanged(int)),
769 this, SLOT(on_channel_selected(int)));
770 decoder_form->addRow(tr("<b>%1</b> (%2)")
771 .arg(QString::fromUtf8(pdch->name))
772 .arg(QString::fromUtf8(pdch->desc)), combo);
774 const ChannelSelector s = {combo, dec, pdch};
775 channel_selectors_.push_back(s);
779 shared_ptr<binding::Decoder> binding(
780 new binding::Decoder(decoder_stack_, dec));
781 binding->add_properties_to_form(decoder_form, true);
783 bindings_.push_back(binding);
786 decoder_forms_.push_back(group);
789 QComboBox* DecodeTrace::create_channel_selector(
790 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
791 const srd_channel *const pdch)
795 const auto sigs(session_.signals());
797 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
798 std::sort(sig_list.begin(), sig_list.end(),
799 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
800 return a->name().compare(b->name()) < 0; });
802 assert(decoder_stack_);
803 const auto channel_iter = dec->channels().find(pdch);
805 QComboBox *selector = new QComboBox(parent);
807 selector->addItem("-", qVariantFromValue((void*)nullptr));
809 if (channel_iter == dec->channels().end())
810 selector->setCurrentIndex(0);
812 for (const shared_ptr<view::Signal> &s : sig_list) {
814 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
815 selector->addItem(s->name(),
816 qVariantFromValue((void*)s.get()));
818 if (channel_iter != dec->channels().end() &&
819 (*channel_iter).second == s)
820 selector->setCurrentIndex(
821 selector->count() - 1);
828 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
832 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
834 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
836 for (const ChannelSelector &s : channel_selectors_) {
837 if (s.decoder_ != dec)
840 const LogicSignal *const selection =
841 (LogicSignal*)s.combo_->itemData(
842 s.combo_->currentIndex()).value<void*>();
844 for (shared_ptr<Signal> sig : sigs)
845 if (sig.get() == selection) {
846 channel_map[s.pdch_] =
847 dynamic_pointer_cast<LogicSignal>(sig);
852 dec->set_channels(channel_map);
855 void DecodeTrace::commit_channels()
857 assert(decoder_stack_);
858 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
859 commit_decoder_channels(dec);
861 decoder_stack_->begin_decode();
864 void DecodeTrace::on_new_decode_data()
867 owner_->row_item_appearance_changed(false, true);
870 void DecodeTrace::delete_pressed()
875 void DecodeTrace::on_delete()
877 session_.remove_decode_signal(this);
880 void DecodeTrace::on_channel_selected(int)
885 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
888 assert(decoder_stack_);
889 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
890 new data::decode::Decoder(decoder)));
891 decoder_stack_->begin_decode();
896 void DecodeTrace::on_delete_decoder(int index)
898 decoder_stack_->remove(index);
903 decoder_stack_->begin_decode();
906 void DecodeTrace::on_show_hide_decoder(int index)
908 using pv::data::decode::Decoder;
910 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
912 // Find the decoder in the stack
913 auto iter = stack.cbegin();
914 for (int i = 0; i < index; i++, iter++)
915 assert(iter != stack.end());
917 shared_ptr<Decoder> dec = *iter;
920 const bool show = !dec->shown();
923 assert(index < (int)decoder_forms_.size());
924 decoder_forms_[index]->set_decoder_visible(show);
927 owner_->row_item_appearance_changed(false, true);