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 p.setPen(OutlineColours[colour]);
435 p.setBrush(Colours[colour]);
437 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
440 if (a.start_sample() == a.end_sample())
441 draw_instant(a, p, h, start, y);
443 draw_range(a, p, 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 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);
494 p.drawRoundedRect(rect, h / 2, h / 2);
497 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
500 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
501 int h, double start, double end, int y, const ViewItemPaintParams &pp,
502 int row_title_width) const
504 const double top = y + .5 - h / 2;
505 const double bottom = y + .5 + h / 2;
506 const vector<QString> annotations = a.annotations();
508 // If the two ends are within 1 pixel, draw a vertical line
509 if (start + 1.0 > end) {
510 p.drawLine(QPointF(start, top), QPointF(start, bottom));
514 const double cap_width = min((end - start) / 4, EndCapWidth);
517 QPointF(start, y + .5f),
518 QPointF(start + cap_width, top),
519 QPointF(end - cap_width, top),
520 QPointF(end, y + .5f),
521 QPointF(end - cap_width, bottom),
522 QPointF(start + cap_width, bottom)
525 p.drawConvexPolygon(pts, countof(pts));
527 if (annotations.empty())
530 const int ann_start = start + cap_width;
531 const int ann_end = end - cap_width;
533 const int real_start = std::max(ann_start, pp.left() + row_title_width);
534 const int real_end = std::min(ann_end, pp.right());
535 const int real_width = real_end - real_start;
537 QRectF rect(real_start, y - h / 2, real_width, h);
538 if (rect.width() <= 4)
543 // Try to find an annotation that will fit
544 QString best_annotation;
547 for (const QString &a : annotations) {
548 const int w = p.boundingRect(QRectF(), 0, a).width();
549 if (w <= rect.width() && w > best_width)
550 best_annotation = a, best_width = w;
553 if (best_annotation.isEmpty())
554 best_annotation = annotations.back();
556 // If not ellide the last in the list
557 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
558 best_annotation, Qt::ElideRight, rect.width()));
561 void DecodeTrace::draw_error(QPainter &p, const QString &message,
562 const ViewItemPaintParams &pp)
564 const int y = get_visual_y();
566 p.setPen(ErrorBgColour.darker());
567 p.setBrush(ErrorBgColour);
569 const QRectF bounding_rect =
570 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
571 const QRectF text_rect = p.boundingRect(bounding_rect,
572 Qt::AlignCenter, message);
573 const float r = text_rect.height() / 4;
575 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
579 p.drawText(text_rect, message);
582 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
585 using namespace pv::data;
586 using pv::data::decode::Decoder;
588 double samples_per_pixel, pixels_offset;
590 assert(decoder_stack_);
592 shared_ptr<Logic> data;
593 shared_ptr<LogicSignal> logic_signal;
595 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
597 // We get the logic data of the first channel in the list.
598 // This works because we are currently assuming all
599 // LogicSignals have the same data/segment
600 for (const shared_ptr<Decoder> &dec : stack)
601 if (dec && !dec->channels().empty() &&
602 ((logic_signal = (*dec->channels().begin()).second)) &&
603 ((data = logic_signal->logic_data())))
606 if (!data || data->logic_segments().empty())
609 const shared_ptr<LogicSegment> segment =
610 data->logic_segments().front();
612 const int64_t sample_count = (int64_t)segment->get_sample_count();
613 if (sample_count == 0)
616 const int64_t samples_decoded = decoder_stack_->samples_decoded();
617 if (sample_count == samples_decoded)
620 const int y = get_visual_y();
622 tie(pixels_offset, samples_per_pixel) =
623 get_pixels_offset_samples_per_pixel();
625 const double start = max(samples_decoded /
626 samples_per_pixel - pixels_offset, left - 1.0);
627 const double end = min(sample_count / samples_per_pixel -
628 pixels_offset, right + 1.0);
629 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
631 p.setPen(QPen(Qt::NoPen));
632 p.setBrush(Qt::white);
633 p.drawRect(no_decode_rect);
635 p.setPen(NoDecodeColour);
636 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
637 p.drawRect(no_decode_rect);
640 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
643 assert(decoder_stack_);
645 const View *view = owner_->view();
648 const double scale = view->scale();
651 const double pixels_offset =
652 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
654 double samplerate = decoder_stack_->samplerate();
656 // Show sample rate as 1Hz when it is unknown
657 if (samplerate == 0.0)
660 return make_pair(pixels_offset, samplerate * scale);
663 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
664 int x_start, int x_end) const
666 double samples_per_pixel, pixels_offset;
667 tie(pixels_offset, samples_per_pixel) =
668 get_pixels_offset_samples_per_pixel();
670 const uint64_t start = (uint64_t)max(
671 (x_start + pixels_offset) * samples_per_pixel, 0.0);
672 const uint64_t end = (uint64_t)max(
673 (x_end + pixels_offset) * samples_per_pixel, 0.0);
675 return make_pair(start, end);
678 int DecodeTrace::get_row_at_point(const QPoint &point)
683 const int y = (point.y() - get_visual_y() + row_height_ / 2);
685 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
689 const int row = y / row_height_;
691 if (row >= (int)visible_rows_.size())
697 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
699 using namespace pv::data::decode;
704 const pair<uint64_t, uint64_t> sample_range =
705 get_sample_range(point.x(), point.x() + 1);
706 const int row = get_row_at_point(point);
710 vector<pv::data::decode::Annotation> annotations;
712 assert(decoder_stack_);
713 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
714 sample_range.first, sample_range.second);
716 return (annotations.empty()) ?
717 QString() : annotations[0].annotations().front();
720 void DecodeTrace::hover_point_changed()
724 const View *const view = owner_->view();
727 QPoint hp = view->hover_point();
728 QString ann = get_annotation_at_point(hp);
732 if (!row_height_ || ann.isEmpty()) {
733 QToolTip::hideText();
737 const int hover_row = get_row_at_point(hp);
739 QFontMetrics m(QToolTip::font());
740 const QRect text_size = m.boundingRect(QRect(), 0, ann);
742 // This is OS-specific and unfortunately we can't query it, so
743 // use an approximation to at least try to minimize the error.
744 const int padding = 8;
746 // Make sure the tool tip doesn't overlap with the mouse cursor.
747 // If it did, the tool tip would constantly hide and re-appear.
748 // We also push it up by one row so that it appears above the
749 // decode trace, not below.
750 hp.setX(hp.x() - (text_size.width() / 2) - padding);
752 hp.setY(get_visual_y() - (row_height_ / 2) +
753 (hover_row * row_height_) -
754 row_height_ - text_size.height() - padding);
756 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
759 void DecodeTrace::create_decoder_form(int index,
760 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
766 const srd_decoder *const decoder = dec->decoder();
769 const bool decoder_deletable = index > 0;
771 pv::widgets::DecoderGroupBox *const group =
772 new pv::widgets::DecoderGroupBox(
773 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
774 group->set_decoder_visible(dec->shown());
776 if (decoder_deletable) {
777 delete_mapper_.setMapping(group, index);
778 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
781 show_hide_mapper_.setMapping(group, index);
782 connect(group, SIGNAL(show_hide_decoder()),
783 &show_hide_mapper_, SLOT(map()));
785 QFormLayout *const decoder_form = new QFormLayout;
786 group->add_layout(decoder_form);
788 // Add the mandatory channels
789 for (l = decoder->channels; l; l = l->next) {
790 const struct srd_channel *const pdch =
791 (struct srd_channel *)l->data;
792 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
793 connect(combo, SIGNAL(currentIndexChanged(int)),
794 this, SLOT(on_channel_selected(int)));
795 decoder_form->addRow(tr("<b>%1</b> (%2) *")
796 .arg(QString::fromUtf8(pdch->name))
797 .arg(QString::fromUtf8(pdch->desc)), combo);
799 const ChannelSelector s = {combo, dec, pdch};
800 channel_selectors_.push_back(s);
803 // Add the optional channels
804 for (l = decoder->opt_channels; l; l = l->next) {
805 const struct srd_channel *const pdch =
806 (struct srd_channel *)l->data;
807 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
808 connect(combo, SIGNAL(currentIndexChanged(int)),
809 this, SLOT(on_channel_selected(int)));
810 decoder_form->addRow(tr("<b>%1</b> (%2)")
811 .arg(QString::fromUtf8(pdch->name))
812 .arg(QString::fromUtf8(pdch->desc)), combo);
814 const ChannelSelector s = {combo, dec, pdch};
815 channel_selectors_.push_back(s);
819 shared_ptr<binding::Decoder> binding(
820 new binding::Decoder(decoder_stack_, dec));
821 binding->add_properties_to_form(decoder_form, true);
823 bindings_.push_back(binding);
826 decoder_forms_.push_back(group);
829 QComboBox* DecodeTrace::create_channel_selector(
830 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
831 const srd_channel *const pdch)
835 const auto sigs(session_.signals());
837 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
838 std::sort(sig_list.begin(), sig_list.end(),
839 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
840 return a->name().compare(b->name()) < 0; });
842 assert(decoder_stack_);
843 const auto channel_iter = dec->channels().find(pdch);
845 QComboBox *selector = new QComboBox(parent);
847 selector->addItem("-", qVariantFromValue((void*)nullptr));
849 if (channel_iter == dec->channels().end())
850 selector->setCurrentIndex(0);
852 for (const shared_ptr<view::Signal> &s : sig_list) {
854 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
855 selector->addItem(s->name(),
856 qVariantFromValue((void*)s.get()));
858 if (channel_iter != dec->channels().end() &&
859 (*channel_iter).second == s)
860 selector->setCurrentIndex(
861 selector->count() - 1);
868 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
872 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
874 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
876 for (const ChannelSelector &s : channel_selectors_) {
877 if (s.decoder_ != dec)
880 const LogicSignal *const selection =
881 (LogicSignal*)s.combo_->itemData(
882 s.combo_->currentIndex()).value<void*>();
884 for (shared_ptr<Signal> sig : sigs)
885 if (sig.get() == selection) {
886 channel_map[s.pdch_] =
887 dynamic_pointer_cast<LogicSignal>(sig);
892 dec->set_channels(channel_map);
895 void DecodeTrace::commit_channels()
897 assert(decoder_stack_);
898 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
899 commit_decoder_channels(dec);
901 decoder_stack_->begin_decode();
904 void DecodeTrace::on_new_decode_data()
907 owner_->row_item_appearance_changed(false, true);
910 void DecodeTrace::delete_pressed()
915 void DecodeTrace::on_delete()
917 session_.remove_decode_signal(this);
920 void DecodeTrace::on_channel_selected(int)
925 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
928 assert(decoder_stack_);
929 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
930 new data::decode::Decoder(decoder)));
931 decoder_stack_->begin_decode();
936 void DecodeTrace::on_delete_decoder(int index)
938 decoder_stack_->remove(index);
943 decoder_stack_->begin_decode();
946 void DecodeTrace::on_show_hide_decoder(int index)
948 using pv::data::decode::Decoder;
950 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
952 // Find the decoder in the stack
953 auto iter = stack.cbegin();
954 for (int i = 0; i < index; i++, iter++)
955 assert(iter != stack.end());
957 shared_ptr<Decoder> dec = *iter;
960 const bool show = !dec->shown();
963 assert(index < (int)decoder_forms_.size());
964 decoder_forms_[index]->set_decoder_visible(show);
967 owner_->row_item_appearance_changed(false, true);