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, see <http://www.gnu.org/licenses/>.
21 #include <libsigrokdecode/libsigrokdecode.h>
30 #include <boost/functional/hash.hpp>
31 #include <boost/thread/locks.hpp>
32 #include <boost/thread/shared_mutex.hpp>
35 #include <QApplication>
37 #include <QFormLayout>
40 #include <QPushButton>
43 #include "decodetrace.hpp"
45 #include <pv/session.hpp>
46 #include <pv/strnatcmp.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/view.hpp>
53 #include <pv/view/viewport.hpp>
54 #include <pv/widgets/decodergroupbox.hpp>
55 #include <pv/widgets/decodermenu.hpp>
57 using boost::shared_lock;
58 using boost::shared_mutex;
59 using std::dynamic_pointer_cast;
61 using std::lock_guard;
68 using std::shared_ptr;
70 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 shared_ptr<data::SignalBase> signalbase, int index) :
135 max_visible_rows_(0),
136 delete_mapper_(this),
137 show_hide_mapper_(this)
139 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
140 base_->decoder_stack();
142 // Determine shortest string we want to see displayed in full
143 QFontMetrics m(QApplication::font());
144 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
146 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
147 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
149 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
150 this, SLOT(on_new_decode_data()));
151 connect(&delete_mapper_, SIGNAL(mapped(int)),
152 this, SLOT(on_delete_decoder(int)));
153 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
154 this, SLOT(on_show_hide_decoder(int)));
157 bool DecodeTrace::enabled() const
162 std::shared_ptr<data::SignalBase> DecodeTrace::base() const
167 pair<int, int> DecodeTrace::v_extents() const
169 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
171 // Make an empty decode trace appear symmetrical
172 const int row_count = max(1, max_visible_rows_);
174 return make_pair(-row_height, row_height * row_count);
177 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
179 Trace::paint_back(p, pp);
180 paint_axis(p, pp, get_visual_y());
183 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
185 using namespace pv::data::decode;
187 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
188 base_->decoder_stack();
190 const int text_height = ViewItemPaintParams::text_height();
191 row_height_ = (text_height * 6) / 4;
192 const int annotation_height = (text_height * 5) / 4;
194 assert(decoder_stack);
195 const QString err = decoder_stack->error_message();
196 if (!err.isEmpty()) {
197 draw_unresolved_period(
198 p, annotation_height, pp.left(), pp.right());
199 draw_error(p, err, pp);
203 // Set default pen to allow for text width calculation
206 // Iterate through the rows
207 int y = get_visual_y();
208 pair<uint64_t, uint64_t> sample_range = get_sample_range(
209 pp.left(), pp.right());
211 const vector<Row> rows(decoder_stack->get_visible_rows());
213 visible_rows_.clear();
214 for (const Row& row : rows) {
215 // Cache the row title widths
218 row_title_width = row_title_widths_.at(row);
219 } catch (std::out_of_range) {
220 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
222 row_title_widths_[row] = w;
226 // Determine the row's color
227 size_t base_colour = 0x13579BDF;
228 boost::hash_combine(base_colour, this);
229 boost::hash_combine(base_colour, row.decoder());
230 boost::hash_combine(base_colour, row.row());
233 vector<Annotation> annotations;
234 decoder_stack->get_annotation_subset(annotations, row,
235 sample_range.first, sample_range.second);
236 if (!annotations.empty()) {
237 draw_annotations(annotations, p, annotation_height, pp, y,
238 base_colour, row_title_width);
242 visible_rows_.push_back(row);
247 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
249 if ((int)visible_rows_.size() > max_visible_rows_)
250 owner_->extents_changed(false, true);
252 // Update the maximum row count if needed
253 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
256 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
258 using namespace pv::data::decode;
262 for (size_t i = 0; i < visible_rows_.size(); i++) {
263 const int y = i * row_height_ + get_visual_y();
265 p.setPen(QPen(Qt::NoPen));
266 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
269 const QPointF points[] = {
270 QPointF(pp.left(), y - ArrowSize),
271 QPointF(pp.left() + ArrowSize, y),
272 QPointF(pp.left(), y + ArrowSize)
274 p.drawPolygon(points, countof(points));
277 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
278 pp.right() - pp.left(), row_height_);
279 const QString h(visible_rows_[i].title());
280 const int f = Qt::AlignLeft | Qt::AlignVCenter |
284 p.setPen(QApplication::palette().color(QPalette::Base));
285 for (int dx = -1; dx <= 1; dx++)
286 for (int dy = -1; dy <= 1; dy++)
287 if (dx != 0 && dy != 0)
288 p.drawText(r.translated(dx, dy), f, h);
291 p.setPen(QApplication::palette().color(QPalette::WindowText));
296 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
298 using pv::data::decode::Decoder;
300 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
301 base_->decoder_stack();
305 assert(decoder_stack);
307 // Add the standard options
308 Trace::populate_popup_form(parent, form);
310 // Add the decoder options
312 channel_selectors_.clear();
313 decoder_forms_.clear();
315 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
318 QLabel *const l = new QLabel(
319 tr("<p><i>No decoders in the stack</i></p>"));
320 l->setAlignment(Qt::AlignCenter);
323 auto iter = stack.cbegin();
324 for (int i = 0; i < (int)stack.size(); i++, iter++) {
325 shared_ptr<Decoder> dec(*iter);
326 create_decoder_form(i, dec, parent, form);
329 form->addRow(new QLabel(
330 tr("<i>* Required channels</i>"), parent));
333 // Add stacking button
334 pv::widgets::DecoderMenu *const decoder_menu =
335 new pv::widgets::DecoderMenu(parent);
336 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
337 this, SLOT(on_stack_decoder(srd_decoder*)));
339 QPushButton *const stack_button =
340 new QPushButton(tr("Stack Decoder"), parent);
341 stack_button->setMenu(decoder_menu);
343 QHBoxLayout *stack_button_box = new QHBoxLayout;
344 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
345 form->addRow(stack_button_box);
348 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
350 QMenu *const menu = Trace::create_context_menu(parent);
352 menu->addSeparator();
354 QAction *const del = new QAction(tr("Delete"), this);
355 del->setShortcuts(QKeySequence::Delete);
356 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
357 menu->addAction(del);
362 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
363 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
364 size_t base_colour, int row_title_width)
366 using namespace pv::data::decode;
368 vector<Annotation> a_block;
371 double samples_per_pixel, pixels_offset;
372 tie(pixels_offset, samples_per_pixel) =
373 get_pixels_offset_samples_per_pixel();
375 // Sort the annotations by start sample so that decoders
376 // can't confuse us by creating annotations out of order
377 stable_sort(annotations.begin(), annotations.end(),
378 [](const Annotation &a, const Annotation &b) {
379 return a.start_sample() < b.start_sample(); });
381 // Gather all annotations that form a visual "block" and draw them as such
382 for (const Annotation &a : annotations) {
384 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
385 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
386 const int a_width = a_end - a_start;
388 const int delta = a_end - p_end;
390 bool a_is_separate = false;
392 // Annotation wider than the threshold for a useful label width?
393 if (a_width >= min_useful_label_width_) {
394 for (const QString &ann_text : a.annotations()) {
395 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
396 // Annotation wide enough to fit a label? Don't put it in a block then
398 a_is_separate = true;
404 // Were the previous and this annotation more than a pixel apart?
405 if ((abs(delta) > 1) || a_is_separate) {
406 // Block was broken, draw annotations that form the current block
407 if (a_block.size() == 1) {
408 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
412 draw_annotation_block(a_block, p, h, y, base_colour);
418 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
419 // Next annotation must start a new block. delta will be > 1
420 // because we set p_end to INT_MIN but that's okay since
421 // a_block will be empty, so nothing will be drawn
424 a_block.push_back(a);
429 if (a_block.size() == 1)
430 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
433 draw_annotation_block(a_block, p, h, y, base_colour);
436 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
437 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
438 size_t base_colour, int row_title_width) const
440 double samples_per_pixel, pixels_offset;
441 tie(pixels_offset, samples_per_pixel) =
442 get_pixels_offset_samples_per_pixel();
444 const double start = a.start_sample() / samples_per_pixel -
446 const double end = a.end_sample() / samples_per_pixel -
449 const size_t colour = (base_colour + a.format()) % countof(Colours);
450 p.setPen(OutlineColours[colour]);
451 p.setBrush(Colours[colour]);
453 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
456 if (a.start_sample() == a.end_sample())
457 draw_instant(a, p, h, start, y);
459 draw_range(a, p, h, start, end, y, pp,
463 void DecodeTrace::draw_annotation_block(
464 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
465 int y, size_t base_colour) const
467 using namespace pv::data::decode;
469 if (annotations.empty())
472 double samples_per_pixel, pixels_offset;
473 tie(pixels_offset, samples_per_pixel) =
474 get_pixels_offset_samples_per_pixel();
476 const double start = annotations.front().start_sample() /
477 samples_per_pixel - pixels_offset;
478 const double end = annotations.back().end_sample() /
479 samples_per_pixel - pixels_offset;
481 const double top = y + .5 - h / 2;
482 const double bottom = y + .5 + h / 2;
484 const size_t colour = (base_colour + annotations.front().format()) %
487 // Check if all annotations are of the same type (i.e. we can use one color)
488 // or if we should use a neutral color (i.e. gray)
489 const int format = annotations.front().format();
490 const bool single_format = std::all_of(
491 annotations.begin(), annotations.end(),
492 [&](const Annotation &a) { return a.format() == format; });
494 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
495 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
498 QRectF(start, top, end - start, bottom - top), h/4, h/4);
501 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
502 int h, double x, int y) const
504 const QString text = a.annotations().empty() ?
505 QString() : a.annotations().back();
506 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
508 const QRectF rect(x - w / 2, y - h / 2, w, h);
510 p.drawRoundedRect(rect, h / 2, h / 2);
513 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
516 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
517 int h, double start, double end, int y, const ViewItemPaintParams &pp,
518 int row_title_width) const
520 const double top = y + .5 - h / 2;
521 const double bottom = y + .5 + h / 2;
522 const vector<QString> annotations = a.annotations();
524 // If the two ends are within 1 pixel, draw a vertical line
525 if (start + 1.0 > end) {
526 p.drawLine(QPointF(start, top), QPointF(start, bottom));
530 const double cap_width = min((end - start) / 4, EndCapWidth);
533 QPointF(start, y + .5f),
534 QPointF(start + cap_width, top),
535 QPointF(end - cap_width, top),
536 QPointF(end, y + .5f),
537 QPointF(end - cap_width, bottom),
538 QPointF(start + cap_width, bottom)
541 p.drawConvexPolygon(pts, countof(pts));
543 if (annotations.empty())
546 const int ann_start = start + cap_width;
547 const int ann_end = end - cap_width;
549 const int real_start = std::max(ann_start, pp.left() + row_title_width);
550 const int real_end = std::min(ann_end, pp.right());
551 const int real_width = real_end - real_start;
553 QRectF rect(real_start, y - h / 2, real_width, h);
554 if (rect.width() <= 4)
559 // Try to find an annotation that will fit
560 QString best_annotation;
563 for (const QString &a : annotations) {
564 const int w = p.boundingRect(QRectF(), 0, a).width();
565 if (w <= rect.width() && w > best_width)
566 best_annotation = a, best_width = w;
569 if (best_annotation.isEmpty())
570 best_annotation = annotations.back();
572 // If not ellide the last in the list
573 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
574 best_annotation, Qt::ElideRight, rect.width()));
577 void DecodeTrace::draw_error(QPainter &p, const QString &message,
578 const ViewItemPaintParams &pp)
580 const int y = get_visual_y();
582 p.setPen(ErrorBgColour.darker());
583 p.setBrush(ErrorBgColour);
585 const QRectF bounding_rect =
586 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
587 const QRectF text_rect = p.boundingRect(bounding_rect,
588 Qt::AlignCenter, message);
589 const float r = text_rect.height() / 4;
591 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
595 p.drawText(text_rect, message);
598 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
601 using namespace pv::data;
602 using pv::data::decode::Decoder;
604 double samples_per_pixel, pixels_offset;
606 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
607 base_->decoder_stack();
609 assert(decoder_stack);
611 shared_ptr<Logic> data;
612 shared_ptr<data::SignalBase> signalbase;
614 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
616 // We get the logic data of the first channel in the list.
617 // This works because we are currently assuming all
618 // LogicSignals have the same data/segment
619 for (const shared_ptr<Decoder> &dec : stack)
620 if (dec && !dec->channels().empty() &&
621 ((signalbase = (*dec->channels().begin()).second)) &&
622 ((data = signalbase->logic_data())))
625 if (!data || data->logic_segments().empty())
628 const shared_ptr<LogicSegment> segment =
629 data->logic_segments().front();
631 const int64_t sample_count = (int64_t)segment->get_sample_count();
632 if (sample_count == 0)
635 const int64_t samples_decoded = decoder_stack->samples_decoded();
636 if (sample_count == samples_decoded)
639 const int y = get_visual_y();
641 tie(pixels_offset, samples_per_pixel) =
642 get_pixels_offset_samples_per_pixel();
644 const double start = max(samples_decoded /
645 samples_per_pixel - pixels_offset, left - 1.0);
646 const double end = min(sample_count / samples_per_pixel -
647 pixels_offset, right + 1.0);
648 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
650 p.setPen(QPen(Qt::NoPen));
651 p.setBrush(Qt::white);
652 p.drawRect(no_decode_rect);
654 p.setPen(NoDecodeColour);
655 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
656 p.drawRect(no_decode_rect);
659 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
661 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
662 base_->decoder_stack();
665 assert(decoder_stack);
667 const View *view = owner_->view();
670 const double scale = view->scale();
673 const double pixels_offset =
674 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
676 double samplerate = decoder_stack->samplerate();
678 // Show sample rate as 1Hz when it is unknown
679 if (samplerate == 0.0)
682 return make_pair(pixels_offset, samplerate * scale);
685 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
686 int x_start, int x_end) const
688 double samples_per_pixel, pixels_offset;
689 tie(pixels_offset, samples_per_pixel) =
690 get_pixels_offset_samples_per_pixel();
692 const uint64_t start = (uint64_t)max(
693 (x_start + pixels_offset) * samples_per_pixel, 0.0);
694 const uint64_t end = (uint64_t)max(
695 (x_end + pixels_offset) * samples_per_pixel, 0.0);
697 return make_pair(start, end);
700 int DecodeTrace::get_row_at_point(const QPoint &point)
705 const int y = (point.y() - get_visual_y() + row_height_ / 2);
707 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
711 const int row = y / row_height_;
713 if (row >= (int)visible_rows_.size())
719 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
721 using namespace pv::data::decode;
726 const pair<uint64_t, uint64_t> sample_range =
727 get_sample_range(point.x(), point.x() + 1);
728 const int row = get_row_at_point(point);
732 vector<pv::data::decode::Annotation> annotations;
734 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
735 base_->decoder_stack();
737 assert(decoder_stack);
738 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
739 sample_range.first, sample_range.second);
741 return (annotations.empty()) ?
742 QString() : annotations[0].annotations().front();
745 void DecodeTrace::hover_point_changed()
749 const View *const view = owner_->view();
752 QPoint hp = view->hover_point();
753 QString ann = get_annotation_at_point(hp);
757 if (!row_height_ || ann.isEmpty()) {
758 QToolTip::hideText();
762 const int hover_row = get_row_at_point(hp);
764 QFontMetrics m(QToolTip::font());
765 const QRect text_size = m.boundingRect(QRect(), 0, ann);
767 // This is OS-specific and unfortunately we can't query it, so
768 // use an approximation to at least try to minimize the error.
769 const int padding = 8;
771 // Make sure the tool tip doesn't overlap with the mouse cursor.
772 // If it did, the tool tip would constantly hide and re-appear.
773 // We also push it up by one row so that it appears above the
774 // decode trace, not below.
775 hp.setX(hp.x() - (text_size.width() / 2) - padding);
777 hp.setY(get_visual_y() - (row_height_ / 2) +
778 (hover_row * row_height_) -
779 row_height_ - text_size.height() - padding);
781 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
784 void DecodeTrace::create_decoder_form(int index,
785 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
791 const srd_decoder *const decoder = dec->decoder();
794 const bool decoder_deletable = index > 0;
796 pv::widgets::DecoderGroupBox *const group =
797 new pv::widgets::DecoderGroupBox(
798 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
799 group->set_decoder_visible(dec->shown());
801 if (decoder_deletable) {
802 delete_mapper_.setMapping(group, index);
803 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
806 show_hide_mapper_.setMapping(group, index);
807 connect(group, SIGNAL(show_hide_decoder()),
808 &show_hide_mapper_, SLOT(map()));
810 QFormLayout *const decoder_form = new QFormLayout;
811 group->add_layout(decoder_form);
813 // Add the mandatory channels
814 for (l = decoder->channels; l; l = l->next) {
815 const struct srd_channel *const pdch =
816 (struct srd_channel *)l->data;
817 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
818 connect(combo, SIGNAL(currentIndexChanged(int)),
819 this, SLOT(on_channel_selected(int)));
820 decoder_form->addRow(tr("<b>%1</b> (%2) *")
821 .arg(QString::fromUtf8(pdch->name),
822 QString::fromUtf8(pdch->desc)), combo);
824 const ChannelSelector s = {combo, dec, pdch};
825 channel_selectors_.push_back(s);
828 // Add the optional channels
829 for (l = decoder->opt_channels; l; l = l->next) {
830 const struct srd_channel *const pdch =
831 (struct srd_channel *)l->data;
832 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
833 connect(combo, SIGNAL(currentIndexChanged(int)),
834 this, SLOT(on_channel_selected(int)));
835 decoder_form->addRow(tr("<b>%1</b> (%2)")
836 .arg(QString::fromUtf8(pdch->name),
837 QString::fromUtf8(pdch->desc)), combo);
839 const ChannelSelector s = {combo, dec, pdch};
840 channel_selectors_.push_back(s);
843 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
844 base_->decoder_stack();
847 shared_ptr<binding::Decoder> binding(
848 new binding::Decoder(decoder_stack, dec));
849 binding->add_properties_to_form(decoder_form, true);
851 bindings_.push_back(binding);
854 decoder_forms_.push_back(group);
857 QComboBox* DecodeTrace::create_channel_selector(
858 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
859 const srd_channel *const pdch)
863 const auto sigs(session_.signalbases());
865 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
866 std::sort(sig_list.begin(), sig_list.end(),
867 [](const shared_ptr<data::SignalBase> &a,
868 const shared_ptr<data::SignalBase> &b) {
869 return strnatcasecmp(a->name().toStdString(),
870 b->name().toStdString()) < 0; });
872 const auto channel_iter = dec->channels().find(pdch);
874 QComboBox *selector = new QComboBox(parent);
876 selector->addItem("-", qVariantFromValue((void*)nullptr));
878 if (channel_iter == dec->channels().end())
879 selector->setCurrentIndex(0);
881 for (const shared_ptr<data::SignalBase> &b : sig_list) {
883 if (b->type() == sigrok::ChannelType::LOGIC && b->enabled()) {
884 selector->addItem(b->name(),
885 qVariantFromValue((void*)b.get()));
887 if (channel_iter != dec->channels().end() &&
888 (*channel_iter).second == b)
889 selector->setCurrentIndex(
890 selector->count() - 1);
897 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
901 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
903 const unordered_set< shared_ptr<data::SignalBase> >
904 sigs(session_.signalbases());
906 for (const ChannelSelector &s : channel_selectors_) {
907 if (s.decoder_ != dec)
910 const data::SignalBase *const selection =
911 (data::SignalBase*)s.combo_->itemData(
912 s.combo_->currentIndex()).value<void*>();
914 for (shared_ptr<data::SignalBase> sig : sigs)
915 if (sig.get() == selection) {
916 channel_map[s.pdch_] = sig;
921 dec->set_channels(channel_map);
924 void DecodeTrace::commit_channels()
926 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
927 base_->decoder_stack();
929 assert(decoder_stack);
930 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
931 commit_decoder_channels(dec);
933 decoder_stack->begin_decode();
936 void DecodeTrace::on_new_decode_data()
939 owner_->row_item_appearance_changed(false, true);
942 void DecodeTrace::delete_pressed()
947 void DecodeTrace::on_delete()
949 session_.remove_decode_signal(base_);
952 void DecodeTrace::on_channel_selected(int)
957 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
959 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
960 base_->decoder_stack();
963 assert(decoder_stack);
964 decoder_stack->push(shared_ptr<data::decode::Decoder>(
965 new data::decode::Decoder(decoder)));
966 decoder_stack->begin_decode();
971 void DecodeTrace::on_delete_decoder(int index)
973 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
974 base_->decoder_stack();
976 decoder_stack->remove(index);
981 decoder_stack->begin_decode();
984 void DecodeTrace::on_show_hide_decoder(int index)
986 using pv::data::decode::Decoder;
988 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
989 base_->decoder_stack();
991 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
993 // Find the decoder in the stack
994 auto iter = stack.cbegin();
995 for (int i = 0; i < index; i++, iter++)
996 assert(iter != stack.end());
998 shared_ptr<Decoder> dec = *iter;
1001 const bool show = !dec->shown();
1004 assert(index < (int)decoder_forms_.size());
1005 decoder_forms_[index]->set_decoder_visible(show);
1008 owner_->row_item_appearance_changed(false, true);
1011 } // namespace TraceView
1012 } // namespace views