2 * This file is part of the PulseView project.
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <libsigrokdecode/libsigrokdecode.h>
31 #include <boost/functional/hash.hpp>
32 #include <boost/thread/locks.hpp>
33 #include <boost/thread/shared_mutex.hpp>
36 #include <QApplication>
38 #include <QFormLayout>
41 #include <QPushButton>
44 #include "decodetrace.hpp"
46 #include <pv/session.hpp>
47 #include <pv/data/decoderstack.hpp>
48 #include <pv/data/decode/decoder.hpp>
49 #include <pv/data/logic.hpp>
50 #include <pv/data/logicsegment.hpp>
51 #include <pv/data/decode/annotation.hpp>
52 #include <pv/view/logicsignal.hpp>
53 #include <pv/view/view.hpp>
54 #include <pv/view/viewport.hpp>
55 #include <pv/widgets/decodergroupbox.hpp>
56 #include <pv/widgets/decodermenu.hpp>
58 using boost::shared_lock;
59 using boost::shared_mutex;
60 using std::dynamic_pointer_cast;
62 using std::lock_guard;
69 using std::shared_ptr;
71 using std::unordered_set;
77 const QColor DecodeTrace::DecodeColours[4] = {
78 QColor(0xEF, 0x29, 0x29), // Red
79 QColor(0xFC, 0xE9, 0x4F), // Yellow
80 QColor(0x8A, 0xE2, 0x34), // Green
81 QColor(0x72, 0x9F, 0xCF) // Blue
84 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
85 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
87 const int DecodeTrace::ArrowSize = 4;
88 const double DecodeTrace::EndCapWidth = 5;
89 const int DecodeTrace::DrawPadding = 100;
91 const QColor DecodeTrace::Colours[16] = {
92 QColor(0xEF, 0x29, 0x29),
93 QColor(0xF6, 0x6A, 0x32),
94 QColor(0xFC, 0xAE, 0x3E),
95 QColor(0xFB, 0xCA, 0x47),
96 QColor(0xFC, 0xE9, 0x4F),
97 QColor(0xCD, 0xF0, 0x40),
98 QColor(0x8A, 0xE2, 0x34),
99 QColor(0x4E, 0xDC, 0x44),
100 QColor(0x55, 0xD7, 0x95),
101 QColor(0x64, 0xD1, 0xD2),
102 QColor(0x72, 0x9F, 0xCF),
103 QColor(0xD4, 0x76, 0xC4),
104 QColor(0x9D, 0x79, 0xB9),
105 QColor(0xAD, 0x7F, 0xA8),
106 QColor(0xC2, 0x62, 0x9B),
107 QColor(0xD7, 0x47, 0x6F)
110 const QColor DecodeTrace::OutlineColours[16] = {
111 QColor(0x77, 0x14, 0x14),
112 QColor(0x7B, 0x35, 0x19),
113 QColor(0x7E, 0x57, 0x1F),
114 QColor(0x7D, 0x65, 0x23),
115 QColor(0x7E, 0x74, 0x27),
116 QColor(0x66, 0x78, 0x20),
117 QColor(0x45, 0x71, 0x1A),
118 QColor(0x27, 0x6E, 0x22),
119 QColor(0x2A, 0x6B, 0x4A),
120 QColor(0x32, 0x68, 0x69),
121 QColor(0x39, 0x4F, 0x67),
122 QColor(0x6A, 0x3B, 0x62),
123 QColor(0x4E, 0x3C, 0x5C),
124 QColor(0x56, 0x3F, 0x54),
125 QColor(0x61, 0x31, 0x4D),
126 QColor(0x6B, 0x23, 0x37)
129 DecodeTrace::DecodeTrace(pv::Session &session,
130 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
131 Trace(QString::fromUtf8(
132 decoder_stack->stack().front()->decoder()->name)),
134 decoder_stack_(decoder_stack),
136 max_visible_rows_(0),
137 delete_mapper_(this),
138 show_hide_mapper_(this)
140 assert(decoder_stack_);
142 set_colour(DecodeColours[index % countof(DecodeColours)]);
144 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
145 this, SLOT(on_new_decode_data()));
146 connect(&delete_mapper_, SIGNAL(mapped(int)),
147 this, SLOT(on_delete_decoder(int)));
148 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
149 this, SLOT(on_show_hide_decoder(int)));
152 bool DecodeTrace::enabled() const
157 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
159 return decoder_stack_;
162 pair<int, int> DecodeTrace::v_extents() const
164 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
166 return make_pair(-row_height, row_height * max_visible_rows_);
169 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
171 Trace::paint_back(p, pp);
172 paint_axis(p, pp, get_visual_y());
175 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
177 using namespace pv::data::decode;
179 const int text_height = ViewItemPaintParams::text_height();
180 row_height_ = (text_height * 6) / 4;
181 const int annotation_height = (text_height * 5) / 4;
183 assert(decoder_stack_);
184 const QString err = decoder_stack_->error_message();
185 if (!err.isEmpty()) {
186 draw_unresolved_period(
187 p, annotation_height, pp.left(), pp.right());
188 draw_error(p, err, pp);
192 // Iterate through the rows
193 int y = get_visual_y();
194 pair<uint64_t, uint64_t> sample_range = get_sample_range(
195 pp.left(), pp.right());
197 assert(decoder_stack_);
198 const vector<Row> rows(decoder_stack_->get_visible_rows());
200 visible_rows_.clear();
201 for (size_t i = 0; i < rows.size(); i++) {
202 const Row &row = rows[i];
204 size_t base_colour = 0x13579BDF;
205 boost::hash_combine(base_colour, this);
206 boost::hash_combine(base_colour, row.decoder());
207 boost::hash_combine(base_colour, row.row());
210 vector<Annotation> annotations;
211 decoder_stack_->get_annotation_subset(annotations, row,
212 sample_range.first, sample_range.second);
213 if (!annotations.empty()) {
214 draw_annotations(annotations, p, annotation_height, pp, y,
219 visible_rows_.push_back(rows[i]);
224 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
226 // Update the maximum row count if needed
227 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
230 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
232 using namespace pv::data::decode;
236 for (size_t i = 0; i < visible_rows_.size(); i++) {
237 const int y = i * row_height_ + get_visual_y();
239 p.setPen(QPen(Qt::NoPen));
240 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
243 const QPointF points[] = {
244 QPointF(pp.left(), y - ArrowSize),
245 QPointF(pp.left() + ArrowSize, y),
246 QPointF(pp.left(), y + ArrowSize)
248 p.drawPolygon(points, countof(points));
251 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
252 pp.right() - pp.left(), row_height_);
253 const QString h(visible_rows_[i].title());
254 const int f = Qt::AlignLeft | Qt::AlignVCenter |
258 p.setPen(QApplication::palette().color(QPalette::Base));
259 for (int dx = -1; dx <= 1; dx++)
260 for (int dy = -1; dy <= 1; dy++)
261 if (dx != 0 && dy != 0)
262 p.drawText(r.translated(dx, dy), f, h);
265 p.setPen(QApplication::palette().color(QPalette::WindowText));
270 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
272 using pv::data::decode::Decoder;
276 assert(decoder_stack_);
278 // Add the standard options
279 Trace::populate_popup_form(parent, form);
281 // Add the decoder options
283 channel_selectors_.clear();
284 decoder_forms_.clear();
286 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
289 QLabel *const l = new QLabel(
290 tr("<p><i>No decoders in the stack</i></p>"));
291 l->setAlignment(Qt::AlignCenter);
294 auto iter = stack.cbegin();
295 for (int i = 0; i < (int)stack.size(); i++, iter++) {
296 shared_ptr<Decoder> dec(*iter);
297 create_decoder_form(i, dec, parent, form);
300 form->addRow(new QLabel(
301 tr("<i>* Required channels</i>"), parent));
304 // Add stacking button
305 pv::widgets::DecoderMenu *const decoder_menu =
306 new pv::widgets::DecoderMenu(parent);
307 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
308 this, SLOT(on_stack_decoder(srd_decoder*)));
310 QPushButton *const stack_button =
311 new QPushButton(tr("Stack Decoder"), parent);
312 stack_button->setMenu(decoder_menu);
314 QHBoxLayout *stack_button_box = new QHBoxLayout;
315 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
316 form->addRow(stack_button_box);
319 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
321 QMenu *const menu = Trace::create_context_menu(parent);
323 menu->addSeparator();
325 QAction *const del = new QAction(tr("Delete"), this);
326 del->setShortcuts(QKeySequence::Delete);
327 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
328 menu->addAction(del);
333 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
334 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
337 using namespace pv::data::decode;
339 vector<Annotation> a_block;
342 double samples_per_pixel, pixels_offset;
343 tie(pixels_offset, samples_per_pixel) =
344 get_pixels_offset_samples_per_pixel();
346 // Sort the annotations by start sample so that decoders
347 // can't confuse us by creating annotations out of order
348 stable_sort(annotations.begin(), annotations.end(),
349 [](const Annotation &a, const Annotation &b) {
350 return a.start_sample() < b.start_sample(); });
352 // Gather all annotations that form a visual "block" and draw them as such
353 for (const Annotation &a : annotations) {
355 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
356 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
357 const int a_width = a_end - a_start;
359 const int delta = a_end - p_end;
361 bool a_is_separate = false;
363 // Annotation wider than the threshold for a useful label width?
365 for (const QString &ann_text : a.annotations()) {
366 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
367 // Annotation wide enough to fit a label? Don't put it in a block then
369 a_is_separate = true;
375 // Were the previous and this annotation more than a pixel apart?
376 if ((abs(delta) > 1) || a_is_separate) {
377 // Block was broken, draw annotations that form the current block
378 if (a_block.size() == 1) {
379 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
382 draw_annotation_block(a_block, p, h, y, base_colour);
388 draw_annotation(a, p, h, pp, y, base_colour);
389 // Next annotation must start a new block. delta will be > 1
390 // because we set p_end to INT_MIN but that's okay since
391 // a_block will be empty, so nothing will be drawn
394 a_block.push_back(a);
399 if (a_block.size() == 1)
400 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
402 draw_annotation_block(a_block, p, h, y, base_colour);
405 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
406 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
407 size_t base_colour) const
409 double samples_per_pixel, pixels_offset;
410 tie(pixels_offset, samples_per_pixel) =
411 get_pixels_offset_samples_per_pixel();
413 const double start = a.start_sample() / samples_per_pixel -
415 const double end = a.end_sample() / samples_per_pixel -
418 const size_t colour = (base_colour + a.format()) % countof(Colours);
419 const QColor &fill = Colours[colour];
420 const QColor &outline = OutlineColours[colour];
422 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
425 if (a.start_sample() == a.end_sample())
426 draw_instant(a, p, fill, outline, h, start, y);
428 draw_range(a, p, fill, outline, h, start, end, y);
431 void DecodeTrace::draw_annotation_block(
432 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
433 int y, size_t base_colour) const
435 using namespace pv::data::decode;
437 if (annotations.empty())
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 = annotations.front().start_sample() /
445 samples_per_pixel - pixels_offset;
446 const double end = annotations.back().end_sample() /
447 samples_per_pixel - pixels_offset;
449 const double top = y + .5 - h / 2;
450 const double bottom = y + .5 + h / 2;
452 const size_t colour = (base_colour + annotations.front().format()) %
455 // Check if all annotations are of the same type (i.e. we can use one color)
456 // or if we should use a neutral color (i.e. gray)
457 const int format = annotations.front().format();
458 const bool single_format = std::all_of(
459 annotations.begin(), annotations.end(),
460 [&](const Annotation &a) { return a.format() == format; });
462 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
463 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
466 QRectF(start, top, end - start, bottom - top), h/4, h/4);
469 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
470 QColor fill, QColor outline, int h, double x, int y) const
472 const QString text = a.annotations().empty() ?
473 QString() : a.annotations().back();
474 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
476 const QRectF rect(x - w / 2, y - h / 2, w, h);
480 p.drawRoundedRect(rect, h / 2, h / 2);
483 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
486 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
487 QColor fill, QColor outline, int h, double start,
488 double end, int y) const
490 const double top = y + .5 - h / 2;
491 const double bottom = y + .5 + h / 2;
492 const vector<QString> annotations = a.annotations();
497 // If the two ends are within 1 pixel, draw a vertical line
498 if (start + 1.0 > end) {
499 p.drawLine(QPointF(start, top), QPointF(start, bottom));
503 const double cap_width = min((end - start) / 4, EndCapWidth);
506 QPointF(start, y + .5f),
507 QPointF(start + cap_width, top),
508 QPointF(end - cap_width, top),
509 QPointF(end, y + .5f),
510 QPointF(end - cap_width, bottom),
511 QPointF(start + cap_width, bottom)
514 p.drawConvexPolygon(pts, countof(pts));
516 if (annotations.empty())
519 QRectF rect(start + cap_width, y - h / 2,
520 end - start - cap_width * 2, h);
521 if (rect.width() <= 4)
526 // Try to find an annotation that will fit
527 QString best_annotation;
530 for (const QString &a : annotations) {
531 const int w = p.boundingRect(QRectF(), 0, a).width();
532 if (w <= rect.width() && w > best_width)
533 best_annotation = a, best_width = w;
536 if (best_annotation.isEmpty())
537 best_annotation = annotations.back();
539 // If not ellide the last in the list
540 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
541 best_annotation, Qt::ElideRight, rect.width()));
544 void DecodeTrace::draw_error(QPainter &p, const QString &message,
545 const ViewItemPaintParams &pp)
547 const int y = get_visual_y();
549 p.setPen(ErrorBgColour.darker());
550 p.setBrush(ErrorBgColour);
552 const QRectF bounding_rect =
553 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
554 const QRectF text_rect = p.boundingRect(bounding_rect,
555 Qt::AlignCenter, message);
556 const float r = text_rect.height() / 4;
558 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
562 p.drawText(text_rect, message);
565 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
568 using namespace pv::data;
569 using pv::data::decode::Decoder;
571 double samples_per_pixel, pixels_offset;
573 assert(decoder_stack_);
575 shared_ptr<Logic> data;
576 shared_ptr<LogicSignal> logic_signal;
578 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
580 // We get the logic data of the first channel in the list.
581 // This works because we are currently assuming all
582 // LogicSignals have the same data/segment
583 for (const shared_ptr<Decoder> &dec : stack)
584 if (dec && !dec->channels().empty() &&
585 ((logic_signal = (*dec->channels().begin()).second)) &&
586 ((data = logic_signal->logic_data())))
589 if (!data || data->logic_segments().empty())
592 const shared_ptr<LogicSegment> segment =
593 data->logic_segments().front();
595 const int64_t sample_count = (int64_t)segment->get_sample_count();
596 if (sample_count == 0)
599 const int64_t samples_decoded = decoder_stack_->samples_decoded();
600 if (sample_count == samples_decoded)
603 const int y = get_visual_y();
605 tie(pixels_offset, samples_per_pixel) =
606 get_pixels_offset_samples_per_pixel();
608 const double start = max(samples_decoded /
609 samples_per_pixel - pixels_offset, left - 1.0);
610 const double end = min(sample_count / samples_per_pixel -
611 pixels_offset, right + 1.0);
612 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
614 p.setPen(QPen(Qt::NoPen));
615 p.setBrush(Qt::white);
616 p.drawRect(no_decode_rect);
618 p.setPen(NoDecodeColour);
619 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
620 p.drawRect(no_decode_rect);
623 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
626 assert(decoder_stack_);
628 const View *view = owner_->view();
631 const double scale = view->scale();
634 const double pixels_offset =
635 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
637 double samplerate = decoder_stack_->samplerate();
639 // Show sample rate as 1Hz when it is unknown
640 if (samplerate == 0.0)
643 return make_pair(pixels_offset, samplerate * scale);
646 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
647 int x_start, int x_end) const
649 double samples_per_pixel, pixels_offset;
650 tie(pixels_offset, samples_per_pixel) =
651 get_pixels_offset_samples_per_pixel();
653 const uint64_t start = (uint64_t)max(
654 (x_start + pixels_offset) * samples_per_pixel, 0.0);
655 const uint64_t end = (uint64_t)max(
656 (x_end + pixels_offset) * samples_per_pixel, 0.0);
658 return make_pair(start, end);
661 int DecodeTrace::get_row_at_point(const QPoint &point)
666 const int y = (point.y() - get_visual_y() + row_height_ / 2);
668 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
672 const int row = y / row_height_;
674 if (row >= (int)visible_rows_.size())
680 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
682 using namespace pv::data::decode;
687 const pair<uint64_t, uint64_t> sample_range =
688 get_sample_range(point.x(), point.x() + 1);
689 const int row = get_row_at_point(point);
693 vector<pv::data::decode::Annotation> annotations;
695 assert(decoder_stack_);
696 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
697 sample_range.first, sample_range.second);
699 return (annotations.empty()) ?
700 QString() : annotations[0].annotations().front();
703 void DecodeTrace::hover_point_changed()
707 const View *const view = owner_->view();
710 QPoint hp = view->hover_point();
711 QString ann = get_annotation_at_point(hp);
715 if (!row_height_ || ann.isEmpty()) {
716 QToolTip::hideText();
720 const int hover_row = get_row_at_point(hp);
722 QFontMetrics m(QToolTip::font());
723 const QRect text_size = m.boundingRect(QRect(), 0, ann);
725 // This is OS-specific and unfortunately we can't query it, so
726 // use an approximation to at least try to minimize the error.
727 const int padding = 8;
729 // Make sure the tool tip doesn't overlap with the mouse cursor.
730 // If it did, the tool tip would constantly hide and re-appear.
731 // We also push it up by one row so that it appears above the
732 // decode trace, not below.
733 hp.setX(hp.x() - (text_size.width() / 2) - padding);
735 hp.setY(get_visual_y() - (row_height_ / 2) +
736 (hover_row * row_height_) -
737 row_height_ - text_size.height() - padding);
739 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
742 void DecodeTrace::create_decoder_form(int index,
743 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
749 const srd_decoder *const decoder = dec->decoder();
752 const bool decoder_deletable = index > 0;
754 pv::widgets::DecoderGroupBox *const group =
755 new pv::widgets::DecoderGroupBox(
756 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
757 group->set_decoder_visible(dec->shown());
759 if (decoder_deletable) {
760 delete_mapper_.setMapping(group, index);
761 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
764 show_hide_mapper_.setMapping(group, index);
765 connect(group, SIGNAL(show_hide_decoder()),
766 &show_hide_mapper_, SLOT(map()));
768 QFormLayout *const decoder_form = new QFormLayout;
769 group->add_layout(decoder_form);
771 // Add the mandatory channels
772 for (l = decoder->channels; l; l = l->next) {
773 const struct srd_channel *const pdch =
774 (struct srd_channel *)l->data;
775 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
776 connect(combo, SIGNAL(currentIndexChanged(int)),
777 this, SLOT(on_channel_selected(int)));
778 decoder_form->addRow(tr("<b>%1</b> (%2) *")
779 .arg(QString::fromUtf8(pdch->name))
780 .arg(QString::fromUtf8(pdch->desc)), combo);
782 const ChannelSelector s = {combo, dec, pdch};
783 channel_selectors_.push_back(s);
786 // Add the optional channels
787 for (l = decoder->opt_channels; l; l = l->next) {
788 const struct srd_channel *const pdch =
789 (struct srd_channel *)l->data;
790 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
791 connect(combo, SIGNAL(currentIndexChanged(int)),
792 this, SLOT(on_channel_selected(int)));
793 decoder_form->addRow(tr("<b>%1</b> (%2)")
794 .arg(QString::fromUtf8(pdch->name))
795 .arg(QString::fromUtf8(pdch->desc)), combo);
797 const ChannelSelector s = {combo, dec, pdch};
798 channel_selectors_.push_back(s);
802 shared_ptr<binding::Decoder> binding(
803 new binding::Decoder(decoder_stack_, dec));
804 binding->add_properties_to_form(decoder_form, true);
806 bindings_.push_back(binding);
809 decoder_forms_.push_back(group);
812 QComboBox* DecodeTrace::create_channel_selector(
813 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
814 const srd_channel *const pdch)
818 const auto sigs(session_.signals());
820 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
821 std::sort(sig_list.begin(), sig_list.end(),
822 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
823 return a->name().compare(b->name()) < 0; });
825 assert(decoder_stack_);
826 const auto channel_iter = dec->channels().find(pdch);
828 QComboBox *selector = new QComboBox(parent);
830 selector->addItem("-", qVariantFromValue((void*)nullptr));
832 if (channel_iter == dec->channels().end())
833 selector->setCurrentIndex(0);
835 for (const shared_ptr<view::Signal> &s : sig_list) {
837 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
838 selector->addItem(s->name(),
839 qVariantFromValue((void*)s.get()));
841 if (channel_iter != dec->channels().end() &&
842 (*channel_iter).second == s)
843 selector->setCurrentIndex(
844 selector->count() - 1);
851 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
855 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
857 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
859 for (const ChannelSelector &s : channel_selectors_) {
860 if (s.decoder_ != dec)
863 const LogicSignal *const selection =
864 (LogicSignal*)s.combo_->itemData(
865 s.combo_->currentIndex()).value<void*>();
867 for (shared_ptr<Signal> sig : sigs)
868 if (sig.get() == selection) {
869 channel_map[s.pdch_] =
870 dynamic_pointer_cast<LogicSignal>(sig);
875 dec->set_channels(channel_map);
878 void DecodeTrace::commit_channels()
880 assert(decoder_stack_);
881 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
882 commit_decoder_channels(dec);
884 decoder_stack_->begin_decode();
887 void DecodeTrace::on_new_decode_data()
890 owner_->row_item_appearance_changed(false, true);
893 void DecodeTrace::delete_pressed()
898 void DecodeTrace::on_delete()
900 session_.remove_decode_signal(this);
903 void DecodeTrace::on_channel_selected(int)
908 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
911 assert(decoder_stack_);
912 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
913 new data::decode::Decoder(decoder)));
914 decoder_stack_->begin_decode();
919 void DecodeTrace::on_delete_decoder(int index)
921 decoder_stack_->remove(index);
926 decoder_stack_->begin_decode();
929 void DecodeTrace::on_show_hide_decoder(int index)
931 using pv::data::decode::Decoder;
933 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
935 // Find the decoder in the stack
936 auto iter = stack.cbegin();
937 for (int i = 0; i < index; i++, iter++)
938 assert(iter != stack.end());
940 shared_ptr<Decoder> dec = *iter;
943 const bool show = !dec->shown();
946 assert(index < (int)decoder_forms_.size());
947 decoder_forms_[index]->set_decoder_visible(show);
950 owner_->row_item_appearance_changed(false, true);