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/strnatcmp.hpp>
48 #include <pv/data/decoderstack.hpp>
49 #include <pv/data/decode/decoder.hpp>
50 #include <pv/data/logic.hpp>
51 #include <pv/data/logicsegment.hpp>
52 #include <pv/data/decode/annotation.hpp>
53 #include <pv/view/logicsignal.hpp>
54 #include <pv/view/view.hpp>
55 #include <pv/view/viewport.hpp>
56 #include <pv/widgets/decodergroupbox.hpp>
57 #include <pv/widgets/decodermenu.hpp>
59 using boost::shared_lock;
60 using boost::shared_mutex;
61 using std::dynamic_pointer_cast;
63 using std::lock_guard;
70 using std::shared_ptr;
72 using std::unordered_set;
78 const QColor DecodeTrace::DecodeColours[4] = {
79 QColor(0xEF, 0x29, 0x29), // Red
80 QColor(0xFC, 0xE9, 0x4F), // Yellow
81 QColor(0x8A, 0xE2, 0x34), // Green
82 QColor(0x72, 0x9F, 0xCF) // Blue
85 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
86 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
88 const int DecodeTrace::ArrowSize = 4;
89 const double DecodeTrace::EndCapWidth = 5;
90 const int DecodeTrace::RowTitleMargin = 10;
91 const int DecodeTrace::DrawPadding = 100;
93 const QColor DecodeTrace::Colours[16] = {
94 QColor(0xEF, 0x29, 0x29),
95 QColor(0xF6, 0x6A, 0x32),
96 QColor(0xFC, 0xAE, 0x3E),
97 QColor(0xFB, 0xCA, 0x47),
98 QColor(0xFC, 0xE9, 0x4F),
99 QColor(0xCD, 0xF0, 0x40),
100 QColor(0x8A, 0xE2, 0x34),
101 QColor(0x4E, 0xDC, 0x44),
102 QColor(0x55, 0xD7, 0x95),
103 QColor(0x64, 0xD1, 0xD2),
104 QColor(0x72, 0x9F, 0xCF),
105 QColor(0xD4, 0x76, 0xC4),
106 QColor(0x9D, 0x79, 0xB9),
107 QColor(0xAD, 0x7F, 0xA8),
108 QColor(0xC2, 0x62, 0x9B),
109 QColor(0xD7, 0x47, 0x6F)
112 const QColor DecodeTrace::OutlineColours[16] = {
113 QColor(0x77, 0x14, 0x14),
114 QColor(0x7B, 0x35, 0x19),
115 QColor(0x7E, 0x57, 0x1F),
116 QColor(0x7D, 0x65, 0x23),
117 QColor(0x7E, 0x74, 0x27),
118 QColor(0x66, 0x78, 0x20),
119 QColor(0x45, 0x71, 0x1A),
120 QColor(0x27, 0x6E, 0x22),
121 QColor(0x2A, 0x6B, 0x4A),
122 QColor(0x32, 0x68, 0x69),
123 QColor(0x39, 0x4F, 0x67),
124 QColor(0x6A, 0x3B, 0x62),
125 QColor(0x4E, 0x3C, 0x5C),
126 QColor(0x56, 0x3F, 0x54),
127 QColor(0x61, 0x31, 0x4D),
128 QColor(0x6B, 0x23, 0x37)
131 DecodeTrace::DecodeTrace(pv::Session &session,
132 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
133 Trace(QString::fromUtf8(
134 decoder_stack->stack().front()->decoder()->name)),
136 decoder_stack_(decoder_stack),
138 max_visible_rows_(0),
139 delete_mapper_(this),
140 show_hide_mapper_(this)
142 assert(decoder_stack_);
144 // Determine shortest string we want to see displayed in full
145 QFontMetrics m(QApplication::font());
146 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
148 set_colour(DecodeColours[index % countof(DecodeColours)]);
150 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
151 this, SLOT(on_new_decode_data()));
152 connect(&delete_mapper_, SIGNAL(mapped(int)),
153 this, SLOT(on_delete_decoder(int)));
154 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
155 this, SLOT(on_show_hide_decoder(int)));
158 bool DecodeTrace::enabled() const
163 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
165 return decoder_stack_;
168 pair<int, int> DecodeTrace::v_extents() const
170 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
172 return make_pair(-row_height, row_height * max_visible_rows_);
175 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
177 Trace::paint_back(p, pp);
178 paint_axis(p, pp, get_visual_y());
181 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
183 using namespace pv::data::decode;
185 const int text_height = ViewItemPaintParams::text_height();
186 row_height_ = (text_height * 6) / 4;
187 const int annotation_height = (text_height * 5) / 4;
189 assert(decoder_stack_);
190 const QString err = decoder_stack_->error_message();
191 if (!err.isEmpty()) {
192 draw_unresolved_period(
193 p, annotation_height, pp.left(), pp.right());
194 draw_error(p, err, pp);
198 // Iterate through the rows
199 int y = get_visual_y();
200 pair<uint64_t, uint64_t> sample_range = get_sample_range(
201 pp.left(), pp.right());
203 assert(decoder_stack_);
204 const vector<Row> rows(decoder_stack_->get_visible_rows());
206 visible_rows_.clear();
207 for (const Row& row : rows) {
208 // Cache the row title widths
211 row_title_width = row_title_widths_.at(row);
212 } catch (std::out_of_range) {
213 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
215 row_title_widths_[row] = w;
219 // Determine the row's color
220 size_t base_colour = 0x13579BDF;
221 boost::hash_combine(base_colour, this);
222 boost::hash_combine(base_colour, row.decoder());
223 boost::hash_combine(base_colour, row.row());
226 vector<Annotation> annotations;
227 decoder_stack_->get_annotation_subset(annotations, row,
228 sample_range.first, sample_range.second);
229 if (!annotations.empty()) {
230 draw_annotations(annotations, p, annotation_height, pp, y,
231 base_colour, row_title_width);
235 visible_rows_.push_back(row);
240 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
242 // Update the maximum row count if needed
243 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
246 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
248 using namespace pv::data::decode;
252 for (size_t i = 0; i < visible_rows_.size(); i++) {
253 const int y = i * row_height_ + get_visual_y();
255 p.setPen(QPen(Qt::NoPen));
256 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
259 const QPointF points[] = {
260 QPointF(pp.left(), y - ArrowSize),
261 QPointF(pp.left() + ArrowSize, y),
262 QPointF(pp.left(), y + ArrowSize)
264 p.drawPolygon(points, countof(points));
267 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
268 pp.right() - pp.left(), row_height_);
269 const QString h(visible_rows_[i].title());
270 const int f = Qt::AlignLeft | Qt::AlignVCenter |
274 p.setPen(QApplication::palette().color(QPalette::Base));
275 for (int dx = -1; dx <= 1; dx++)
276 for (int dy = -1; dy <= 1; dy++)
277 if (dx != 0 && dy != 0)
278 p.drawText(r.translated(dx, dy), f, h);
281 p.setPen(QApplication::palette().color(QPalette::WindowText));
286 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
288 using pv::data::decode::Decoder;
292 assert(decoder_stack_);
294 // Add the standard options
295 Trace::populate_popup_form(parent, form);
297 // Add the decoder options
299 channel_selectors_.clear();
300 decoder_forms_.clear();
302 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
305 QLabel *const l = new QLabel(
306 tr("<p><i>No decoders in the stack</i></p>"));
307 l->setAlignment(Qt::AlignCenter);
310 auto iter = stack.cbegin();
311 for (int i = 0; i < (int)stack.size(); i++, iter++) {
312 shared_ptr<Decoder> dec(*iter);
313 create_decoder_form(i, dec, parent, form);
316 form->addRow(new QLabel(
317 tr("<i>* Required channels</i>"), parent));
320 // Add stacking button
321 pv::widgets::DecoderMenu *const decoder_menu =
322 new pv::widgets::DecoderMenu(parent);
323 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
324 this, SLOT(on_stack_decoder(srd_decoder*)));
326 QPushButton *const stack_button =
327 new QPushButton(tr("Stack Decoder"), parent);
328 stack_button->setMenu(decoder_menu);
330 QHBoxLayout *stack_button_box = new QHBoxLayout;
331 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
332 form->addRow(stack_button_box);
335 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
337 QMenu *const menu = Trace::create_context_menu(parent);
339 menu->addSeparator();
341 QAction *const del = new QAction(tr("Delete"), this);
342 del->setShortcuts(QKeySequence::Delete);
343 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
344 menu->addAction(del);
349 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
350 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
351 size_t base_colour, int row_title_width)
353 using namespace pv::data::decode;
355 vector<Annotation> a_block;
358 double samples_per_pixel, pixels_offset;
359 tie(pixels_offset, samples_per_pixel) =
360 get_pixels_offset_samples_per_pixel();
362 // Sort the annotations by start sample so that decoders
363 // can't confuse us by creating annotations out of order
364 stable_sort(annotations.begin(), annotations.end(),
365 [](const Annotation &a, const Annotation &b) {
366 return a.start_sample() < b.start_sample(); });
368 // Gather all annotations that form a visual "block" and draw them as such
369 for (const Annotation &a : annotations) {
371 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
372 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
373 const int a_width = a_end - a_start;
375 const int delta = a_end - p_end;
377 bool a_is_separate = false;
379 // Annotation wider than the threshold for a useful label width?
380 if (a_width >= min_useful_label_width_) {
381 for (const QString &ann_text : a.annotations()) {
382 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
383 // Annotation wide enough to fit a label? Don't put it in a block then
385 a_is_separate = true;
391 // Were the previous and this annotation more than a pixel apart?
392 if ((abs(delta) > 1) || a_is_separate) {
393 // Block was broken, draw annotations that form the current block
394 if (a_block.size() == 1) {
395 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
399 draw_annotation_block(a_block, p, h, y, base_colour);
405 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
406 // Next annotation must start a new block. delta will be > 1
407 // because we set p_end to INT_MIN but that's okay since
408 // a_block will be empty, so nothing will be drawn
411 a_block.push_back(a);
416 if (a_block.size() == 1)
417 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
420 draw_annotation_block(a_block, p, h, y, base_colour);
423 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
424 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
425 size_t base_colour, int row_title_width) const
427 double samples_per_pixel, pixels_offset;
428 tie(pixels_offset, samples_per_pixel) =
429 get_pixels_offset_samples_per_pixel();
431 const double start = a.start_sample() / samples_per_pixel -
433 const double end = a.end_sample() / samples_per_pixel -
436 const size_t colour = (base_colour + a.format()) % countof(Colours);
437 p.setPen(OutlineColours[colour]);
438 p.setBrush(Colours[colour]);
440 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
443 if (a.start_sample() == a.end_sample())
444 draw_instant(a, p, h, start, y);
446 draw_range(a, p, h, start, end, y, pp,
450 void DecodeTrace::draw_annotation_block(
451 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
452 int y, size_t base_colour) const
454 using namespace pv::data::decode;
456 if (annotations.empty())
459 double samples_per_pixel, pixels_offset;
460 tie(pixels_offset, samples_per_pixel) =
461 get_pixels_offset_samples_per_pixel();
463 const double start = annotations.front().start_sample() /
464 samples_per_pixel - pixels_offset;
465 const double end = annotations.back().end_sample() /
466 samples_per_pixel - pixels_offset;
468 const double top = y + .5 - h / 2;
469 const double bottom = y + .5 + h / 2;
471 const size_t colour = (base_colour + annotations.front().format()) %
474 // Check if all annotations are of the same type (i.e. we can use one color)
475 // or if we should use a neutral color (i.e. gray)
476 const int format = annotations.front().format();
477 const bool single_format = std::all_of(
478 annotations.begin(), annotations.end(),
479 [&](const Annotation &a) { return a.format() == format; });
481 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
482 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
485 QRectF(start, top, end - start, bottom - top), h/4, h/4);
488 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
489 int h, double x, int y) const
491 const QString text = a.annotations().empty() ?
492 QString() : a.annotations().back();
493 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
495 const QRectF rect(x - w / 2, y - h / 2, w, h);
497 p.drawRoundedRect(rect, h / 2, h / 2);
500 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
503 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
504 int h, double start, double end, int y, const ViewItemPaintParams &pp,
505 int row_title_width) const
507 const double top = y + .5 - h / 2;
508 const double bottom = y + .5 + h / 2;
509 const vector<QString> annotations = a.annotations();
511 // If the two ends are within 1 pixel, draw a vertical line
512 if (start + 1.0 > end) {
513 p.drawLine(QPointF(start, top), QPointF(start, bottom));
517 const double cap_width = min((end - start) / 4, EndCapWidth);
520 QPointF(start, y + .5f),
521 QPointF(start + cap_width, top),
522 QPointF(end - cap_width, top),
523 QPointF(end, y + .5f),
524 QPointF(end - cap_width, bottom),
525 QPointF(start + cap_width, bottom)
528 p.drawConvexPolygon(pts, countof(pts));
530 if (annotations.empty())
533 const int ann_start = start + cap_width;
534 const int ann_end = end - cap_width;
536 const int real_start = std::max(ann_start, pp.left() + row_title_width);
537 const int real_end = std::min(ann_end, pp.right());
538 const int real_width = real_end - real_start;
540 QRectF rect(real_start, y - h / 2, real_width, h);
541 if (rect.width() <= 4)
546 // Try to find an annotation that will fit
547 QString best_annotation;
550 for (const QString &a : annotations) {
551 const int w = p.boundingRect(QRectF(), 0, a).width();
552 if (w <= rect.width() && w > best_width)
553 best_annotation = a, best_width = w;
556 if (best_annotation.isEmpty())
557 best_annotation = annotations.back();
559 // If not ellide the last in the list
560 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
561 best_annotation, Qt::ElideRight, rect.width()));
564 void DecodeTrace::draw_error(QPainter &p, const QString &message,
565 const ViewItemPaintParams &pp)
567 const int y = get_visual_y();
569 p.setPen(ErrorBgColour.darker());
570 p.setBrush(ErrorBgColour);
572 const QRectF bounding_rect =
573 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
574 const QRectF text_rect = p.boundingRect(bounding_rect,
575 Qt::AlignCenter, message);
576 const float r = text_rect.height() / 4;
578 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
582 p.drawText(text_rect, message);
585 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
588 using namespace pv::data;
589 using pv::data::decode::Decoder;
591 double samples_per_pixel, pixels_offset;
593 assert(decoder_stack_);
595 shared_ptr<Logic> data;
596 shared_ptr<LogicSignal> logic_signal;
598 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
600 // We get the logic data of the first channel in the list.
601 // This works because we are currently assuming all
602 // LogicSignals have the same data/segment
603 for (const shared_ptr<Decoder> &dec : stack)
604 if (dec && !dec->channels().empty() &&
605 ((logic_signal = (*dec->channels().begin()).second)) &&
606 ((data = logic_signal->logic_data())))
609 if (!data || data->logic_segments().empty())
612 const shared_ptr<LogicSegment> segment =
613 data->logic_segments().front();
615 const int64_t sample_count = (int64_t)segment->get_sample_count();
616 if (sample_count == 0)
619 const int64_t samples_decoded = decoder_stack_->samples_decoded();
620 if (sample_count == samples_decoded)
623 const int y = get_visual_y();
625 tie(pixels_offset, samples_per_pixel) =
626 get_pixels_offset_samples_per_pixel();
628 const double start = max(samples_decoded /
629 samples_per_pixel - pixels_offset, left - 1.0);
630 const double end = min(sample_count / samples_per_pixel -
631 pixels_offset, right + 1.0);
632 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
634 p.setPen(QPen(Qt::NoPen));
635 p.setBrush(Qt::white);
636 p.drawRect(no_decode_rect);
638 p.setPen(NoDecodeColour);
639 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
640 p.drawRect(no_decode_rect);
643 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
646 assert(decoder_stack_);
648 const View *view = owner_->view();
651 const double scale = view->scale();
654 const double pixels_offset =
655 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
657 double samplerate = decoder_stack_->samplerate();
659 // Show sample rate as 1Hz when it is unknown
660 if (samplerate == 0.0)
663 return make_pair(pixels_offset, samplerate * scale);
666 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
667 int x_start, int x_end) const
669 double samples_per_pixel, pixels_offset;
670 tie(pixels_offset, samples_per_pixel) =
671 get_pixels_offset_samples_per_pixel();
673 const uint64_t start = (uint64_t)max(
674 (x_start + pixels_offset) * samples_per_pixel, 0.0);
675 const uint64_t end = (uint64_t)max(
676 (x_end + pixels_offset) * samples_per_pixel, 0.0);
678 return make_pair(start, end);
681 int DecodeTrace::get_row_at_point(const QPoint &point)
686 const int y = (point.y() - get_visual_y() + row_height_ / 2);
688 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
692 const int row = y / row_height_;
694 if (row >= (int)visible_rows_.size())
700 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
702 using namespace pv::data::decode;
707 const pair<uint64_t, uint64_t> sample_range =
708 get_sample_range(point.x(), point.x() + 1);
709 const int row = get_row_at_point(point);
713 vector<pv::data::decode::Annotation> annotations;
715 assert(decoder_stack_);
716 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
717 sample_range.first, sample_range.second);
719 return (annotations.empty()) ?
720 QString() : annotations[0].annotations().front();
723 void DecodeTrace::hover_point_changed()
727 const View *const view = owner_->view();
730 QPoint hp = view->hover_point();
731 QString ann = get_annotation_at_point(hp);
735 if (!row_height_ || ann.isEmpty()) {
736 QToolTip::hideText();
740 const int hover_row = get_row_at_point(hp);
742 QFontMetrics m(QToolTip::font());
743 const QRect text_size = m.boundingRect(QRect(), 0, ann);
745 // This is OS-specific and unfortunately we can't query it, so
746 // use an approximation to at least try to minimize the error.
747 const int padding = 8;
749 // Make sure the tool tip doesn't overlap with the mouse cursor.
750 // If it did, the tool tip would constantly hide and re-appear.
751 // We also push it up by one row so that it appears above the
752 // decode trace, not below.
753 hp.setX(hp.x() - (text_size.width() / 2) - padding);
755 hp.setY(get_visual_y() - (row_height_ / 2) +
756 (hover_row * row_height_) -
757 row_height_ - text_size.height() - padding);
759 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
762 void DecodeTrace::create_decoder_form(int index,
763 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
769 const srd_decoder *const decoder = dec->decoder();
772 const bool decoder_deletable = index > 0;
774 pv::widgets::DecoderGroupBox *const group =
775 new pv::widgets::DecoderGroupBox(
776 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
777 group->set_decoder_visible(dec->shown());
779 if (decoder_deletable) {
780 delete_mapper_.setMapping(group, index);
781 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
784 show_hide_mapper_.setMapping(group, index);
785 connect(group, SIGNAL(show_hide_decoder()),
786 &show_hide_mapper_, SLOT(map()));
788 QFormLayout *const decoder_form = new QFormLayout;
789 group->add_layout(decoder_form);
791 // Add the mandatory channels
792 for (l = decoder->channels; l; l = l->next) {
793 const struct srd_channel *const pdch =
794 (struct srd_channel *)l->data;
795 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
796 connect(combo, SIGNAL(currentIndexChanged(int)),
797 this, SLOT(on_channel_selected(int)));
798 decoder_form->addRow(tr("<b>%1</b> (%2) *")
799 .arg(QString::fromUtf8(pdch->name),
800 QString::fromUtf8(pdch->desc)), combo);
802 const ChannelSelector s = {combo, dec, pdch};
803 channel_selectors_.push_back(s);
806 // Add the optional channels
807 for (l = decoder->opt_channels; l; l = l->next) {
808 const struct srd_channel *const pdch =
809 (struct srd_channel *)l->data;
810 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
811 connect(combo, SIGNAL(currentIndexChanged(int)),
812 this, SLOT(on_channel_selected(int)));
813 decoder_form->addRow(tr("<b>%1</b> (%2)")
814 .arg(QString::fromUtf8(pdch->name),
815 QString::fromUtf8(pdch->desc)), combo);
817 const ChannelSelector s = {combo, dec, pdch};
818 channel_selectors_.push_back(s);
822 shared_ptr<binding::Decoder> binding(
823 new binding::Decoder(decoder_stack_, dec));
824 binding->add_properties_to_form(decoder_form, true);
826 bindings_.push_back(binding);
829 decoder_forms_.push_back(group);
832 QComboBox* DecodeTrace::create_channel_selector(
833 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
834 const srd_channel *const pdch)
838 const auto sigs(session_.signals());
840 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
841 std::sort(sig_list.begin(), sig_list.end(),
842 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
843 return strnatcasecmp(a->name().toStdString(),
844 b->name().toStdString()) < 0; });
846 assert(decoder_stack_);
847 const auto channel_iter = dec->channels().find(pdch);
849 QComboBox *selector = new QComboBox(parent);
851 selector->addItem("-", qVariantFromValue((void*)nullptr));
853 if (channel_iter == dec->channels().end())
854 selector->setCurrentIndex(0);
856 for (const shared_ptr<view::Signal> &s : sig_list) {
858 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
859 selector->addItem(s->name(),
860 qVariantFromValue((void*)s.get()));
862 if (channel_iter != dec->channels().end() &&
863 (*channel_iter).second == s)
864 selector->setCurrentIndex(
865 selector->count() - 1);
872 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
876 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
878 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
880 for (const ChannelSelector &s : channel_selectors_) {
881 if (s.decoder_ != dec)
884 const LogicSignal *const selection =
885 (LogicSignal*)s.combo_->itemData(
886 s.combo_->currentIndex()).value<void*>();
888 for (shared_ptr<Signal> sig : sigs)
889 if (sig.get() == selection) {
890 channel_map[s.pdch_] =
891 dynamic_pointer_cast<LogicSignal>(sig);
896 dec->set_channels(channel_map);
899 void DecodeTrace::commit_channels()
901 assert(decoder_stack_);
902 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
903 commit_decoder_channels(dec);
905 decoder_stack_->begin_decode();
908 void DecodeTrace::on_new_decode_data()
911 owner_->row_item_appearance_changed(false, true);
914 void DecodeTrace::delete_pressed()
919 void DecodeTrace::on_delete()
921 session_.remove_decode_signal(this);
924 void DecodeTrace::on_channel_selected(int)
929 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
932 assert(decoder_stack_);
933 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
934 new data::decode::Decoder(decoder)));
935 decoder_stack_->begin_decode();
940 void DecodeTrace::on_delete_decoder(int index)
942 decoder_stack_->remove(index);
947 decoder_stack_->begin_decode();
950 void DecodeTrace::on_show_hide_decoder(int index)
952 using pv::data::decode::Decoder;
954 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
956 // Find the decoder in the stack
957 auto iter = stack.cbegin();
958 for (int i = 0; i < index; i++, iter++)
959 assert(iter != stack.end());
961 shared_ptr<Decoder> dec = *iter;
964 const bool show = !dec->shown();
967 assert(index < (int)decoder_forms_.size());
968 decoder_forms_[index]->set_decoder_visible(show);
971 owner_->row_item_appearance_changed(false, true);