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 set_colour(DecodeColours[index % countof(DecodeColours)]);
146 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
147 this, SLOT(on_new_decode_data()));
148 connect(&delete_mapper_, SIGNAL(mapped(int)),
149 this, SLOT(on_delete_decoder(int)));
150 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
151 this, SLOT(on_show_hide_decoder(int)));
154 bool DecodeTrace::enabled() const
159 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
161 return decoder_stack_;
164 pair<int, int> DecodeTrace::v_extents() const
166 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
168 return make_pair(-row_height, row_height * max_visible_rows_);
171 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
173 Trace::paint_back(p, pp);
174 paint_axis(p, pp, get_visual_y());
177 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
179 using namespace pv::data::decode;
181 const int text_height = ViewItemPaintParams::text_height();
182 row_height_ = (text_height * 6) / 4;
183 const int annotation_height = (text_height * 5) / 4;
185 assert(decoder_stack_);
186 const QString err = decoder_stack_->error_message();
187 if (!err.isEmpty()) {
188 draw_unresolved_period(
189 p, annotation_height, pp.left(), pp.right());
190 draw_error(p, err, pp);
194 // Iterate through the rows
195 int y = get_visual_y();
196 pair<uint64_t, uint64_t> sample_range = get_sample_range(
197 pp.left(), pp.right());
199 assert(decoder_stack_);
200 const vector<Row> rows(decoder_stack_->get_visible_rows());
202 visible_rows_.clear();
203 for (const Row& row : rows) {
204 // Cache the row title widths
207 row_title_width = row_title_widths_.at(row);
208 } catch (std::out_of_range) {
209 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
211 row_title_widths_[row] = w;
215 // Determine the row's color
216 size_t base_colour = 0x13579BDF;
217 boost::hash_combine(base_colour, this);
218 boost::hash_combine(base_colour, row.decoder());
219 boost::hash_combine(base_colour, row.row());
222 vector<Annotation> annotations;
223 decoder_stack_->get_annotation_subset(annotations, row,
224 sample_range.first, sample_range.second);
225 if (!annotations.empty()) {
226 draw_annotations(annotations, p, annotation_height, pp, y,
227 base_colour, row_title_width);
231 visible_rows_.push_back(row);
236 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
238 // Update the maximum row count if needed
239 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
242 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
244 using namespace pv::data::decode;
248 for (size_t i = 0; i < visible_rows_.size(); i++) {
249 const int y = i * row_height_ + get_visual_y();
251 p.setPen(QPen(Qt::NoPen));
252 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
255 const QPointF points[] = {
256 QPointF(pp.left(), y - ArrowSize),
257 QPointF(pp.left() + ArrowSize, y),
258 QPointF(pp.left(), y + ArrowSize)
260 p.drawPolygon(points, countof(points));
263 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
264 pp.right() - pp.left(), row_height_);
265 const QString h(visible_rows_[i].title());
266 const int f = Qt::AlignLeft | Qt::AlignVCenter |
270 p.setPen(QApplication::palette().color(QPalette::Base));
271 for (int dx = -1; dx <= 1; dx++)
272 for (int dy = -1; dy <= 1; dy++)
273 if (dx != 0 && dy != 0)
274 p.drawText(r.translated(dx, dy), f, h);
277 p.setPen(QApplication::palette().color(QPalette::WindowText));
282 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
284 using pv::data::decode::Decoder;
288 assert(decoder_stack_);
290 // Add the standard options
291 Trace::populate_popup_form(parent, form);
293 // Add the decoder options
295 channel_selectors_.clear();
296 decoder_forms_.clear();
298 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
301 QLabel *const l = new QLabel(
302 tr("<p><i>No decoders in the stack</i></p>"));
303 l->setAlignment(Qt::AlignCenter);
306 auto iter = stack.cbegin();
307 for (int i = 0; i < (int)stack.size(); i++, iter++) {
308 shared_ptr<Decoder> dec(*iter);
309 create_decoder_form(i, dec, parent, form);
312 form->addRow(new QLabel(
313 tr("<i>* Required channels</i>"), parent));
316 // Add stacking button
317 pv::widgets::DecoderMenu *const decoder_menu =
318 new pv::widgets::DecoderMenu(parent);
319 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
320 this, SLOT(on_stack_decoder(srd_decoder*)));
322 QPushButton *const stack_button =
323 new QPushButton(tr("Stack Decoder"), parent);
324 stack_button->setMenu(decoder_menu);
326 QHBoxLayout *stack_button_box = new QHBoxLayout;
327 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
328 form->addRow(stack_button_box);
331 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
333 QMenu *const menu = Trace::create_context_menu(parent);
335 menu->addSeparator();
337 QAction *const del = new QAction(tr("Delete"), this);
338 del->setShortcuts(QKeySequence::Delete);
339 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
340 menu->addAction(del);
345 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
346 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
347 size_t base_colour, int row_title_width)
349 using namespace pv::data::decode;
351 vector<Annotation> a_block;
354 double samples_per_pixel, pixels_offset;
355 tie(pixels_offset, samples_per_pixel) =
356 get_pixels_offset_samples_per_pixel();
358 // Sort the annotations by start sample so that decoders
359 // can't confuse us by creating annotations out of order
360 stable_sort(annotations.begin(), annotations.end(),
361 [](const Annotation &a, const Annotation &b) {
362 return a.start_sample() < b.start_sample(); });
364 // Gather all annotations that form a visual "block" and draw them as such
365 for (const Annotation &a : annotations) {
367 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
368 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
369 const int a_width = a_end - a_start;
371 const int delta = a_end - p_end;
373 bool a_is_separate = false;
375 // Annotation wider than the threshold for a useful label width?
377 for (const QString &ann_text : a.annotations()) {
378 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
379 // Annotation wide enough to fit a label? Don't put it in a block then
381 a_is_separate = true;
387 // Were the previous and this annotation more than a pixel apart?
388 if ((abs(delta) > 1) || a_is_separate) {
389 // Block was broken, draw annotations that form the current block
390 if (a_block.size() == 1) {
391 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
395 draw_annotation_block(a_block, p, h, y, base_colour);
401 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
402 // Next annotation must start a new block. delta will be > 1
403 // because we set p_end to INT_MIN but that's okay since
404 // a_block will be empty, so nothing will be drawn
407 a_block.push_back(a);
412 if (a_block.size() == 1)
413 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
416 draw_annotation_block(a_block, p, h, y, base_colour);
419 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
420 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
421 size_t base_colour, int row_title_width) const
423 double samples_per_pixel, pixels_offset;
424 tie(pixels_offset, samples_per_pixel) =
425 get_pixels_offset_samples_per_pixel();
427 const double start = a.start_sample() / samples_per_pixel -
429 const double end = a.end_sample() / samples_per_pixel -
432 const size_t colour = (base_colour + a.format()) % countof(Colours);
433 p.setPen(OutlineColours[colour]);
434 p.setBrush(Colours[colour]);
436 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
439 if (a.start_sample() == a.end_sample())
440 draw_instant(a, p, h, start, y);
442 draw_range(a, p, h, start, end, y, pp,
446 void DecodeTrace::draw_annotation_block(
447 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
448 int y, size_t base_colour) const
450 using namespace pv::data::decode;
452 if (annotations.empty())
455 double samples_per_pixel, pixels_offset;
456 tie(pixels_offset, samples_per_pixel) =
457 get_pixels_offset_samples_per_pixel();
459 const double start = annotations.front().start_sample() /
460 samples_per_pixel - pixels_offset;
461 const double end = annotations.back().end_sample() /
462 samples_per_pixel - pixels_offset;
464 const double top = y + .5 - h / 2;
465 const double bottom = y + .5 + h / 2;
467 const size_t colour = (base_colour + annotations.front().format()) %
470 // Check if all annotations are of the same type (i.e. we can use one color)
471 // or if we should use a neutral color (i.e. gray)
472 const int format = annotations.front().format();
473 const bool single_format = std::all_of(
474 annotations.begin(), annotations.end(),
475 [&](const Annotation &a) { return a.format() == format; });
477 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
478 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
481 QRectF(start, top, end - start, bottom - top), h/4, h/4);
484 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
485 int h, double x, int y) const
487 const QString text = a.annotations().empty() ?
488 QString() : a.annotations().back();
489 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
491 const QRectF rect(x - w / 2, y - h / 2, w, h);
493 p.drawRoundedRect(rect, h / 2, h / 2);
496 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
499 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
500 int h, double start, double end, int y, const ViewItemPaintParams &pp,
501 int row_title_width) const
503 const double top = y + .5 - h / 2;
504 const double bottom = y + .5 + h / 2;
505 const vector<QString> annotations = a.annotations();
507 // If the two ends are within 1 pixel, draw a vertical line
508 if (start + 1.0 > end) {
509 p.drawLine(QPointF(start, top), QPointF(start, bottom));
513 const double cap_width = min((end - start) / 4, EndCapWidth);
516 QPointF(start, y + .5f),
517 QPointF(start + cap_width, top),
518 QPointF(end - cap_width, top),
519 QPointF(end, y + .5f),
520 QPointF(end - cap_width, bottom),
521 QPointF(start + cap_width, bottom)
524 p.drawConvexPolygon(pts, countof(pts));
526 if (annotations.empty())
529 const int ann_start = start + cap_width;
530 const int ann_end = end - cap_width;
532 const int real_start = std::max(ann_start, pp.left() + row_title_width);
533 const int real_end = std::min(ann_end, pp.right());
534 const int real_width = real_end - real_start;
536 QRectF rect(real_start, y - h / 2, real_width, h);
537 if (rect.width() <= 4)
542 // Try to find an annotation that will fit
543 QString best_annotation;
546 for (const QString &a : annotations) {
547 const int w = p.boundingRect(QRectF(), 0, a).width();
548 if (w <= rect.width() && w > best_width)
549 best_annotation = a, best_width = w;
552 if (best_annotation.isEmpty())
553 best_annotation = annotations.back();
555 // If not ellide the last in the list
556 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
557 best_annotation, Qt::ElideRight, rect.width()));
560 void DecodeTrace::draw_error(QPainter &p, const QString &message,
561 const ViewItemPaintParams &pp)
563 const int y = get_visual_y();
565 p.setPen(ErrorBgColour.darker());
566 p.setBrush(ErrorBgColour);
568 const QRectF bounding_rect =
569 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
570 const QRectF text_rect = p.boundingRect(bounding_rect,
571 Qt::AlignCenter, message);
572 const float r = text_rect.height() / 4;
574 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
578 p.drawText(text_rect, message);
581 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
584 using namespace pv::data;
585 using pv::data::decode::Decoder;
587 double samples_per_pixel, pixels_offset;
589 assert(decoder_stack_);
591 shared_ptr<Logic> data;
592 shared_ptr<LogicSignal> logic_signal;
594 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
596 // We get the logic data of the first channel in the list.
597 // This works because we are currently assuming all
598 // LogicSignals have the same data/segment
599 for (const shared_ptr<Decoder> &dec : stack)
600 if (dec && !dec->channels().empty() &&
601 ((logic_signal = (*dec->channels().begin()).second)) &&
602 ((data = logic_signal->logic_data())))
605 if (!data || data->logic_segments().empty())
608 const shared_ptr<LogicSegment> segment =
609 data->logic_segments().front();
611 const int64_t sample_count = (int64_t)segment->get_sample_count();
612 if (sample_count == 0)
615 const int64_t samples_decoded = decoder_stack_->samples_decoded();
616 if (sample_count == samples_decoded)
619 const int y = get_visual_y();
621 tie(pixels_offset, samples_per_pixel) =
622 get_pixels_offset_samples_per_pixel();
624 const double start = max(samples_decoded /
625 samples_per_pixel - pixels_offset, left - 1.0);
626 const double end = min(sample_count / samples_per_pixel -
627 pixels_offset, right + 1.0);
628 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
630 p.setPen(QPen(Qt::NoPen));
631 p.setBrush(Qt::white);
632 p.drawRect(no_decode_rect);
634 p.setPen(NoDecodeColour);
635 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
636 p.drawRect(no_decode_rect);
639 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
642 assert(decoder_stack_);
644 const View *view = owner_->view();
647 const double scale = view->scale();
650 const double pixels_offset =
651 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
653 double samplerate = decoder_stack_->samplerate();
655 // Show sample rate as 1Hz when it is unknown
656 if (samplerate == 0.0)
659 return make_pair(pixels_offset, samplerate * scale);
662 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
663 int x_start, int x_end) const
665 double samples_per_pixel, pixels_offset;
666 tie(pixels_offset, samples_per_pixel) =
667 get_pixels_offset_samples_per_pixel();
669 const uint64_t start = (uint64_t)max(
670 (x_start + pixels_offset) * samples_per_pixel, 0.0);
671 const uint64_t end = (uint64_t)max(
672 (x_end + pixels_offset) * samples_per_pixel, 0.0);
674 return make_pair(start, end);
677 int DecodeTrace::get_row_at_point(const QPoint &point)
682 const int y = (point.y() - get_visual_y() + row_height_ / 2);
684 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
688 const int row = y / row_height_;
690 if (row >= (int)visible_rows_.size())
696 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
698 using namespace pv::data::decode;
703 const pair<uint64_t, uint64_t> sample_range =
704 get_sample_range(point.x(), point.x() + 1);
705 const int row = get_row_at_point(point);
709 vector<pv::data::decode::Annotation> annotations;
711 assert(decoder_stack_);
712 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
713 sample_range.first, sample_range.second);
715 return (annotations.empty()) ?
716 QString() : annotations[0].annotations().front();
719 void DecodeTrace::hover_point_changed()
723 const View *const view = owner_->view();
726 QPoint hp = view->hover_point();
727 QString ann = get_annotation_at_point(hp);
731 if (!row_height_ || ann.isEmpty()) {
732 QToolTip::hideText();
736 const int hover_row = get_row_at_point(hp);
738 QFontMetrics m(QToolTip::font());
739 const QRect text_size = m.boundingRect(QRect(), 0, ann);
741 // This is OS-specific and unfortunately we can't query it, so
742 // use an approximation to at least try to minimize the error.
743 const int padding = 8;
745 // Make sure the tool tip doesn't overlap with the mouse cursor.
746 // If it did, the tool tip would constantly hide and re-appear.
747 // We also push it up by one row so that it appears above the
748 // decode trace, not below.
749 hp.setX(hp.x() - (text_size.width() / 2) - padding);
751 hp.setY(get_visual_y() - (row_height_ / 2) +
752 (hover_row * row_height_) -
753 row_height_ - text_size.height() - padding);
755 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
758 void DecodeTrace::create_decoder_form(int index,
759 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
765 const srd_decoder *const decoder = dec->decoder();
768 const bool decoder_deletable = index > 0;
770 pv::widgets::DecoderGroupBox *const group =
771 new pv::widgets::DecoderGroupBox(
772 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
773 group->set_decoder_visible(dec->shown());
775 if (decoder_deletable) {
776 delete_mapper_.setMapping(group, index);
777 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
780 show_hide_mapper_.setMapping(group, index);
781 connect(group, SIGNAL(show_hide_decoder()),
782 &show_hide_mapper_, SLOT(map()));
784 QFormLayout *const decoder_form = new QFormLayout;
785 group->add_layout(decoder_form);
787 // Add the mandatory channels
788 for (l = decoder->channels; l; l = l->next) {
789 const struct srd_channel *const pdch =
790 (struct srd_channel *)l->data;
791 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
792 connect(combo, SIGNAL(currentIndexChanged(int)),
793 this, SLOT(on_channel_selected(int)));
794 decoder_form->addRow(tr("<b>%1</b> (%2) *")
795 .arg(QString::fromUtf8(pdch->name),
796 QString::fromUtf8(pdch->desc)), combo);
798 const ChannelSelector s = {combo, dec, pdch};
799 channel_selectors_.push_back(s);
802 // Add the optional channels
803 for (l = decoder->opt_channels; l; l = l->next) {
804 const struct srd_channel *const pdch =
805 (struct srd_channel *)l->data;
806 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
807 connect(combo, SIGNAL(currentIndexChanged(int)),
808 this, SLOT(on_channel_selected(int)));
809 decoder_form->addRow(tr("<b>%1</b> (%2)")
810 .arg(QString::fromUtf8(pdch->name),
811 QString::fromUtf8(pdch->desc)), combo);
813 const ChannelSelector s = {combo, dec, pdch};
814 channel_selectors_.push_back(s);
818 shared_ptr<binding::Decoder> binding(
819 new binding::Decoder(decoder_stack_, dec));
820 binding->add_properties_to_form(decoder_form, true);
822 bindings_.push_back(binding);
825 decoder_forms_.push_back(group);
828 QComboBox* DecodeTrace::create_channel_selector(
829 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
830 const srd_channel *const pdch)
834 const auto sigs(session_.signals());
836 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
837 std::sort(sig_list.begin(), sig_list.end(),
838 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
839 return strnatcasecmp(a->name().toStdString(),
840 b->name().toStdString()) < 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);