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, pp);
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 ViewItemPaintParams &pp) 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 const int ann_start = start + cap_width;
520 const int ann_end = end - cap_width;
522 const int real_start = std::max(ann_start, pp.left());
523 const int real_end = std::min(ann_end, pp.right());
525 const int real_width = real_end - real_start;
527 QRectF rect(real_start, y - h / 2, real_width, h);
528 if (rect.width() <= 4)
533 // Try to find an annotation that will fit
534 QString best_annotation;
537 for (const QString &a : annotations) {
538 const int w = p.boundingRect(QRectF(), 0, a).width();
539 if (w <= rect.width() && w > best_width)
540 best_annotation = a, best_width = w;
543 if (best_annotation.isEmpty())
544 best_annotation = annotations.back();
546 // If not ellide the last in the list
547 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
548 best_annotation, Qt::ElideRight, rect.width()));
551 void DecodeTrace::draw_error(QPainter &p, const QString &message,
552 const ViewItemPaintParams &pp)
554 const int y = get_visual_y();
556 p.setPen(ErrorBgColour.darker());
557 p.setBrush(ErrorBgColour);
559 const QRectF bounding_rect =
560 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
561 const QRectF text_rect = p.boundingRect(bounding_rect,
562 Qt::AlignCenter, message);
563 const float r = text_rect.height() / 4;
565 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
569 p.drawText(text_rect, message);
572 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
575 using namespace pv::data;
576 using pv::data::decode::Decoder;
578 double samples_per_pixel, pixels_offset;
580 assert(decoder_stack_);
582 shared_ptr<Logic> data;
583 shared_ptr<LogicSignal> logic_signal;
585 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
587 // We get the logic data of the first channel in the list.
588 // This works because we are currently assuming all
589 // LogicSignals have the same data/segment
590 for (const shared_ptr<Decoder> &dec : stack)
591 if (dec && !dec->channels().empty() &&
592 ((logic_signal = (*dec->channels().begin()).second)) &&
593 ((data = logic_signal->logic_data())))
596 if (!data || data->logic_segments().empty())
599 const shared_ptr<LogicSegment> segment =
600 data->logic_segments().front();
602 const int64_t sample_count = (int64_t)segment->get_sample_count();
603 if (sample_count == 0)
606 const int64_t samples_decoded = decoder_stack_->samples_decoded();
607 if (sample_count == samples_decoded)
610 const int y = get_visual_y();
612 tie(pixels_offset, samples_per_pixel) =
613 get_pixels_offset_samples_per_pixel();
615 const double start = max(samples_decoded /
616 samples_per_pixel - pixels_offset, left - 1.0);
617 const double end = min(sample_count / samples_per_pixel -
618 pixels_offset, right + 1.0);
619 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
621 p.setPen(QPen(Qt::NoPen));
622 p.setBrush(Qt::white);
623 p.drawRect(no_decode_rect);
625 p.setPen(NoDecodeColour);
626 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
627 p.drawRect(no_decode_rect);
630 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
633 assert(decoder_stack_);
635 const View *view = owner_->view();
638 const double scale = view->scale();
641 const double pixels_offset =
642 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
644 double samplerate = decoder_stack_->samplerate();
646 // Show sample rate as 1Hz when it is unknown
647 if (samplerate == 0.0)
650 return make_pair(pixels_offset, samplerate * scale);
653 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
654 int x_start, int x_end) const
656 double samples_per_pixel, pixels_offset;
657 tie(pixels_offset, samples_per_pixel) =
658 get_pixels_offset_samples_per_pixel();
660 const uint64_t start = (uint64_t)max(
661 (x_start + pixels_offset) * samples_per_pixel, 0.0);
662 const uint64_t end = (uint64_t)max(
663 (x_end + pixels_offset) * samples_per_pixel, 0.0);
665 return make_pair(start, end);
668 int DecodeTrace::get_row_at_point(const QPoint &point)
673 const int y = (point.y() - get_visual_y() + row_height_ / 2);
675 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
679 const int row = y / row_height_;
681 if (row >= (int)visible_rows_.size())
687 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
689 using namespace pv::data::decode;
694 const pair<uint64_t, uint64_t> sample_range =
695 get_sample_range(point.x(), point.x() + 1);
696 const int row = get_row_at_point(point);
700 vector<pv::data::decode::Annotation> annotations;
702 assert(decoder_stack_);
703 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
704 sample_range.first, sample_range.second);
706 return (annotations.empty()) ?
707 QString() : annotations[0].annotations().front();
710 void DecodeTrace::hover_point_changed()
714 const View *const view = owner_->view();
717 QPoint hp = view->hover_point();
718 QString ann = get_annotation_at_point(hp);
722 if (!row_height_ || ann.isEmpty()) {
723 QToolTip::hideText();
727 const int hover_row = get_row_at_point(hp);
729 QFontMetrics m(QToolTip::font());
730 const QRect text_size = m.boundingRect(QRect(), 0, ann);
732 // This is OS-specific and unfortunately we can't query it, so
733 // use an approximation to at least try to minimize the error.
734 const int padding = 8;
736 // Make sure the tool tip doesn't overlap with the mouse cursor.
737 // If it did, the tool tip would constantly hide and re-appear.
738 // We also push it up by one row so that it appears above the
739 // decode trace, not below.
740 hp.setX(hp.x() - (text_size.width() / 2) - padding);
742 hp.setY(get_visual_y() - (row_height_ / 2) +
743 (hover_row * row_height_) -
744 row_height_ - text_size.height() - padding);
746 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
749 void DecodeTrace::create_decoder_form(int index,
750 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
756 const srd_decoder *const decoder = dec->decoder();
759 const bool decoder_deletable = index > 0;
761 pv::widgets::DecoderGroupBox *const group =
762 new pv::widgets::DecoderGroupBox(
763 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
764 group->set_decoder_visible(dec->shown());
766 if (decoder_deletable) {
767 delete_mapper_.setMapping(group, index);
768 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
771 show_hide_mapper_.setMapping(group, index);
772 connect(group, SIGNAL(show_hide_decoder()),
773 &show_hide_mapper_, SLOT(map()));
775 QFormLayout *const decoder_form = new QFormLayout;
776 group->add_layout(decoder_form);
778 // Add the mandatory channels
779 for (l = decoder->channels; l; l = l->next) {
780 const struct srd_channel *const pdch =
781 (struct srd_channel *)l->data;
782 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
783 connect(combo, SIGNAL(currentIndexChanged(int)),
784 this, SLOT(on_channel_selected(int)));
785 decoder_form->addRow(tr("<b>%1</b> (%2) *")
786 .arg(QString::fromUtf8(pdch->name))
787 .arg(QString::fromUtf8(pdch->desc)), combo);
789 const ChannelSelector s = {combo, dec, pdch};
790 channel_selectors_.push_back(s);
793 // Add the optional channels
794 for (l = decoder->opt_channels; l; l = l->next) {
795 const struct srd_channel *const pdch =
796 (struct srd_channel *)l->data;
797 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
798 connect(combo, SIGNAL(currentIndexChanged(int)),
799 this, SLOT(on_channel_selected(int)));
800 decoder_form->addRow(tr("<b>%1</b> (%2)")
801 .arg(QString::fromUtf8(pdch->name))
802 .arg(QString::fromUtf8(pdch->desc)), combo);
804 const ChannelSelector s = {combo, dec, pdch};
805 channel_selectors_.push_back(s);
809 shared_ptr<binding::Decoder> binding(
810 new binding::Decoder(decoder_stack_, dec));
811 binding->add_properties_to_form(decoder_form, true);
813 bindings_.push_back(binding);
816 decoder_forms_.push_back(group);
819 QComboBox* DecodeTrace::create_channel_selector(
820 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
821 const srd_channel *const pdch)
825 const auto sigs(session_.signals());
827 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
828 std::sort(sig_list.begin(), sig_list.end(),
829 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
830 return a->name().compare(b->name()) < 0; });
832 assert(decoder_stack_);
833 const auto channel_iter = dec->channels().find(pdch);
835 QComboBox *selector = new QComboBox(parent);
837 selector->addItem("-", qVariantFromValue((void*)nullptr));
839 if (channel_iter == dec->channels().end())
840 selector->setCurrentIndex(0);
842 for (const shared_ptr<view::Signal> &s : sig_list) {
844 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
845 selector->addItem(s->name(),
846 qVariantFromValue((void*)s.get()));
848 if (channel_iter != dec->channels().end() &&
849 (*channel_iter).second == s)
850 selector->setCurrentIndex(
851 selector->count() - 1);
858 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
862 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
864 const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
866 for (const ChannelSelector &s : channel_selectors_) {
867 if (s.decoder_ != dec)
870 const LogicSignal *const selection =
871 (LogicSignal*)s.combo_->itemData(
872 s.combo_->currentIndex()).value<void*>();
874 for (shared_ptr<Signal> sig : sigs)
875 if (sig.get() == selection) {
876 channel_map[s.pdch_] =
877 dynamic_pointer_cast<LogicSignal>(sig);
882 dec->set_channels(channel_map);
885 void DecodeTrace::commit_channels()
887 assert(decoder_stack_);
888 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
889 commit_decoder_channels(dec);
891 decoder_stack_->begin_decode();
894 void DecodeTrace::on_new_decode_data()
897 owner_->row_item_appearance_changed(false, true);
900 void DecodeTrace::delete_pressed()
905 void DecodeTrace::on_delete()
907 session_.remove_decode_signal(this);
910 void DecodeTrace::on_channel_selected(int)
915 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
918 assert(decoder_stack_);
919 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
920 new data::decode::Decoder(decoder)));
921 decoder_stack_->begin_decode();
926 void DecodeTrace::on_delete_decoder(int index)
928 decoder_stack_->remove(index);
933 decoder_stack_->begin_decode();
936 void DecodeTrace::on_show_hide_decoder(int index)
938 using pv::data::decode::Decoder;
940 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
942 // Find the decoder in the stack
943 auto iter = stack.cbegin();
944 for (int i = 0; i < index; i++, iter++)
945 assert(iter != stack.end());
947 shared_ptr<Decoder> dec = *iter;
950 const bool show = !dec->shown();
953 assert(index < (int)decoder_forms_.size());
954 decoder_forms_[index]->set_decoder_visible(show);
957 owner_->row_item_appearance_changed(false, true);