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::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 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
132 Trace(QString::fromUtf8(
133 decoder_stack->stack().front()->decoder()->name)),
135 decoder_stack_(decoder_stack),
137 max_visible_rows_(0),
138 delete_mapper_(this),
139 show_hide_mapper_(this)
141 assert(decoder_stack_);
143 set_colour(DecodeColours[index % countof(DecodeColours)]);
145 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
146 this, SLOT(on_new_decode_data()));
147 connect(&delete_mapper_, SIGNAL(mapped(int)),
148 this, SLOT(on_delete_decoder(int)));
149 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
150 this, SLOT(on_show_hide_decoder(int)));
153 bool DecodeTrace::enabled() const
158 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
160 return decoder_stack_;
163 pair<int, int> DecodeTrace::v_extents() const
165 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
167 return make_pair(-row_height, row_height * max_visible_rows_);
170 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
172 Trace::paint_back(p, pp);
173 paint_axis(p, pp, get_visual_y());
176 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
178 using namespace pv::data::decode;
180 const int text_height = ViewItemPaintParams::text_height();
181 row_height_ = (text_height * 6) / 4;
182 const int annotation_height = (text_height * 5) / 4;
184 assert(decoder_stack_);
185 const QString err = decoder_stack_->error_message();
186 if (!err.isEmpty()) {
187 draw_unresolved_period(
188 p, annotation_height, pp.left(), pp.right());
189 draw_error(p, err, pp);
193 // Iterate through the rows
194 int y = get_visual_y();
195 pair<uint64_t, uint64_t> sample_range = get_sample_range(
196 pp.left(), pp.right());
198 assert(decoder_stack_);
199 const vector<Row> rows(decoder_stack_->get_visible_rows());
201 visible_rows_.clear();
202 for (const Row& row : rows) {
203 // Cache the row title widths
206 row_title_width = row_title_widths_.at(row);
207 } catch (std::out_of_range) {
208 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
210 row_title_widths_[row] = w;
214 // Determine the row's color
215 size_t base_colour = 0x13579BDF;
216 boost::hash_combine(base_colour, this);
217 boost::hash_combine(base_colour, row.decoder());
218 boost::hash_combine(base_colour, row.row());
221 vector<Annotation> annotations;
222 decoder_stack_->get_annotation_subset(annotations, row,
223 sample_range.first, sample_range.second);
224 if (!annotations.empty()) {
225 draw_annotations(annotations, p, annotation_height, pp, y,
226 base_colour, row_title_width);
230 visible_rows_.push_back(row);
235 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
237 // Update the maximum row count if needed
238 max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
241 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
243 using namespace pv::data::decode;
247 for (size_t i = 0; i < visible_rows_.size(); i++) {
248 const int y = i * row_height_ + get_visual_y();
250 p.setPen(QPen(Qt::NoPen));
251 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
254 const QPointF points[] = {
255 QPointF(pp.left(), y - ArrowSize),
256 QPointF(pp.left() + ArrowSize, y),
257 QPointF(pp.left(), y + ArrowSize)
259 p.drawPolygon(points, countof(points));
262 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
263 pp.right() - pp.left(), row_height_);
264 const QString h(visible_rows_[i].title());
265 const int f = Qt::AlignLeft | Qt::AlignVCenter |
269 p.setPen(QApplication::palette().color(QPalette::Base));
270 for (int dx = -1; dx <= 1; dx++)
271 for (int dy = -1; dy <= 1; dy++)
272 if (dx != 0 && dy != 0)
273 p.drawText(r.translated(dx, dy), f, h);
276 p.setPen(QApplication::palette().color(QPalette::WindowText));
281 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
283 using pv::data::decode::Decoder;
287 assert(decoder_stack_);
289 // Add the standard options
290 Trace::populate_popup_form(parent, form);
292 // Add the decoder options
294 channel_selectors_.clear();
295 decoder_forms_.clear();
297 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
300 QLabel *const l = new QLabel(
301 tr("<p><i>No decoders in the stack</i></p>"));
302 l->setAlignment(Qt::AlignCenter);
305 auto iter = stack.cbegin();
306 for (int i = 0; i < (int)stack.size(); i++, iter++) {
307 shared_ptr<Decoder> dec(*iter);
308 create_decoder_form(i, dec, parent, form);
311 form->addRow(new QLabel(
312 tr("<i>* Required channels</i>"), parent));
315 // Add stacking button
316 pv::widgets::DecoderMenu *const decoder_menu =
317 new pv::widgets::DecoderMenu(parent);
318 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
319 this, SLOT(on_stack_decoder(srd_decoder*)));
321 QPushButton *const stack_button =
322 new QPushButton(tr("Stack Decoder"), parent);
323 stack_button->setMenu(decoder_menu);
325 QHBoxLayout *stack_button_box = new QHBoxLayout;
326 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
327 form->addRow(stack_button_box);
330 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
332 QMenu *const menu = Trace::create_context_menu(parent);
334 menu->addSeparator();
336 QAction *const del = new QAction(tr("Delete"), this);
337 del->setShortcuts(QKeySequence::Delete);
338 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
339 menu->addAction(del);
344 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
345 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
346 size_t base_colour, int row_title_width)
348 using namespace pv::data::decode;
350 vector<Annotation> a_block;
353 double samples_per_pixel, pixels_offset;
354 tie(pixels_offset, samples_per_pixel) =
355 get_pixels_offset_samples_per_pixel();
357 // Sort the annotations by start sample so that decoders
358 // can't confuse us by creating annotations out of order
359 stable_sort(annotations.begin(), annotations.end(),
360 [](const Annotation &a, const Annotation &b) {
361 return a.start_sample() < b.start_sample(); });
363 // Gather all annotations that form a visual "block" and draw them as such
364 for (const Annotation &a : annotations) {
366 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
367 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
368 const int a_width = a_end - a_start;
370 const int delta = a_end - p_end;
372 bool a_is_separate = false;
374 // Annotation wider than the threshold for a useful label width?
376 for (const QString &ann_text : a.annotations()) {
377 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
378 // Annotation wide enough to fit a label? Don't put it in a block then
380 a_is_separate = true;
386 // Were the previous and this annotation more than a pixel apart?
387 if ((abs(delta) > 1) || a_is_separate) {
388 // Block was broken, draw annotations that form the current block
389 if (a_block.size() == 1) {
390 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
394 draw_annotation_block(a_block, p, h, y, base_colour);
400 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
401 // Next annotation must start a new block. delta will be > 1
402 // because we set p_end to INT_MIN but that's okay since
403 // a_block will be empty, so nothing will be drawn
406 a_block.push_back(a);
411 if (a_block.size() == 1)
412 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
415 draw_annotation_block(a_block, p, h, y, base_colour);
418 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
419 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
420 size_t base_colour, int row_title_width) const
422 double samples_per_pixel, pixels_offset;
423 tie(pixels_offset, samples_per_pixel) =
424 get_pixels_offset_samples_per_pixel();
426 const double start = a.start_sample() / samples_per_pixel -
428 const double end = a.end_sample() / samples_per_pixel -
431 const size_t colour = (base_colour + a.format()) % countof(Colours);
432 p.setPen(OutlineColours[colour]);
433 p.setBrush(Colours[colour]);
435 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
438 if (a.start_sample() == a.end_sample())
439 draw_instant(a, p, h, start, y);
441 draw_range(a, p, h, start, end, y, pp,
445 void DecodeTrace::draw_annotation_block(
446 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
447 int y, size_t base_colour) const
449 using namespace pv::data::decode;
451 if (annotations.empty())
454 double samples_per_pixel, pixels_offset;
455 tie(pixels_offset, samples_per_pixel) =
456 get_pixels_offset_samples_per_pixel();
458 const double start = annotations.front().start_sample() /
459 samples_per_pixel - pixels_offset;
460 const double end = annotations.back().end_sample() /
461 samples_per_pixel - pixels_offset;
463 const double top = y + .5 - h / 2;
464 const double bottom = y + .5 + h / 2;
466 const size_t colour = (base_colour + annotations.front().format()) %
469 // Check if all annotations are of the same type (i.e. we can use one color)
470 // or if we should use a neutral color (i.e. gray)
471 const int format = annotations.front().format();
472 const bool single_format = std::all_of(
473 annotations.begin(), annotations.end(),
474 [&](const Annotation &a) { return a.format() == format; });
476 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
477 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
480 QRectF(start, top, end - start, bottom - top), h/4, h/4);
483 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
484 int h, double x, int y) const
486 const QString text = a.annotations().empty() ?
487 QString() : a.annotations().back();
488 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
490 const QRectF rect(x - w / 2, y - h / 2, w, h);
492 p.drawRoundedRect(rect, h / 2, h / 2);
495 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
498 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
499 int h, double start, double end, int y, const ViewItemPaintParams &pp,
500 int row_title_width) const
502 const double top = y + .5 - h / 2;
503 const double bottom = y + .5 + h / 2;
504 const vector<QString> annotations = a.annotations();
506 // If the two ends are within 1 pixel, draw a vertical line
507 if (start + 1.0 > end) {
508 p.drawLine(QPointF(start, top), QPointF(start, bottom));
512 const double cap_width = min((end - start) / 4, EndCapWidth);
515 QPointF(start, y + .5f),
516 QPointF(start + cap_width, top),
517 QPointF(end - cap_width, top),
518 QPointF(end, y + .5f),
519 QPointF(end - cap_width, bottom),
520 QPointF(start + cap_width, bottom)
523 p.drawConvexPolygon(pts, countof(pts));
525 if (annotations.empty())
528 const int ann_start = start + cap_width;
529 const int ann_end = end - cap_width;
531 const int real_start = std::max(ann_start, pp.left() + row_title_width);
532 const int real_end = std::min(ann_end, pp.right());
533 const int real_width = real_end - real_start;
535 QRectF rect(real_start, y - h / 2, real_width, h);
536 if (rect.width() <= 4)
541 // Try to find an annotation that will fit
542 QString best_annotation;
545 for (const QString &a : annotations) {
546 const int w = p.boundingRect(QRectF(), 0, a).width();
547 if (w <= rect.width() && w > best_width)
548 best_annotation = a, best_width = w;
551 if (best_annotation.isEmpty())
552 best_annotation = annotations.back();
554 // If not ellide the last in the list
555 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
556 best_annotation, Qt::ElideRight, rect.width()));
559 void DecodeTrace::draw_error(QPainter &p, const QString &message,
560 const ViewItemPaintParams &pp)
562 const int y = get_visual_y();
564 p.setPen(ErrorBgColour.darker());
565 p.setBrush(ErrorBgColour);
567 const QRectF bounding_rect =
568 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
569 const QRectF text_rect = p.boundingRect(bounding_rect,
570 Qt::AlignCenter, message);
571 const float r = text_rect.height() / 4;
573 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
577 p.drawText(text_rect, message);
580 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
583 using namespace pv::data;
584 using pv::data::decode::Decoder;
586 double samples_per_pixel, pixels_offset;
588 assert(decoder_stack_);
590 shared_ptr<Logic> data;
591 shared_ptr<LogicSignal> logic_signal;
593 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
595 // We get the logic data of the first channel in the list.
596 // This works because we are currently assuming all
597 // LogicSignals have the same data/segment
598 for (const shared_ptr<Decoder> &dec : stack)
599 if (dec && !dec->channels().empty() &&
600 ((logic_signal = (*dec->channels().begin()).second)) &&
601 ((data = logic_signal->logic_data())))
604 if (!data || data->logic_segments().empty())
607 const shared_ptr<LogicSegment> segment =
608 data->logic_segments().front();
610 const int64_t sample_count = (int64_t)segment->get_sample_count();
611 if (sample_count == 0)
614 const int64_t samples_decoded = decoder_stack_->samples_decoded();
615 if (sample_count == samples_decoded)
618 const int y = get_visual_y();
620 tie(pixels_offset, samples_per_pixel) =
621 get_pixels_offset_samples_per_pixel();
623 const double start = max(samples_decoded /
624 samples_per_pixel - pixels_offset, left - 1.0);
625 const double end = min(sample_count / samples_per_pixel -
626 pixels_offset, right + 1.0);
627 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
629 p.setPen(QPen(Qt::NoPen));
630 p.setBrush(Qt::white);
631 p.drawRect(no_decode_rect);
633 p.setPen(NoDecodeColour);
634 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
635 p.drawRect(no_decode_rect);
638 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
641 assert(decoder_stack_);
643 const View *view = owner_->view();
646 const double scale = view->scale();
649 const double pixels_offset =
650 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
652 double samplerate = decoder_stack_->samplerate();
654 // Show sample rate as 1Hz when it is unknown
655 if (samplerate == 0.0)
658 return make_pair(pixels_offset, samplerate * scale);
661 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
662 int x_start, int x_end) const
664 double samples_per_pixel, pixels_offset;
665 tie(pixels_offset, samples_per_pixel) =
666 get_pixels_offset_samples_per_pixel();
668 const uint64_t start = (uint64_t)max(
669 (x_start + pixels_offset) * samples_per_pixel, 0.0);
670 const uint64_t end = (uint64_t)max(
671 (x_end + pixels_offset) * samples_per_pixel, 0.0);
673 return make_pair(start, end);
676 int DecodeTrace::get_row_at_point(const QPoint &point)
681 const int y = (point.y() - get_visual_y() + row_height_ / 2);
683 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
687 const int row = y / row_height_;
689 if (row >= (int)visible_rows_.size())
695 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
697 using namespace pv::data::decode;
702 const pair<uint64_t, uint64_t> sample_range =
703 get_sample_range(point.x(), point.x() + 1);
704 const int row = get_row_at_point(point);
708 vector<pv::data::decode::Annotation> annotations;
710 assert(decoder_stack_);
711 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
712 sample_range.first, sample_range.second);
714 return (annotations.empty()) ?
715 QString() : annotations[0].annotations().front();
718 void DecodeTrace::hover_point_changed()
722 const View *const view = owner_->view();
725 QPoint hp = view->hover_point();
726 QString ann = get_annotation_at_point(hp);
730 if (!row_height_ || ann.isEmpty()) {
731 QToolTip::hideText();
735 const int hover_row = get_row_at_point(hp);
737 QFontMetrics m(QToolTip::font());
738 const QRect text_size = m.boundingRect(QRect(), 0, ann);
740 // This is OS-specific and unfortunately we can't query it, so
741 // use an approximation to at least try to minimize the error.
742 const int padding = 8;
744 // Make sure the tool tip doesn't overlap with the mouse cursor.
745 // If it did, the tool tip would constantly hide and re-appear.
746 // We also push it up by one row so that it appears above the
747 // decode trace, not below.
748 hp.setX(hp.x() - (text_size.width() / 2) - padding);
750 hp.setY(get_visual_y() - (row_height_ / 2) +
751 (hover_row * row_height_) -
752 row_height_ - text_size.height() - padding);
754 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
757 void DecodeTrace::create_decoder_form(int index,
758 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
764 const srd_decoder *const decoder = dec->decoder();
767 const bool decoder_deletable = index > 0;
769 pv::widgets::DecoderGroupBox *const group =
770 new pv::widgets::DecoderGroupBox(
771 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
772 group->set_decoder_visible(dec->shown());
774 if (decoder_deletable) {
775 delete_mapper_.setMapping(group, index);
776 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
779 show_hide_mapper_.setMapping(group, index);
780 connect(group, SIGNAL(show_hide_decoder()),
781 &show_hide_mapper_, SLOT(map()));
783 QFormLayout *const decoder_form = new QFormLayout;
784 group->add_layout(decoder_form);
786 // Add the mandatory channels
787 for (l = decoder->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 QString::fromUtf8(pdch->desc)), combo);
797 const ChannelSelector s = {combo, dec, pdch};
798 channel_selectors_.push_back(s);
801 // Add the optional channels
802 for (l = decoder->opt_channels; l; l = l->next) {
803 const struct srd_channel *const pdch =
804 (struct srd_channel *)l->data;
805 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
806 connect(combo, SIGNAL(currentIndexChanged(int)),
807 this, SLOT(on_channel_selected(int)));
808 decoder_form->addRow(tr("<b>%1</b> (%2)")
809 .arg(QString::fromUtf8(pdch->name),
810 QString::fromUtf8(pdch->desc)), combo);
812 const ChannelSelector s = {combo, dec, pdch};
813 channel_selectors_.push_back(s);
817 shared_ptr<binding::Decoder> binding(
818 new binding::Decoder(decoder_stack_, dec));
819 binding->add_properties_to_form(decoder_form, true);
821 bindings_.push_back(binding);
824 decoder_forms_.push_back(group);
827 QComboBox* DecodeTrace::create_channel_selector(
828 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
829 const srd_channel *const pdch)
833 const auto sigs(session_.signals());
835 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
836 std::sort(sig_list.begin(), sig_list.end(),
837 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
838 return a->name().compare(b->name()) < 0; });
840 assert(decoder_stack_);
841 const auto channel_iter = dec->channels().find(pdch);
843 QComboBox *selector = new QComboBox(parent);
845 selector->addItem("-", qVariantFromValue((void*)nullptr));
847 if (channel_iter == dec->channels().end())
848 selector->setCurrentIndex(0);
850 for (const shared_ptr<view::Signal> &s : sig_list) {
852 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
853 selector->addItem(s->name(),
854 qVariantFromValue((void*)s.get()));
856 if (channel_iter != dec->channels().end() &&
857 (*channel_iter).second == s)
858 selector->setCurrentIndex(
859 selector->count() - 1);
866 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
870 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
872 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
874 for (const ChannelSelector &s : channel_selectors_) {
875 if (s.decoder_ != dec)
878 const LogicSignal *const selection =
879 (LogicSignal*)s.combo_->itemData(
880 s.combo_->currentIndex()).value<void*>();
882 for (shared_ptr<Signal> sig : sigs)
883 if (sig.get() == selection) {
884 channel_map[s.pdch_] =
885 dynamic_pointer_cast<LogicSignal>(sig);
890 dec->set_channels(channel_map);
893 void DecodeTrace::commit_channels()
895 assert(decoder_stack_);
896 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
897 commit_decoder_channels(dec);
899 decoder_stack_->begin_decode();
902 void DecodeTrace::on_new_decode_data()
905 owner_->row_item_appearance_changed(false, true);
908 void DecodeTrace::delete_pressed()
913 void DecodeTrace::on_delete()
915 session_.remove_decode_signal(this);
918 void DecodeTrace::on_channel_selected(int)
923 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
926 assert(decoder_stack_);
927 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
928 new data::decode::Decoder(decoder)));
929 decoder_stack_->begin_decode();
934 void DecodeTrace::on_delete_decoder(int index)
936 decoder_stack_->remove(index);
941 decoder_stack_->begin_decode();
944 void DecodeTrace::on_show_hide_decoder(int index)
946 using pv::data::decode::Decoder;
948 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
950 // Find the decoder in the stack
951 auto iter = stack.cbegin();
952 for (int i = 0; i < index; i++, iter++)
953 assert(iter != stack.end());
955 shared_ptr<Decoder> dec = *iter;
958 const bool show = !dec->shown();
961 assert(index < (int)decoder_forms_.size());
962 decoder_forms_[index]->set_decoder_visible(show);
965 owner_->row_item_appearance_changed(false, true);