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 delete_mapper_(this),
137 show_hide_mapper_(this)
139 assert(decoder_stack_);
141 set_colour(DecodeColours[index % countof(DecodeColours)]);
143 connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
144 this, SLOT(on_new_decode_data()));
145 connect(&delete_mapper_, SIGNAL(mapped(int)),
146 this, SLOT(on_delete_decoder(int)));
147 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
148 this, SLOT(on_show_hide_decoder(int)));
151 bool DecodeTrace::enabled() const
156 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
158 return decoder_stack_;
161 pair<int, int> DecodeTrace::v_extents() const
163 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
164 const int rows = visible_rows_.size();
166 return make_pair(-row_height, row_height * 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());
227 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
229 using namespace pv::data::decode;
233 for (size_t i = 0; i < visible_rows_.size(); i++) {
234 const int y = i * row_height_ + get_visual_y();
236 p.setPen(QPen(Qt::NoPen));
237 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
240 const QPointF points[] = {
241 QPointF(pp.left(), y - ArrowSize),
242 QPointF(pp.left() + ArrowSize, y),
243 QPointF(pp.left(), y + ArrowSize)
245 p.drawPolygon(points, countof(points));
248 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
249 pp.right() - pp.left(), row_height_);
250 const QString h(visible_rows_[i].title());
251 const int f = Qt::AlignLeft | Qt::AlignVCenter |
255 p.setPen(QApplication::palette().color(QPalette::Base));
256 for (int dx = -1; dx <= 1; dx++)
257 for (int dy = -1; dy <= 1; dy++)
258 if (dx != 0 && dy != 0)
259 p.drawText(r.translated(dx, dy), f, h);
262 p.setPen(QApplication::palette().color(QPalette::WindowText));
267 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
269 using pv::data::decode::Decoder;
273 assert(decoder_stack_);
275 // Add the standard options
276 Trace::populate_popup_form(parent, form);
278 // Add the decoder options
280 channel_selectors_.clear();
281 decoder_forms_.clear();
283 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
286 QLabel *const l = new QLabel(
287 tr("<p><i>No decoders in the stack</i></p>"));
288 l->setAlignment(Qt::AlignCenter);
291 auto iter = stack.cbegin();
292 for (int i = 0; i < (int)stack.size(); i++, iter++) {
293 shared_ptr<Decoder> dec(*iter);
294 create_decoder_form(i, dec, parent, form);
297 form->addRow(new QLabel(
298 tr("<i>* Required channels</i>"), parent));
301 // Add stacking button
302 pv::widgets::DecoderMenu *const decoder_menu =
303 new pv::widgets::DecoderMenu(parent);
304 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
305 this, SLOT(on_stack_decoder(srd_decoder*)));
307 QPushButton *const stack_button =
308 new QPushButton(tr("Stack Decoder"), parent);
309 stack_button->setMenu(decoder_menu);
311 QHBoxLayout *stack_button_box = new QHBoxLayout;
312 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
313 form->addRow(stack_button_box);
316 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
318 QMenu *const menu = Trace::create_context_menu(parent);
320 menu->addSeparator();
322 QAction *const del = new QAction(tr("Delete"), this);
323 del->setShortcuts(QKeySequence::Delete);
324 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
325 menu->addAction(del);
330 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
331 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
334 using namespace pv::data::decode;
336 vector<Annotation> a_block;
337 int prev_ann_pos = INT_MIN;
339 double samples_per_pixel, pixels_offset;
340 tie(pixels_offset, samples_per_pixel) =
341 get_pixels_offset_samples_per_pixel();
343 // Gather all annotations that form a visual "block" and draw them as such
344 for (const Annotation &a : annotations) {
346 const int end = a.end_sample() / samples_per_pixel - pixels_offset;
347 const int delta = end - prev_ann_pos;
349 // Some annotations are in reverse order, so we cannot
350 // simply check for delta > 1
351 if (abs(delta) > 1) {
352 // Block was broken, draw it
353 if (a_block.size() == 1)
354 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
356 if (a_block.size() > 0)
357 draw_annotation_block(a_block, p, h, pp, y, base_colour);
362 a_block.push_back(a);
366 if (a_block.size() == 1)
367 draw_annotation(a_block.front(), p, h, pp, y, base_colour);
369 draw_annotation_block(a_block, p, h, pp, y, base_colour);
372 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
373 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
374 size_t base_colour) const
376 double samples_per_pixel, pixels_offset;
377 tie(pixels_offset, samples_per_pixel) =
378 get_pixels_offset_samples_per_pixel();
380 const double start = a.start_sample() / samples_per_pixel -
382 const double end = a.end_sample() / samples_per_pixel -
385 const size_t colour = (base_colour + a.format()) % countof(Colours);
386 const QColor &fill = Colours[colour];
387 const QColor &outline = OutlineColours[colour];
389 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
392 if (a.start_sample() == a.end_sample())
393 draw_instant(a, p, fill, outline, h, start, y);
395 draw_range(a, p, fill, outline, h, start, end, y);
398 void DecodeTrace::draw_annotation_block(vector<pv::data::decode::Annotation> a,
399 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
400 size_t base_colour) const
402 double samples_per_pixel, pixels_offset;
403 tie(pixels_offset, samples_per_pixel) =
404 get_pixels_offset_samples_per_pixel();
406 const size_t colour =
407 (base_colour + a.front().format()) % countof(Colours);
409 const int start = a.front().start_sample() / samples_per_pixel -
411 const int end = a.back().end_sample() / samples_per_pixel -
415 std::max(pp.left(), start),
417 std::min(pp.right(), end) - std::max(pp.left(), start) + 1,
420 p.setPen(OutlineColours[colour]);
421 p.setBrush(Colours[colour]);
425 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
426 QColor fill, QColor outline, int h, double x, int y) const
428 const QString text = a.annotations().empty() ?
429 QString() : a.annotations().back();
430 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
432 const QRectF rect(x - w / 2, y - h / 2, w, h);
436 p.drawRoundedRect(rect, h / 2, h / 2);
439 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
442 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
443 QColor fill, QColor outline, int h, double start,
444 double end, int y) const
446 const double top = y + .5 - h / 2;
447 const double bottom = y + .5 + h / 2;
448 const vector<QString> annotations = a.annotations();
453 // If the two ends are within 1 pixel, draw a vertical line
454 if (start + 1.0 > end) {
455 p.drawLine(QPointF(start, top), QPointF(start, bottom));
459 const double cap_width = min((end - start) / 4, EndCapWidth);
462 QPointF(start, y + .5f),
463 QPointF(start + cap_width, top),
464 QPointF(end - cap_width, top),
465 QPointF(end, y + .5f),
466 QPointF(end - cap_width, bottom),
467 QPointF(start + cap_width, bottom)
470 p.drawConvexPolygon(pts, countof(pts));
472 if (annotations.empty())
475 QRectF rect(start + cap_width, y - h / 2,
476 end - start - cap_width * 2, h);
477 if (rect.width() <= 4)
482 // Try to find an annotation that will fit
483 QString best_annotation;
486 for (const QString &a : annotations) {
487 const int w = p.boundingRect(QRectF(), 0, a).width();
488 if (w <= rect.width() && w > best_width)
489 best_annotation = a, best_width = w;
492 if (best_annotation.isEmpty())
493 best_annotation = annotations.back();
495 // If not ellide the last in the list
496 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
497 best_annotation, Qt::ElideRight, rect.width()));
500 void DecodeTrace::draw_error(QPainter &p, const QString &message,
501 const ViewItemPaintParams &pp)
503 const int y = get_visual_y();
505 p.setPen(ErrorBgColour.darker());
506 p.setBrush(ErrorBgColour);
508 const QRectF bounding_rect =
509 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
510 const QRectF text_rect = p.boundingRect(bounding_rect,
511 Qt::AlignCenter, message);
512 const float r = text_rect.height() / 4;
514 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
518 p.drawText(text_rect, message);
521 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
524 using namespace pv::data;
525 using pv::data::decode::Decoder;
527 double samples_per_pixel, pixels_offset;
529 assert(decoder_stack_);
531 shared_ptr<Logic> data;
532 shared_ptr<LogicSignal> logic_signal;
534 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
536 // We get the logic data of the first channel in the list.
537 // This works because we are currently assuming all
538 // LogicSignals have the same data/segment
539 for (const shared_ptr<Decoder> &dec : stack)
540 if (dec && !dec->channels().empty() &&
541 ((logic_signal = (*dec->channels().begin()).second)) &&
542 ((data = logic_signal->logic_data())))
545 if (!data || data->logic_segments().empty())
548 const shared_ptr<LogicSegment> segment =
549 data->logic_segments().front();
551 const int64_t sample_count = (int64_t)segment->get_sample_count();
552 if (sample_count == 0)
555 const int64_t samples_decoded = decoder_stack_->samples_decoded();
556 if (sample_count == samples_decoded)
559 const int y = get_visual_y();
561 tie(pixels_offset, samples_per_pixel) =
562 get_pixels_offset_samples_per_pixel();
564 const double start = max(samples_decoded /
565 samples_per_pixel - pixels_offset, left - 1.0);
566 const double end = min(sample_count / samples_per_pixel -
567 pixels_offset, right + 1.0);
568 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
570 p.setPen(QPen(Qt::NoPen));
571 p.setBrush(Qt::white);
572 p.drawRect(no_decode_rect);
574 p.setPen(NoDecodeColour);
575 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
576 p.drawRect(no_decode_rect);
579 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
582 assert(decoder_stack_);
584 const View *view = owner_->view();
587 const double scale = view->scale();
590 const double pixels_offset =
591 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
593 double samplerate = decoder_stack_->samplerate();
595 // Show sample rate as 1Hz when it is unknown
596 if (samplerate == 0.0)
599 return make_pair(pixels_offset, samplerate * scale);
602 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
603 int x_start, int x_end) const
605 double samples_per_pixel, pixels_offset;
606 tie(pixels_offset, samples_per_pixel) =
607 get_pixels_offset_samples_per_pixel();
609 const uint64_t start = (uint64_t)max(
610 (x_start + pixels_offset) * samples_per_pixel, 0.0);
611 const uint64_t end = (uint64_t)max(
612 (x_end + pixels_offset) * samples_per_pixel, 0.0);
614 return make_pair(start, end);
617 int DecodeTrace::get_row_at_point(const QPoint &point)
622 const int y = (point.y() - get_visual_y() + row_height_ / 2);
624 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
628 const int row = y / row_height_;
630 if (row >= (int)visible_rows_.size())
636 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
638 using namespace pv::data::decode;
643 const pair<uint64_t, uint64_t> sample_range =
644 get_sample_range(point.x(), point.x() + 1);
645 const int row = get_row_at_point(point);
649 vector<pv::data::decode::Annotation> annotations;
651 assert(decoder_stack_);
652 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
653 sample_range.first, sample_range.second);
655 return (annotations.empty()) ?
656 QString() : annotations[0].annotations().front();
659 void DecodeTrace::hover_point_changed()
663 const View *const view = owner_->view();
666 QPoint hp = view->hover_point();
667 QString ann = get_annotation_at_point(hp);
671 if (!row_height_ || ann.isEmpty()) {
672 QToolTip::hideText();
676 const int hover_row = get_row_at_point(hp);
678 QFontMetrics m(QToolTip::font());
679 const QRect text_size = m.boundingRect(QRect(), 0, ann);
681 // This is OS-specific and unfortunately we can't query it, so
682 // use an approximation to at least try to minimize the error.
683 const int padding = 8;
685 // Make sure the tool tip doesn't overlap with the mouse cursor.
686 // If it did, the tool tip would constantly hide and re-appear.
687 // We also push it up by one row so that it appears above the
688 // decode trace, not below.
689 hp.setX(hp.x() - (text_size.width() / 2) - padding);
691 hp.setY(get_visual_y() - (row_height_ / 2) +
692 (hover_row * row_height_) -
693 row_height_ - text_size.height() - padding);
695 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
698 void DecodeTrace::create_decoder_form(int index,
699 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
705 const srd_decoder *const decoder = dec->decoder();
708 const bool decoder_deletable = index > 0;
710 pv::widgets::DecoderGroupBox *const group =
711 new pv::widgets::DecoderGroupBox(
712 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
713 group->set_decoder_visible(dec->shown());
715 if (decoder_deletable) {
716 delete_mapper_.setMapping(group, index);
717 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
720 show_hide_mapper_.setMapping(group, index);
721 connect(group, SIGNAL(show_hide_decoder()),
722 &show_hide_mapper_, SLOT(map()));
724 QFormLayout *const decoder_form = new QFormLayout;
725 group->add_layout(decoder_form);
727 // Add the mandatory channels
728 for (l = decoder->channels; l; l = l->next) {
729 const struct srd_channel *const pdch =
730 (struct srd_channel *)l->data;
731 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
732 connect(combo, SIGNAL(currentIndexChanged(int)),
733 this, SLOT(on_channel_selected(int)));
734 decoder_form->addRow(tr("<b>%1</b> (%2) *")
735 .arg(QString::fromUtf8(pdch->name))
736 .arg(QString::fromUtf8(pdch->desc)), combo);
738 const ChannelSelector s = {combo, dec, pdch};
739 channel_selectors_.push_back(s);
742 // Add the optional channels
743 for (l = decoder->opt_channels; l; l = l->next) {
744 const struct srd_channel *const pdch =
745 (struct srd_channel *)l->data;
746 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
747 connect(combo, SIGNAL(currentIndexChanged(int)),
748 this, SLOT(on_channel_selected(int)));
749 decoder_form->addRow(tr("<b>%1</b> (%2)")
750 .arg(QString::fromUtf8(pdch->name))
751 .arg(QString::fromUtf8(pdch->desc)), combo);
753 const ChannelSelector s = {combo, dec, pdch};
754 channel_selectors_.push_back(s);
758 shared_ptr<binding::Decoder> binding(
759 new binding::Decoder(decoder_stack_, dec));
760 binding->add_properties_to_form(decoder_form, true);
762 bindings_.push_back(binding);
765 decoder_forms_.push_back(group);
768 QComboBox* DecodeTrace::create_channel_selector(
769 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
770 const srd_channel *const pdch)
774 const auto sigs(session_.signals());
776 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
777 std::sort(sig_list.begin(), sig_list.end(),
778 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
779 return a->name().compare(b->name()) < 0; });
781 assert(decoder_stack_);
782 const auto channel_iter = dec->channels().find(pdch);
784 QComboBox *selector = new QComboBox(parent);
786 selector->addItem("-", qVariantFromValue((void*)nullptr));
788 if (channel_iter == dec->channels().end())
789 selector->setCurrentIndex(0);
791 for (const shared_ptr<view::Signal> &s : sig_list) {
793 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
794 selector->addItem(s->name(),
795 qVariantFromValue((void*)s.get()));
797 if (channel_iter != dec->channels().end() &&
798 (*channel_iter).second == s)
799 selector->setCurrentIndex(
800 selector->count() - 1);
807 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
811 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
813 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
815 for (const ChannelSelector &s : channel_selectors_) {
816 if (s.decoder_ != dec)
819 const LogicSignal *const selection =
820 (LogicSignal*)s.combo_->itemData(
821 s.combo_->currentIndex()).value<void*>();
823 for (shared_ptr<Signal> sig : sigs)
824 if (sig.get() == selection) {
825 channel_map[s.pdch_] =
826 dynamic_pointer_cast<LogicSignal>(sig);
831 dec->set_channels(channel_map);
834 void DecodeTrace::commit_channels()
836 assert(decoder_stack_);
837 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
838 commit_decoder_channels(dec);
840 decoder_stack_->begin_decode();
843 void DecodeTrace::on_new_decode_data()
846 owner_->row_item_appearance_changed(false, true);
849 void DecodeTrace::delete_pressed()
854 void DecodeTrace::on_delete()
856 session_.remove_decode_signal(this);
859 void DecodeTrace::on_channel_selected(int)
864 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
867 assert(decoder_stack_);
868 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
869 new data::decode::Decoder(decoder)));
870 decoder_stack_->begin_decode();
875 void DecodeTrace::on_delete_decoder(int index)
877 decoder_stack_->remove(index);
882 decoder_stack_->begin_decode();
885 void DecodeTrace::on_show_hide_decoder(int index)
887 using pv::data::decode::Decoder;
889 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
891 // Find the decoder in the stack
892 auto iter = stack.cbegin();
893 for (int i = 0; i < index; i++, iter++)
894 assert(iter != stack.end());
896 shared_ptr<Decoder> dec = *iter;
899 const bool show = !dec->shown();
902 assert(index < (int)decoder_forms_.size());
903 decoder_forms_[index]->set_decoder_visible(show);
906 owner_->row_item_appearance_changed(false, true);