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 bool single_format = true;
435 int format = annotations.front().format();
437 for (const Annotation &a : annotations)
438 if (a.format() != format) {
439 single_format = false;
443 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
444 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
447 p.drawConvexPolygon(pts, countof(pts));
450 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
451 QColor fill, QColor outline, int h, double x, int y) const
453 const QString text = a.annotations().empty() ?
454 QString() : a.annotations().back();
455 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
457 const QRectF rect(x - w / 2, y - h / 2, w, h);
461 p.drawRoundedRect(rect, h / 2, h / 2);
464 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
467 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
468 QColor fill, QColor outline, int h, double start,
469 double end, int y) const
471 const double top = y + .5 - h / 2;
472 const double bottom = y + .5 + h / 2;
473 const vector<QString> annotations = a.annotations();
478 // If the two ends are within 1 pixel, draw a vertical line
479 if (start + 1.0 > end) {
480 p.drawLine(QPointF(start, top), QPointF(start, bottom));
484 const double cap_width = min((end - start) / 4, EndCapWidth);
487 QPointF(start, y + .5f),
488 QPointF(start + cap_width, top),
489 QPointF(end - cap_width, top),
490 QPointF(end, y + .5f),
491 QPointF(end - cap_width, bottom),
492 QPointF(start + cap_width, bottom)
495 p.drawConvexPolygon(pts, countof(pts));
497 if (annotations.empty())
500 QRectF rect(start + cap_width, y - h / 2,
501 end - start - cap_width * 2, h);
502 if (rect.width() <= 4)
507 // Try to find an annotation that will fit
508 QString best_annotation;
511 for (const QString &a : annotations) {
512 const int w = p.boundingRect(QRectF(), 0, a).width();
513 if (w <= rect.width() && w > best_width)
514 best_annotation = a, best_width = w;
517 if (best_annotation.isEmpty())
518 best_annotation = annotations.back();
520 // If not ellide the last in the list
521 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
522 best_annotation, Qt::ElideRight, rect.width()));
525 void DecodeTrace::draw_error(QPainter &p, const QString &message,
526 const ViewItemPaintParams &pp)
528 const int y = get_visual_y();
530 p.setPen(ErrorBgColour.darker());
531 p.setBrush(ErrorBgColour);
533 const QRectF bounding_rect =
534 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
535 const QRectF text_rect = p.boundingRect(bounding_rect,
536 Qt::AlignCenter, message);
537 const float r = text_rect.height() / 4;
539 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
543 p.drawText(text_rect, message);
546 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
549 using namespace pv::data;
550 using pv::data::decode::Decoder;
552 double samples_per_pixel, pixels_offset;
554 assert(decoder_stack_);
556 shared_ptr<Logic> data;
557 shared_ptr<LogicSignal> logic_signal;
559 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
561 // We get the logic data of the first channel in the list.
562 // This works because we are currently assuming all
563 // LogicSignals have the same data/segment
564 for (const shared_ptr<Decoder> &dec : stack)
565 if (dec && !dec->channels().empty() &&
566 ((logic_signal = (*dec->channels().begin()).second)) &&
567 ((data = logic_signal->logic_data())))
570 if (!data || data->logic_segments().empty())
573 const shared_ptr<LogicSegment> segment =
574 data->logic_segments().front();
576 const int64_t sample_count = (int64_t)segment->get_sample_count();
577 if (sample_count == 0)
580 const int64_t samples_decoded = decoder_stack_->samples_decoded();
581 if (sample_count == samples_decoded)
584 const int y = get_visual_y();
586 tie(pixels_offset, samples_per_pixel) =
587 get_pixels_offset_samples_per_pixel();
589 const double start = max(samples_decoded /
590 samples_per_pixel - pixels_offset, left - 1.0);
591 const double end = min(sample_count / samples_per_pixel -
592 pixels_offset, right + 1.0);
593 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
595 p.setPen(QPen(Qt::NoPen));
596 p.setBrush(Qt::white);
597 p.drawRect(no_decode_rect);
599 p.setPen(NoDecodeColour);
600 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
601 p.drawRect(no_decode_rect);
604 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
607 assert(decoder_stack_);
609 const View *view = owner_->view();
612 const double scale = view->scale();
615 const double pixels_offset =
616 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
618 double samplerate = decoder_stack_->samplerate();
620 // Show sample rate as 1Hz when it is unknown
621 if (samplerate == 0.0)
624 return make_pair(pixels_offset, samplerate * scale);
627 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
628 int x_start, int x_end) const
630 double samples_per_pixel, pixels_offset;
631 tie(pixels_offset, samples_per_pixel) =
632 get_pixels_offset_samples_per_pixel();
634 const uint64_t start = (uint64_t)max(
635 (x_start + pixels_offset) * samples_per_pixel, 0.0);
636 const uint64_t end = (uint64_t)max(
637 (x_end + pixels_offset) * samples_per_pixel, 0.0);
639 return make_pair(start, end);
642 int DecodeTrace::get_row_at_point(const QPoint &point)
647 const int y = (point.y() - get_visual_y() + row_height_ / 2);
649 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
653 const int row = y / row_height_;
655 if (row >= (int)visible_rows_.size())
661 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
663 using namespace pv::data::decode;
668 const pair<uint64_t, uint64_t> sample_range =
669 get_sample_range(point.x(), point.x() + 1);
670 const int row = get_row_at_point(point);
674 vector<pv::data::decode::Annotation> annotations;
676 assert(decoder_stack_);
677 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
678 sample_range.first, sample_range.second);
680 return (annotations.empty()) ?
681 QString() : annotations[0].annotations().front();
684 void DecodeTrace::hover_point_changed()
688 const View *const view = owner_->view();
691 QPoint hp = view->hover_point();
692 QString ann = get_annotation_at_point(hp);
696 if (!row_height_ || ann.isEmpty()) {
697 QToolTip::hideText();
701 const int hover_row = get_row_at_point(hp);
703 QFontMetrics m(QToolTip::font());
704 const QRect text_size = m.boundingRect(QRect(), 0, ann);
706 // This is OS-specific and unfortunately we can't query it, so
707 // use an approximation to at least try to minimize the error.
708 const int padding = 8;
710 // Make sure the tool tip doesn't overlap with the mouse cursor.
711 // If it did, the tool tip would constantly hide and re-appear.
712 // We also push it up by one row so that it appears above the
713 // decode trace, not below.
714 hp.setX(hp.x() - (text_size.width() / 2) - padding);
716 hp.setY(get_visual_y() - (row_height_ / 2) +
717 (hover_row * row_height_) -
718 row_height_ - text_size.height() - padding);
720 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
723 void DecodeTrace::create_decoder_form(int index,
724 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
730 const srd_decoder *const decoder = dec->decoder();
733 const bool decoder_deletable = index > 0;
735 pv::widgets::DecoderGroupBox *const group =
736 new pv::widgets::DecoderGroupBox(
737 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
738 group->set_decoder_visible(dec->shown());
740 if (decoder_deletable) {
741 delete_mapper_.setMapping(group, index);
742 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
745 show_hide_mapper_.setMapping(group, index);
746 connect(group, SIGNAL(show_hide_decoder()),
747 &show_hide_mapper_, SLOT(map()));
749 QFormLayout *const decoder_form = new QFormLayout;
750 group->add_layout(decoder_form);
752 // Add the mandatory channels
753 for (l = decoder->channels; l; l = l->next) {
754 const struct srd_channel *const pdch =
755 (struct srd_channel *)l->data;
756 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
757 connect(combo, SIGNAL(currentIndexChanged(int)),
758 this, SLOT(on_channel_selected(int)));
759 decoder_form->addRow(tr("<b>%1</b> (%2) *")
760 .arg(QString::fromUtf8(pdch->name))
761 .arg(QString::fromUtf8(pdch->desc)), combo);
763 const ChannelSelector s = {combo, dec, pdch};
764 channel_selectors_.push_back(s);
767 // Add the optional channels
768 for (l = decoder->opt_channels; l; l = l->next) {
769 const struct srd_channel *const pdch =
770 (struct srd_channel *)l->data;
771 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
772 connect(combo, SIGNAL(currentIndexChanged(int)),
773 this, SLOT(on_channel_selected(int)));
774 decoder_form->addRow(tr("<b>%1</b> (%2)")
775 .arg(QString::fromUtf8(pdch->name))
776 .arg(QString::fromUtf8(pdch->desc)), combo);
778 const ChannelSelector s = {combo, dec, pdch};
779 channel_selectors_.push_back(s);
783 shared_ptr<binding::Decoder> binding(
784 new binding::Decoder(decoder_stack_, dec));
785 binding->add_properties_to_form(decoder_form, true);
787 bindings_.push_back(binding);
790 decoder_forms_.push_back(group);
793 QComboBox* DecodeTrace::create_channel_selector(
794 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
795 const srd_channel *const pdch)
799 const auto sigs(session_.signals());
801 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
802 std::sort(sig_list.begin(), sig_list.end(),
803 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
804 return a->name().compare(b->name()) < 0; });
806 assert(decoder_stack_);
807 const auto channel_iter = dec->channels().find(pdch);
809 QComboBox *selector = new QComboBox(parent);
811 selector->addItem("-", qVariantFromValue((void*)nullptr));
813 if (channel_iter == dec->channels().end())
814 selector->setCurrentIndex(0);
816 for (const shared_ptr<view::Signal> &s : sig_list) {
818 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
819 selector->addItem(s->name(),
820 qVariantFromValue((void*)s.get()));
822 if (channel_iter != dec->channels().end() &&
823 (*channel_iter).second == s)
824 selector->setCurrentIndex(
825 selector->count() - 1);
832 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
836 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
838 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
840 for (const ChannelSelector &s : channel_selectors_) {
841 if (s.decoder_ != dec)
844 const LogicSignal *const selection =
845 (LogicSignal*)s.combo_->itemData(
846 s.combo_->currentIndex()).value<void*>();
848 for (shared_ptr<Signal> sig : sigs)
849 if (sig.get() == selection) {
850 channel_map[s.pdch_] =
851 dynamic_pointer_cast<LogicSignal>(sig);
856 dec->set_channels(channel_map);
859 void DecodeTrace::commit_channels()
861 assert(decoder_stack_);
862 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
863 commit_decoder_channels(dec);
865 decoder_stack_->begin_decode();
868 void DecodeTrace::on_new_decode_data()
871 owner_->row_item_appearance_changed(false, true);
874 void DecodeTrace::delete_pressed()
879 void DecodeTrace::on_delete()
881 session_.remove_decode_signal(this);
884 void DecodeTrace::on_channel_selected(int)
889 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
892 assert(decoder_stack_);
893 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
894 new data::decode::Decoder(decoder)));
895 decoder_stack_->begin_decode();
900 void DecodeTrace::on_delete_decoder(int index)
902 decoder_stack_->remove(index);
907 decoder_stack_->begin_decode();
910 void DecodeTrace::on_show_hide_decoder(int index)
912 using pv::data::decode::Decoder;
914 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
916 // Find the decoder in the stack
917 auto iter = stack.cbegin();
918 for (int i = 0; i < index; i++, iter++)
919 assert(iter != stack.end());
921 shared_ptr<Decoder> dec = *iter;
924 const bool show = !dec->shown();
927 assert(index < (int)decoder_forms_.size());
928 decoder_forms_[index]->set_decoder_visible(show);
931 owner_->row_item_appearance_changed(false, true);