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/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;
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 shared_ptr<data::SignalBase> signalbase, int index) :
135 signalbase_(signalbase),
137 max_visible_rows_(0),
138 delete_mapper_(this),
139 show_hide_mapper_(this)
141 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
142 signalbase_->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 signalbase_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
149 signalbase_->set_colour(DecodeColours[index % countof(DecodeColours)]);
151 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
152 this, SLOT(on_new_decode_data()));
153 connect(&delete_mapper_, SIGNAL(mapped(int)),
154 this, SLOT(on_delete_decoder(int)));
155 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
156 this, SLOT(on_show_hide_decoder(int)));
159 bool DecodeTrace::enabled() const
164 std::shared_ptr<data::SignalBase> DecodeTrace::base() const
169 pair<int, int> DecodeTrace::v_extents() const
171 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
173 // Make an empty decode trace appear symmetrical
174 const int row_count = max(1, max_visible_rows_);
176 return make_pair(-row_height, row_height * row_count);
179 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
181 Trace::paint_back(p, pp);
182 paint_axis(p, pp, get_visual_y());
185 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
187 using namespace pv::data::decode;
189 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
190 signalbase_->decoder_stack();
192 const int text_height = ViewItemPaintParams::text_height();
193 row_height_ = (text_height * 6) / 4;
194 const int annotation_height = (text_height * 5) / 4;
196 assert(decoder_stack);
197 const QString err = decoder_stack->error_message();
198 if (!err.isEmpty()) {
199 draw_unresolved_period(
200 p, annotation_height, pp.left(), pp.right());
201 draw_error(p, err, pp);
205 // Set default pen to allow for text width calculation
208 // Iterate through the rows
209 int y = get_visual_y();
210 pair<uint64_t, uint64_t> sample_range = get_sample_range(
211 pp.left(), pp.right());
213 const vector<Row> rows(decoder_stack->get_visible_rows());
215 visible_rows_.clear();
216 for (const Row& row : rows) {
217 // Cache the row title widths
220 row_title_width = row_title_widths_.at(row);
221 } catch (std::out_of_range) {
222 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
224 row_title_widths_[row] = w;
228 // Determine the row's color
229 size_t base_colour = 0x13579BDF;
230 boost::hash_combine(base_colour, this);
231 boost::hash_combine(base_colour, row.decoder());
232 boost::hash_combine(base_colour, row.row());
235 vector<Annotation> annotations;
236 decoder_stack->get_annotation_subset(annotations, row,
237 sample_range.first, sample_range.second);
238 if (!annotations.empty()) {
239 draw_annotations(annotations, p, annotation_height, pp, y,
240 base_colour, row_title_width);
244 visible_rows_.push_back(row);
249 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
251 if ((int)visible_rows_.size() > max_visible_rows_)
252 owner_->extents_changed(false, true);
254 // Update the maximum row count if needed
255 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
258 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
260 using namespace pv::data::decode;
264 for (size_t i = 0; i < visible_rows_.size(); i++) {
265 const int y = i * row_height_ + get_visual_y();
267 p.setPen(QPen(Qt::NoPen));
268 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
271 const QPointF points[] = {
272 QPointF(pp.left(), y - ArrowSize),
273 QPointF(pp.left() + ArrowSize, y),
274 QPointF(pp.left(), y + ArrowSize)
276 p.drawPolygon(points, countof(points));
279 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
280 pp.right() - pp.left(), row_height_);
281 const QString h(visible_rows_[i].title());
282 const int f = Qt::AlignLeft | Qt::AlignVCenter |
286 p.setPen(QApplication::palette().color(QPalette::Base));
287 for (int dx = -1; dx <= 1; dx++)
288 for (int dy = -1; dy <= 1; dy++)
289 if (dx != 0 && dy != 0)
290 p.drawText(r.translated(dx, dy), f, h);
293 p.setPen(QApplication::palette().color(QPalette::WindowText));
298 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
300 using pv::data::decode::Decoder;
302 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
303 signalbase_->decoder_stack();
307 assert(decoder_stack);
309 // Add the standard options
310 Trace::populate_popup_form(parent, form);
312 // Add the decoder options
314 channel_selectors_.clear();
315 decoder_forms_.clear();
317 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
320 QLabel *const l = new QLabel(
321 tr("<p><i>No decoders in the stack</i></p>"));
322 l->setAlignment(Qt::AlignCenter);
325 auto iter = stack.cbegin();
326 for (int i = 0; i < (int)stack.size(); i++, iter++) {
327 shared_ptr<Decoder> dec(*iter);
328 create_decoder_form(i, dec, parent, form);
331 form->addRow(new QLabel(
332 tr("<i>* Required channels</i>"), parent));
335 // Add stacking button
336 pv::widgets::DecoderMenu *const decoder_menu =
337 new pv::widgets::DecoderMenu(parent);
338 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
339 this, SLOT(on_stack_decoder(srd_decoder*)));
341 QPushButton *const stack_button =
342 new QPushButton(tr("Stack Decoder"), parent);
343 stack_button->setMenu(decoder_menu);
345 QHBoxLayout *stack_button_box = new QHBoxLayout;
346 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
347 form->addRow(stack_button_box);
350 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
352 QMenu *const menu = Trace::create_context_menu(parent);
354 menu->addSeparator();
356 QAction *const del = new QAction(tr("Delete"), this);
357 del->setShortcuts(QKeySequence::Delete);
358 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
359 menu->addAction(del);
364 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
365 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
366 size_t base_colour, int row_title_width)
368 using namespace pv::data::decode;
370 vector<Annotation> a_block;
373 double samples_per_pixel, pixels_offset;
374 tie(pixels_offset, samples_per_pixel) =
375 get_pixels_offset_samples_per_pixel();
377 // Sort the annotations by start sample so that decoders
378 // can't confuse us by creating annotations out of order
379 stable_sort(annotations.begin(), annotations.end(),
380 [](const Annotation &a, const Annotation &b) {
381 return a.start_sample() < b.start_sample(); });
383 // Gather all annotations that form a visual "block" and draw them as such
384 for (const Annotation &a : annotations) {
386 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
387 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
388 const int a_width = a_end - a_start;
390 const int delta = a_end - p_end;
392 bool a_is_separate = false;
394 // Annotation wider than the threshold for a useful label width?
395 if (a_width >= min_useful_label_width_) {
396 for (const QString &ann_text : a.annotations()) {
397 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
398 // Annotation wide enough to fit a label? Don't put it in a block then
400 a_is_separate = true;
406 // Were the previous and this annotation more than a pixel apart?
407 if ((abs(delta) > 1) || a_is_separate) {
408 // Block was broken, draw annotations that form the current block
409 if (a_block.size() == 1) {
410 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
414 draw_annotation_block(a_block, p, h, y, base_colour);
420 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
421 // Next annotation must start a new block. delta will be > 1
422 // because we set p_end to INT_MIN but that's okay since
423 // a_block will be empty, so nothing will be drawn
426 a_block.push_back(a);
431 if (a_block.size() == 1)
432 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
435 draw_annotation_block(a_block, p, h, y, base_colour);
438 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
439 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
440 size_t base_colour, int row_title_width) const
442 double samples_per_pixel, pixels_offset;
443 tie(pixels_offset, samples_per_pixel) =
444 get_pixels_offset_samples_per_pixel();
446 const double start = a.start_sample() / samples_per_pixel -
448 const double end = a.end_sample() / samples_per_pixel -
451 const size_t colour = (base_colour + a.format()) % countof(Colours);
452 p.setPen(OutlineColours[colour]);
453 p.setBrush(Colours[colour]);
455 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
458 if (a.start_sample() == a.end_sample())
459 draw_instant(a, p, h, start, y);
461 draw_range(a, p, h, start, end, y, pp,
465 void DecodeTrace::draw_annotation_block(
466 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
467 int y, size_t base_colour) const
469 using namespace pv::data::decode;
471 if (annotations.empty())
474 double samples_per_pixel, pixels_offset;
475 tie(pixels_offset, samples_per_pixel) =
476 get_pixels_offset_samples_per_pixel();
478 const double start = annotations.front().start_sample() /
479 samples_per_pixel - pixels_offset;
480 const double end = annotations.back().end_sample() /
481 samples_per_pixel - pixels_offset;
483 const double top = y + .5 - h / 2;
484 const double bottom = y + .5 + h / 2;
486 const size_t colour = (base_colour + annotations.front().format()) %
489 // Check if all annotations are of the same type (i.e. we can use one color)
490 // or if we should use a neutral color (i.e. gray)
491 const int format = annotations.front().format();
492 const bool single_format = std::all_of(
493 annotations.begin(), annotations.end(),
494 [&](const Annotation &a) { return a.format() == format; });
496 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
497 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
500 QRectF(start, top, end - start, bottom - top), h/4, h/4);
503 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
504 int h, double x, int y) const
506 const QString text = a.annotations().empty() ?
507 QString() : a.annotations().back();
508 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
510 const QRectF rect(x - w / 2, y - h / 2, w, h);
512 p.drawRoundedRect(rect, h / 2, h / 2);
515 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
518 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
519 int h, double start, double end, int y, const ViewItemPaintParams &pp,
520 int row_title_width) const
522 const double top = y + .5 - h / 2;
523 const double bottom = y + .5 + h / 2;
524 const vector<QString> annotations = a.annotations();
526 // If the two ends are within 1 pixel, draw a vertical line
527 if (start + 1.0 > end) {
528 p.drawLine(QPointF(start, top), QPointF(start, bottom));
532 const double cap_width = min((end - start) / 4, EndCapWidth);
535 QPointF(start, y + .5f),
536 QPointF(start + cap_width, top),
537 QPointF(end - cap_width, top),
538 QPointF(end, y + .5f),
539 QPointF(end - cap_width, bottom),
540 QPointF(start + cap_width, bottom)
543 p.drawConvexPolygon(pts, countof(pts));
545 if (annotations.empty())
548 const int ann_start = start + cap_width;
549 const int ann_end = end - cap_width;
551 const int real_start = std::max(ann_start, pp.left() + row_title_width);
552 const int real_end = std::min(ann_end, pp.right());
553 const int real_width = real_end - real_start;
555 QRectF rect(real_start, y - h / 2, real_width, h);
556 if (rect.width() <= 4)
561 // Try to find an annotation that will fit
562 QString best_annotation;
565 for (const QString &a : annotations) {
566 const int w = p.boundingRect(QRectF(), 0, a).width();
567 if (w <= rect.width() && w > best_width)
568 best_annotation = a, best_width = w;
571 if (best_annotation.isEmpty())
572 best_annotation = annotations.back();
574 // If not ellide the last in the list
575 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
576 best_annotation, Qt::ElideRight, rect.width()));
579 void DecodeTrace::draw_error(QPainter &p, const QString &message,
580 const ViewItemPaintParams &pp)
582 const int y = get_visual_y();
584 p.setPen(ErrorBgColour.darker());
585 p.setBrush(ErrorBgColour);
587 const QRectF bounding_rect =
588 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
589 const QRectF text_rect = p.boundingRect(bounding_rect,
590 Qt::AlignCenter, message);
591 const float r = text_rect.height() / 4;
593 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
597 p.drawText(text_rect, message);
600 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
603 using namespace pv::data;
604 using pv::data::decode::Decoder;
606 double samples_per_pixel, pixels_offset;
608 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
609 signalbase_->decoder_stack();
611 assert(decoder_stack);
613 shared_ptr<Logic> data;
614 shared_ptr<data::SignalBase> signalbase;
616 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
618 // We get the logic data of the first channel in the list.
619 // This works because we are currently assuming all
620 // LogicSignals have the same data/segment
621 for (const shared_ptr<Decoder> &dec : stack)
622 if (dec && !dec->channels().empty() &&
623 ((signalbase = (*dec->channels().begin()).second)) &&
624 ((data = signalbase->logic_data())))
627 if (!data || data->logic_segments().empty())
630 const shared_ptr<LogicSegment> segment =
631 data->logic_segments().front();
633 const int64_t sample_count = (int64_t)segment->get_sample_count();
634 if (sample_count == 0)
637 const int64_t samples_decoded = decoder_stack->samples_decoded();
638 if (sample_count == samples_decoded)
641 const int y = get_visual_y();
643 tie(pixels_offset, samples_per_pixel) =
644 get_pixels_offset_samples_per_pixel();
646 const double start = max(samples_decoded /
647 samples_per_pixel - pixels_offset, left - 1.0);
648 const double end = min(sample_count / samples_per_pixel -
649 pixels_offset, right + 1.0);
650 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
652 p.setPen(QPen(Qt::NoPen));
653 p.setBrush(Qt::white);
654 p.drawRect(no_decode_rect);
656 p.setPen(NoDecodeColour);
657 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
658 p.drawRect(no_decode_rect);
661 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
663 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
664 signalbase_->decoder_stack();
667 assert(decoder_stack);
669 const View *view = owner_->view();
672 const double scale = view->scale();
675 const double pixels_offset =
676 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
678 double samplerate = decoder_stack->samplerate();
680 // Show sample rate as 1Hz when it is unknown
681 if (samplerate == 0.0)
684 return make_pair(pixels_offset, samplerate * scale);
687 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
688 int x_start, int x_end) const
690 double samples_per_pixel, pixels_offset;
691 tie(pixels_offset, samples_per_pixel) =
692 get_pixels_offset_samples_per_pixel();
694 const uint64_t start = (uint64_t)max(
695 (x_start + pixels_offset) * samples_per_pixel, 0.0);
696 const uint64_t end = (uint64_t)max(
697 (x_end + pixels_offset) * samples_per_pixel, 0.0);
699 return make_pair(start, end);
702 int DecodeTrace::get_row_at_point(const QPoint &point)
707 const int y = (point.y() - get_visual_y() + row_height_ / 2);
709 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
713 const int row = y / row_height_;
715 if (row >= (int)visible_rows_.size())
721 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
723 using namespace pv::data::decode;
728 const pair<uint64_t, uint64_t> sample_range =
729 get_sample_range(point.x(), point.x() + 1);
730 const int row = get_row_at_point(point);
734 vector<pv::data::decode::Annotation> annotations;
736 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
737 signalbase_->decoder_stack();
739 assert(decoder_stack);
740 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
741 sample_range.first, sample_range.second);
743 return (annotations.empty()) ?
744 QString() : annotations[0].annotations().front();
747 void DecodeTrace::hover_point_changed()
751 const View *const view = owner_->view();
754 QPoint hp = view->hover_point();
755 QString ann = get_annotation_at_point(hp);
759 if (!row_height_ || ann.isEmpty()) {
760 QToolTip::hideText();
764 const int hover_row = get_row_at_point(hp);
766 QFontMetrics m(QToolTip::font());
767 const QRect text_size = m.boundingRect(QRect(), 0, ann);
769 // This is OS-specific and unfortunately we can't query it, so
770 // use an approximation to at least try to minimize the error.
771 const int padding = 8;
773 // Make sure the tool tip doesn't overlap with the mouse cursor.
774 // If it did, the tool tip would constantly hide and re-appear.
775 // We also push it up by one row so that it appears above the
776 // decode trace, not below.
777 hp.setX(hp.x() - (text_size.width() / 2) - padding);
779 hp.setY(get_visual_y() - (row_height_ / 2) +
780 (hover_row * row_height_) -
781 row_height_ - text_size.height() - padding);
783 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
786 void DecodeTrace::create_decoder_form(int index,
787 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
793 const srd_decoder *const decoder = dec->decoder();
796 const bool decoder_deletable = index > 0;
798 pv::widgets::DecoderGroupBox *const group =
799 new pv::widgets::DecoderGroupBox(
800 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
801 group->set_decoder_visible(dec->shown());
803 if (decoder_deletable) {
804 delete_mapper_.setMapping(group, index);
805 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
808 show_hide_mapper_.setMapping(group, index);
809 connect(group, SIGNAL(show_hide_decoder()),
810 &show_hide_mapper_, SLOT(map()));
812 QFormLayout *const decoder_form = new QFormLayout;
813 group->add_layout(decoder_form);
815 // Add the mandatory channels
816 for (l = decoder->channels; l; l = l->next) {
817 const struct srd_channel *const pdch =
818 (struct srd_channel *)l->data;
819 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
820 connect(combo, SIGNAL(currentIndexChanged(int)),
821 this, SLOT(on_channel_selected(int)));
822 decoder_form->addRow(tr("<b>%1</b> (%2) *")
823 .arg(QString::fromUtf8(pdch->name),
824 QString::fromUtf8(pdch->desc)), combo);
826 const ChannelSelector s = {combo, dec, pdch};
827 channel_selectors_.push_back(s);
830 // Add the optional channels
831 for (l = decoder->opt_channels; l; l = l->next) {
832 const struct srd_channel *const pdch =
833 (struct srd_channel *)l->data;
834 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
835 connect(combo, SIGNAL(currentIndexChanged(int)),
836 this, SLOT(on_channel_selected(int)));
837 decoder_form->addRow(tr("<b>%1</b> (%2)")
838 .arg(QString::fromUtf8(pdch->name),
839 QString::fromUtf8(pdch->desc)), combo);
841 const ChannelSelector s = {combo, dec, pdch};
842 channel_selectors_.push_back(s);
845 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
846 signalbase_->decoder_stack();
849 shared_ptr<binding::Decoder> binding(
850 new binding::Decoder(decoder_stack, dec));
851 binding->add_properties_to_form(decoder_form, true);
853 bindings_.push_back(binding);
856 decoder_forms_.push_back(group);
859 QComboBox* DecodeTrace::create_channel_selector(
860 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
861 const srd_channel *const pdch)
865 const auto sigs(session_.signalbases());
867 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
868 std::sort(sig_list.begin(), sig_list.end(),
869 [](const shared_ptr<data::SignalBase> &a,
870 const shared_ptr<data::SignalBase> &b) {
871 return strnatcasecmp(a->name().toStdString(),
872 b->name().toStdString()) < 0; });
874 const auto channel_iter = dec->channels().find(pdch);
876 QComboBox *selector = new QComboBox(parent);
878 selector->addItem("-", qVariantFromValue((void*)nullptr));
880 if (channel_iter == dec->channels().end())
881 selector->setCurrentIndex(0);
883 for (const shared_ptr<data::SignalBase> &b : sig_list) {
885 if (b->type() == sigrok::ChannelType::LOGIC && b->enabled()) {
886 selector->addItem(b->name(),
887 qVariantFromValue((void*)b.get()));
889 if (channel_iter != dec->channels().end() &&
890 (*channel_iter).second == b)
891 selector->setCurrentIndex(
892 selector->count() - 1);
899 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
903 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
905 const unordered_set< shared_ptr<data::SignalBase> >
906 sigs(session_.signalbases());
908 for (const ChannelSelector &s : channel_selectors_) {
909 if (s.decoder_ != dec)
912 const data::SignalBase *const selection =
913 (data::SignalBase*)s.combo_->itemData(
914 s.combo_->currentIndex()).value<void*>();
916 for (shared_ptr<data::SignalBase> sig : sigs)
917 if (sig.get() == selection) {
918 channel_map[s.pdch_] = sig;
923 dec->set_channels(channel_map);
926 void DecodeTrace::commit_channels()
928 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
929 signalbase_->decoder_stack();
931 assert(decoder_stack);
932 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
933 commit_decoder_channels(dec);
935 decoder_stack->begin_decode();
938 void DecodeTrace::on_new_decode_data()
941 owner_->row_item_appearance_changed(false, true);
944 void DecodeTrace::delete_pressed()
949 void DecodeTrace::on_delete()
951 session_.remove_decode_signal(base_);
954 void DecodeTrace::on_channel_selected(int)
959 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
961 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
962 signalbase_->decoder_stack();
965 assert(decoder_stack);
966 decoder_stack->push(shared_ptr<data::decode::Decoder>(
967 new data::decode::Decoder(decoder)));
968 decoder_stack->begin_decode();
973 void DecodeTrace::on_delete_decoder(int index)
975 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
976 signalbase_->decoder_stack();
978 decoder_stack->remove(index);
983 decoder_stack->begin_decode();
986 void DecodeTrace::on_show_hide_decoder(int index)
988 using pv::data::decode::Decoder;
990 std::shared_ptr<pv::data::DecoderStack> decoder_stack =
991 signalbase_->decoder_stack();
993 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
995 // Find the decoder in the stack
996 auto iter = stack.cbegin();
997 for (int i = 0; i < index; i++, iter++)
998 assert(iter != stack.end());
1000 shared_ptr<Decoder> dec = *iter;
1003 const bool show = !dec->shown();
1006 assert(index < (int)decoder_forms_.size());
1007 decoder_forms_[index]->set_decoder_visible(show);
1010 owner_->row_item_appearance_changed(false, true);
1013 } // namespace TraceView
1014 } // namespace views