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;
340 int prev_ann_pos = INT_MIN;
342 double samples_per_pixel, pixels_offset;
343 tie(pixels_offset, samples_per_pixel) =
344 get_pixels_offset_samples_per_pixel();
346 // Gather all annotations that form a visual "block" and draw them as such
347 for (const Annotation &a : annotations) {
349 const int end = a.end_sample() / samples_per_pixel - pixels_offset;
350 const int delta = end - prev_ann_pos;
352 // Some annotations are in reverse order, so we cannot
353 // simply check for delta > 1
354 if (abs(delta) > 1) {
355 // Block was broken, draw it
356 if (a_block.size() == 1)
357 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
359 if (a_block.size() > 0)
360 draw_annotation_block(a_block, p, h, y, base_colour);
365 a_block.push_back(a);
369 if (a_block.size() == 1)
370 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
372 draw_annotation_block(a_block, p, h, y, base_colour);
375 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
376 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
377 size_t base_colour) const
379 double samples_per_pixel, pixels_offset;
380 tie(pixels_offset, samples_per_pixel) =
381 get_pixels_offset_samples_per_pixel();
383 const double start = a.start_sample() / samples_per_pixel -
385 const double end = a.end_sample() / samples_per_pixel -
388 const size_t colour = (base_colour + a.format()) % countof(Colours);
389 const QColor &fill = Colours[colour];
390 const QColor &outline = OutlineColours[colour];
392 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
395 if (a.start_sample() == a.end_sample())
396 draw_instant(a, p, fill, outline, h, start, y);
398 draw_range(a, p, fill, outline, h, start, end, y);
401 void DecodeTrace::draw_annotation_block(
402 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
403 int y, size_t base_colour) const
405 using namespace pv::data::decode;
407 double samples_per_pixel, pixels_offset;
408 tie(pixels_offset, samples_per_pixel) =
409 get_pixels_offset_samples_per_pixel();
411 const double start = annotations.front().start_sample() /
412 samples_per_pixel - pixels_offset;
413 const double end = annotations.back().end_sample() /
414 samples_per_pixel - pixels_offset;
416 const double top = y + .5 - h / 2;
417 const double bottom = y + .5 + h / 2;
419 const size_t colour = (base_colour + annotations.front().format()) %
422 // Check if all annotations are of the same type (i.e. we can use one color)
423 // or if we should use a neutral color (i.e. gray)
424 const int format = annotations.front().format();
425 const bool single_format = std::all_of(
426 annotations.begin(), annotations.end(),
427 [&](const Annotation &a) { return a.format() == format; });
429 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
430 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
433 QRectF(start, top, end - start, bottom - top), h/4, h/4);
436 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
437 QColor fill, QColor outline, int h, double x, int y) const
439 const QString text = a.annotations().empty() ?
440 QString() : a.annotations().back();
441 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
443 const QRectF rect(x - w / 2, y - h / 2, w, h);
447 p.drawRoundedRect(rect, h / 2, h / 2);
450 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
453 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
454 QColor fill, QColor outline, int h, double start,
455 double end, int y) const
457 const double top = y + .5 - h / 2;
458 const double bottom = y + .5 + h / 2;
459 const vector<QString> annotations = a.annotations();
464 // If the two ends are within 1 pixel, draw a vertical line
465 if (start + 1.0 > end) {
466 p.drawLine(QPointF(start, top), QPointF(start, bottom));
470 const double cap_width = min((end - start) / 4, EndCapWidth);
473 QPointF(start, y + .5f),
474 QPointF(start + cap_width, top),
475 QPointF(end - cap_width, top),
476 QPointF(end, y + .5f),
477 QPointF(end - cap_width, bottom),
478 QPointF(start + cap_width, bottom)
481 p.drawConvexPolygon(pts, countof(pts));
483 if (annotations.empty())
486 QRectF rect(start + cap_width, y - h / 2,
487 end - start - cap_width * 2, h);
488 if (rect.width() <= 4)
493 // Try to find an annotation that will fit
494 QString best_annotation;
497 for (const QString &a : annotations) {
498 const int w = p.boundingRect(QRectF(), 0, a).width();
499 if (w <= rect.width() && w > best_width)
500 best_annotation = a, best_width = w;
503 if (best_annotation.isEmpty())
504 best_annotation = annotations.back();
506 // If not ellide the last in the list
507 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
508 best_annotation, Qt::ElideRight, rect.width()));
511 void DecodeTrace::draw_error(QPainter &p, const QString &message,
512 const ViewItemPaintParams &pp)
514 const int y = get_visual_y();
516 p.setPen(ErrorBgColour.darker());
517 p.setBrush(ErrorBgColour);
519 const QRectF bounding_rect =
520 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
521 const QRectF text_rect = p.boundingRect(bounding_rect,
522 Qt::AlignCenter, message);
523 const float r = text_rect.height() / 4;
525 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
529 p.drawText(text_rect, message);
532 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
535 using namespace pv::data;
536 using pv::data::decode::Decoder;
538 double samples_per_pixel, pixels_offset;
540 assert(decoder_stack_);
542 shared_ptr<Logic> data;
543 shared_ptr<LogicSignal> logic_signal;
545 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
547 // We get the logic data of the first channel in the list.
548 // This works because we are currently assuming all
549 // LogicSignals have the same data/segment
550 for (const shared_ptr<Decoder> &dec : stack)
551 if (dec && !dec->channels().empty() &&
552 ((logic_signal = (*dec->channels().begin()).second)) &&
553 ((data = logic_signal->logic_data())))
556 if (!data || data->logic_segments().empty())
559 const shared_ptr<LogicSegment> segment =
560 data->logic_segments().front();
562 const int64_t sample_count = (int64_t)segment->get_sample_count();
563 if (sample_count == 0)
566 const int64_t samples_decoded = decoder_stack_->samples_decoded();
567 if (sample_count == samples_decoded)
570 const int y = get_visual_y();
572 tie(pixels_offset, samples_per_pixel) =
573 get_pixels_offset_samples_per_pixel();
575 const double start = max(samples_decoded /
576 samples_per_pixel - pixels_offset, left - 1.0);
577 const double end = min(sample_count / samples_per_pixel -
578 pixels_offset, right + 1.0);
579 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
581 p.setPen(QPen(Qt::NoPen));
582 p.setBrush(Qt::white);
583 p.drawRect(no_decode_rect);
585 p.setPen(NoDecodeColour);
586 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
587 p.drawRect(no_decode_rect);
590 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
593 assert(decoder_stack_);
595 const View *view = owner_->view();
598 const double scale = view->scale();
601 const double pixels_offset =
602 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
604 double samplerate = decoder_stack_->samplerate();
606 // Show sample rate as 1Hz when it is unknown
607 if (samplerate == 0.0)
610 return make_pair(pixels_offset, samplerate * scale);
613 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
614 int x_start, int x_end) const
616 double samples_per_pixel, pixels_offset;
617 tie(pixels_offset, samples_per_pixel) =
618 get_pixels_offset_samples_per_pixel();
620 const uint64_t start = (uint64_t)max(
621 (x_start + pixels_offset) * samples_per_pixel, 0.0);
622 const uint64_t end = (uint64_t)max(
623 (x_end + pixels_offset) * samples_per_pixel, 0.0);
625 return make_pair(start, end);
628 int DecodeTrace::get_row_at_point(const QPoint &point)
633 const int y = (point.y() - get_visual_y() + row_height_ / 2);
635 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
639 const int row = y / row_height_;
641 if (row >= (int)visible_rows_.size())
647 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
649 using namespace pv::data::decode;
654 const pair<uint64_t, uint64_t> sample_range =
655 get_sample_range(point.x(), point.x() + 1);
656 const int row = get_row_at_point(point);
660 vector<pv::data::decode::Annotation> annotations;
662 assert(decoder_stack_);
663 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
664 sample_range.first, sample_range.second);
666 return (annotations.empty()) ?
667 QString() : annotations[0].annotations().front();
670 void DecodeTrace::hover_point_changed()
674 const View *const view = owner_->view();
677 QPoint hp = view->hover_point();
678 QString ann = get_annotation_at_point(hp);
682 if (!row_height_ || ann.isEmpty()) {
683 QToolTip::hideText();
687 const int hover_row = get_row_at_point(hp);
689 QFontMetrics m(QToolTip::font());
690 const QRect text_size = m.boundingRect(QRect(), 0, ann);
692 // This is OS-specific and unfortunately we can't query it, so
693 // use an approximation to at least try to minimize the error.
694 const int padding = 8;
696 // Make sure the tool tip doesn't overlap with the mouse cursor.
697 // If it did, the tool tip would constantly hide and re-appear.
698 // We also push it up by one row so that it appears above the
699 // decode trace, not below.
700 hp.setX(hp.x() - (text_size.width() / 2) - padding);
702 hp.setY(get_visual_y() - (row_height_ / 2) +
703 (hover_row * row_height_) -
704 row_height_ - text_size.height() - padding);
706 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
709 void DecodeTrace::create_decoder_form(int index,
710 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
716 const srd_decoder *const decoder = dec->decoder();
719 const bool decoder_deletable = index > 0;
721 pv::widgets::DecoderGroupBox *const group =
722 new pv::widgets::DecoderGroupBox(
723 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
724 group->set_decoder_visible(dec->shown());
726 if (decoder_deletable) {
727 delete_mapper_.setMapping(group, index);
728 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
731 show_hide_mapper_.setMapping(group, index);
732 connect(group, SIGNAL(show_hide_decoder()),
733 &show_hide_mapper_, SLOT(map()));
735 QFormLayout *const decoder_form = new QFormLayout;
736 group->add_layout(decoder_form);
738 // Add the mandatory channels
739 for (l = decoder->channels; l; l = l->next) {
740 const struct srd_channel *const pdch =
741 (struct srd_channel *)l->data;
742 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
743 connect(combo, SIGNAL(currentIndexChanged(int)),
744 this, SLOT(on_channel_selected(int)));
745 decoder_form->addRow(tr("<b>%1</b> (%2) *")
746 .arg(QString::fromUtf8(pdch->name))
747 .arg(QString::fromUtf8(pdch->desc)), combo);
749 const ChannelSelector s = {combo, dec, pdch};
750 channel_selectors_.push_back(s);
753 // Add the optional channels
754 for (l = decoder->opt_channels; l; l = l->next) {
755 const struct srd_channel *const pdch =
756 (struct srd_channel *)l->data;
757 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
758 connect(combo, SIGNAL(currentIndexChanged(int)),
759 this, SLOT(on_channel_selected(int)));
760 decoder_form->addRow(tr("<b>%1</b> (%2)")
761 .arg(QString::fromUtf8(pdch->name))
762 .arg(QString::fromUtf8(pdch->desc)), combo);
764 const ChannelSelector s = {combo, dec, pdch};
765 channel_selectors_.push_back(s);
769 shared_ptr<binding::Decoder> binding(
770 new binding::Decoder(decoder_stack_, dec));
771 binding->add_properties_to_form(decoder_form, true);
773 bindings_.push_back(binding);
776 decoder_forms_.push_back(group);
779 QComboBox* DecodeTrace::create_channel_selector(
780 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
781 const srd_channel *const pdch)
785 const auto sigs(session_.signals());
787 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
788 std::sort(sig_list.begin(), sig_list.end(),
789 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
790 return a->name().compare(b->name()) < 0; });
792 assert(decoder_stack_);
793 const auto channel_iter = dec->channels().find(pdch);
795 QComboBox *selector = new QComboBox(parent);
797 selector->addItem("-", qVariantFromValue((void*)nullptr));
799 if (channel_iter == dec->channels().end())
800 selector->setCurrentIndex(0);
802 for (const shared_ptr<view::Signal> &s : sig_list) {
804 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
805 selector->addItem(s->name(),
806 qVariantFromValue((void*)s.get()));
808 if (channel_iter != dec->channels().end() &&
809 (*channel_iter).second == s)
810 selector->setCurrentIndex(
811 selector->count() - 1);
818 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
822 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
824 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
826 for (const ChannelSelector &s : channel_selectors_) {
827 if (s.decoder_ != dec)
830 const LogicSignal *const selection =
831 (LogicSignal*)s.combo_->itemData(
832 s.combo_->currentIndex()).value<void*>();
834 for (shared_ptr<Signal> sig : sigs)
835 if (sig.get() == selection) {
836 channel_map[s.pdch_] =
837 dynamic_pointer_cast<LogicSignal>(sig);
842 dec->set_channels(channel_map);
845 void DecodeTrace::commit_channels()
847 assert(decoder_stack_);
848 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
849 commit_decoder_channels(dec);
851 decoder_stack_->begin_decode();
854 void DecodeTrace::on_new_decode_data()
857 owner_->row_item_appearance_changed(false, true);
860 void DecodeTrace::delete_pressed()
865 void DecodeTrace::on_delete()
867 session_.remove_decode_signal(this);
870 void DecodeTrace::on_channel_selected(int)
875 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
878 assert(decoder_stack_);
879 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
880 new data::decode::Decoder(decoder)));
881 decoder_stack_->begin_decode();
886 void DecodeTrace::on_delete_decoder(int index)
888 decoder_stack_->remove(index);
893 decoder_stack_->begin_decode();
896 void DecodeTrace::on_show_hide_decoder(int index)
898 using pv::data::decode::Decoder;
900 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
902 // Find the decoder in the stack
903 auto iter = stack.cbegin();
904 for (int i = 0; i < index; i++, iter++)
905 assert(iter != stack.end());
907 shared_ptr<Decoder> dec = *iter;
910 const bool show = !dec->shown();
913 assert(index < (int)decoder_forms_.size());
914 decoder_forms_[index]->set_decoder_visible(show);
917 owner_->row_item_appearance_changed(false, true);