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, see <http://www.gnu.org/licenses/>.
21 #include <libsigrokdecode/libsigrokdecode.h>
30 #include <boost/functional/hash.hpp>
31 #include <boost/thread/locks.hpp>
32 #include <boost/thread/shared_mutex.hpp>
35 #include <QApplication>
37 #include <QFormLayout>
40 #include <QPushButton>
43 #include "decodetrace.hpp"
45 #include <pv/session.hpp>
46 #include <pv/strnatcmp.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/view.hpp>
53 #include <pv/view/viewport.hpp>
54 #include <pv/widgets/decodergroupbox.hpp>
55 #include <pv/widgets/decodermenu.hpp>
57 using boost::shared_lock;
58 using boost::shared_mutex;
61 using std::dynamic_pointer_cast;
63 using std::lock_guard;
69 using std::out_of_range;
71 using std::shared_ptr;
72 using std::make_shared;
74 using std::unordered_set;
81 const QColor DecodeTrace::DecodeColours[4] = {
82 QColor(0xEF, 0x29, 0x29), // Red
83 QColor(0xFC, 0xE9, 0x4F), // Yellow
84 QColor(0x8A, 0xE2, 0x34), // Green
85 QColor(0x72, 0x9F, 0xCF) // Blue
88 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
89 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
91 const int DecodeTrace::ArrowSize = 4;
92 const double DecodeTrace::EndCapWidth = 5;
93 const int DecodeTrace::RowTitleMargin = 10;
94 const int DecodeTrace::DrawPadding = 100;
96 const QColor DecodeTrace::Colours[16] = {
97 QColor(0xEF, 0x29, 0x29),
98 QColor(0xF6, 0x6A, 0x32),
99 QColor(0xFC, 0xAE, 0x3E),
100 QColor(0xFB, 0xCA, 0x47),
101 QColor(0xFC, 0xE9, 0x4F),
102 QColor(0xCD, 0xF0, 0x40),
103 QColor(0x8A, 0xE2, 0x34),
104 QColor(0x4E, 0xDC, 0x44),
105 QColor(0x55, 0xD7, 0x95),
106 QColor(0x64, 0xD1, 0xD2),
107 QColor(0x72, 0x9F, 0xCF),
108 QColor(0xD4, 0x76, 0xC4),
109 QColor(0x9D, 0x79, 0xB9),
110 QColor(0xAD, 0x7F, 0xA8),
111 QColor(0xC2, 0x62, 0x9B),
112 QColor(0xD7, 0x47, 0x6F)
115 const QColor DecodeTrace::OutlineColours[16] = {
116 QColor(0x77, 0x14, 0x14),
117 QColor(0x7B, 0x35, 0x19),
118 QColor(0x7E, 0x57, 0x1F),
119 QColor(0x7D, 0x65, 0x23),
120 QColor(0x7E, 0x74, 0x27),
121 QColor(0x66, 0x78, 0x20),
122 QColor(0x45, 0x71, 0x1A),
123 QColor(0x27, 0x6E, 0x22),
124 QColor(0x2A, 0x6B, 0x4A),
125 QColor(0x32, 0x68, 0x69),
126 QColor(0x39, 0x4F, 0x67),
127 QColor(0x6A, 0x3B, 0x62),
128 QColor(0x4E, 0x3C, 0x5C),
129 QColor(0x56, 0x3F, 0x54),
130 QColor(0x61, 0x31, 0x4D),
131 QColor(0x6B, 0x23, 0x37)
134 DecodeTrace::DecodeTrace(pv::Session &session,
135 shared_ptr<data::SignalBase> signalbase, int index) :
139 max_visible_rows_(0),
140 delete_mapper_(this),
141 show_hide_mapper_(this)
143 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
145 // Determine shortest string we want to see displayed in full
146 QFontMetrics m(QApplication::font());
147 min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
149 base_->set_name(QString::fromUtf8(decoder_stack->stack().front()->decoder()->name));
150 base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
152 connect(decoder_stack.get(), SIGNAL(new_decode_data()),
153 this, SLOT(on_new_decode_data()));
154 connect(&delete_mapper_, SIGNAL(mapped(int)),
155 this, SLOT(on_delete_decoder(int)));
156 connect(&show_hide_mapper_, SIGNAL(mapped(int)),
157 this, SLOT(on_show_hide_decoder(int)));
160 bool DecodeTrace::enabled() const
165 shared_ptr<data::SignalBase> DecodeTrace::base() const
170 pair<int, int> DecodeTrace::v_extents() const
172 const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
174 // Make an empty decode trace appear symmetrical
175 const int row_count = max(1, max_visible_rows_);
177 return make_pair(-row_height, row_height * row_count);
180 void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
182 Trace::paint_back(p, pp);
183 paint_axis(p, pp, get_visual_y());
186 void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
188 using namespace pv::data::decode;
190 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
192 const int text_height = ViewItemPaintParams::text_height();
193 row_height_ = (text_height * 6) / 4;
194 const int annotation_height = (text_height * 5) / 4;
196 assert(decoder_stack);
197 const QString err = decoder_stack->error_message();
198 if (!err.isEmpty()) {
199 draw_unresolved_period(
200 p, annotation_height, pp.left(), pp.right());
201 draw_error(p, err, pp);
205 // Set default pen to allow for text width calculation
208 // Iterate through the rows
209 int y = get_visual_y();
210 pair<uint64_t, uint64_t> sample_range = get_sample_range(
211 pp.left(), pp.right());
213 const vector<Row> rows(decoder_stack->get_visible_rows());
215 visible_rows_.clear();
216 for (const Row& row : rows) {
217 // Cache the row title widths
220 row_title_width = row_title_widths_.at(row);
221 } catch (out_of_range) {
222 const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
224 row_title_widths_[row] = w;
228 // Determine the row's color
229 size_t base_colour = 0x13579BDF;
230 boost::hash_combine(base_colour, this);
231 boost::hash_combine(base_colour, row.decoder());
232 boost::hash_combine(base_colour, row.row());
235 vector<Annotation> annotations;
236 decoder_stack->get_annotation_subset(annotations, row,
237 sample_range.first, sample_range.second);
238 if (!annotations.empty()) {
239 draw_annotations(annotations, p, annotation_height, pp, y,
240 base_colour, row_title_width);
244 visible_rows_.push_back(row);
249 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
251 if ((int)visible_rows_.size() > max_visible_rows_)
252 owner_->extents_changed(false, true);
254 // Update the maximum row count if needed
255 max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
258 void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
260 using namespace pv::data::decode;
264 for (size_t i = 0; i < visible_rows_.size(); i++) {
265 const int y = i * row_height_ + get_visual_y();
267 p.setPen(QPen(Qt::NoPen));
268 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
271 const QPointF points[] = {
272 QPointF(pp.left(), y - ArrowSize),
273 QPointF(pp.left() + ArrowSize, y),
274 QPointF(pp.left(), y + ArrowSize)
276 p.drawPolygon(points, countof(points));
279 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
280 pp.right() - pp.left(), row_height_);
281 const QString h(visible_rows_[i].title());
282 const int f = Qt::AlignLeft | Qt::AlignVCenter |
286 p.setPen(QApplication::palette().color(QPalette::Base));
287 for (int dx = -1; dx <= 1; dx++)
288 for (int dy = -1; dy <= 1; dy++)
289 if (dx != 0 && dy != 0)
290 p.drawText(r.translated(dx, dy), f, h);
293 p.setPen(QApplication::palette().color(QPalette::WindowText));
298 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
300 using pv::data::decode::Decoder;
302 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
306 assert(decoder_stack);
308 // Add the standard options
309 Trace::populate_popup_form(parent, form);
311 // Add the decoder options
313 channel_selectors_.clear();
314 decoder_forms_.clear();
316 const list< shared_ptr<Decoder> >& stack = decoder_stack->stack();
319 QLabel *const l = new QLabel(
320 tr("<p><i>No decoders in the stack</i></p>"));
321 l->setAlignment(Qt::AlignCenter);
324 auto iter = stack.cbegin();
325 for (int i = 0; i < (int)stack.size(); i++, iter++) {
326 shared_ptr<Decoder> dec(*iter);
327 create_decoder_form(i, dec, parent, form);
330 form->addRow(new QLabel(
331 tr("<i>* Required channels</i>"), parent));
334 // Add stacking button
335 pv::widgets::DecoderMenu *const decoder_menu =
336 new pv::widgets::DecoderMenu(parent);
337 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
338 this, SLOT(on_stack_decoder(srd_decoder*)));
340 QPushButton *const stack_button =
341 new QPushButton(tr("Stack Decoder"), parent);
342 stack_button->setMenu(decoder_menu);
344 QHBoxLayout *stack_button_box = new QHBoxLayout;
345 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
346 form->addRow(stack_button_box);
349 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
351 QMenu *const menu = Trace::create_context_menu(parent);
353 menu->addSeparator();
355 QAction *const del = new QAction(tr("Delete"), this);
356 del->setShortcuts(QKeySequence::Delete);
357 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
358 menu->addAction(del);
363 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
364 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
365 size_t base_colour, int row_title_width)
367 using namespace pv::data::decode;
369 vector<Annotation> a_block;
372 double samples_per_pixel, pixels_offset;
373 tie(pixels_offset, samples_per_pixel) =
374 get_pixels_offset_samples_per_pixel();
376 // Sort the annotations by start sample so that decoders
377 // can't confuse us by creating annotations out of order
378 stable_sort(annotations.begin(), annotations.end(),
379 [](const Annotation &a, const Annotation &b) {
380 return a.start_sample() < b.start_sample(); });
382 // Gather all annotations that form a visual "block" and draw them as such
383 for (const Annotation &a : annotations) {
385 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
386 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
387 const int a_width = a_end - a_start;
389 const int delta = a_end - p_end;
391 bool a_is_separate = false;
393 // Annotation wider than the threshold for a useful label width?
394 if (a_width >= min_useful_label_width_) {
395 for (const QString &ann_text : a.annotations()) {
396 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
397 // Annotation wide enough to fit a label? Don't put it in a block then
399 a_is_separate = true;
405 // Were the previous and this annotation more than a pixel apart?
406 if ((abs(delta) > 1) || a_is_separate) {
407 // Block was broken, draw annotations that form the current block
408 if (a_block.size() == 1) {
409 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
413 draw_annotation_block(a_block, p, h, y, base_colour);
419 draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
420 // Next annotation must start a new block. delta will be > 1
421 // because we set p_end to INT_MIN but that's okay since
422 // a_block will be empty, so nothing will be drawn
425 a_block.push_back(a);
430 if (a_block.size() == 1)
431 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
434 draw_annotation_block(a_block, p, h, y, base_colour);
437 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
438 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
439 size_t base_colour, int row_title_width) const
441 double samples_per_pixel, pixels_offset;
442 tie(pixels_offset, samples_per_pixel) =
443 get_pixels_offset_samples_per_pixel();
445 const double start = a.start_sample() / samples_per_pixel -
447 const double end = a.end_sample() / samples_per_pixel -
450 const size_t colour = (base_colour + a.format()) % countof(Colours);
451 p.setPen(OutlineColours[colour]);
452 p.setBrush(Colours[colour]);
454 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
457 if (a.start_sample() == a.end_sample())
458 draw_instant(a, p, h, start, y);
460 draw_range(a, p, h, start, end, y, pp,
464 void DecodeTrace::draw_annotation_block(
465 vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
466 int y, size_t base_colour) const
468 using namespace pv::data::decode;
470 if (annotations.empty())
473 double samples_per_pixel, pixels_offset;
474 tie(pixels_offset, samples_per_pixel) =
475 get_pixels_offset_samples_per_pixel();
477 const double start = annotations.front().start_sample() /
478 samples_per_pixel - pixels_offset;
479 const double end = annotations.back().end_sample() /
480 samples_per_pixel - pixels_offset;
482 const double top = y + .5 - h / 2;
483 const double bottom = y + .5 + h / 2;
485 const size_t colour = (base_colour + annotations.front().format()) %
488 // Check if all annotations are of the same type (i.e. we can use one color)
489 // or if we should use a neutral color (i.e. gray)
490 const int format = annotations.front().format();
491 const bool single_format = all_of(
492 annotations.begin(), annotations.end(),
493 [&](const Annotation &a) { return a.format() == format; });
495 p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
496 p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
499 QRectF(start, top, end - start, bottom - top), h/4, h/4);
502 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
503 int h, double x, int y) const
505 const QString text = a.annotations().empty() ?
506 QString() : a.annotations().back();
507 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
509 const QRectF rect(x - w / 2, y - h / 2, w, h);
511 p.drawRoundedRect(rect, h / 2, h / 2);
514 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
517 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
518 int h, double start, double end, int y, const ViewItemPaintParams &pp,
519 int row_title_width) const
521 const double top = y + .5 - h / 2;
522 const double bottom = y + .5 + h / 2;
523 const vector<QString> annotations = a.annotations();
525 // If the two ends are within 1 pixel, draw a vertical line
526 if (start + 1.0 > end) {
527 p.drawLine(QPointF(start, top), QPointF(start, bottom));
531 const double cap_width = min((end - start) / 4, EndCapWidth);
534 QPointF(start, y + .5f),
535 QPointF(start + cap_width, top),
536 QPointF(end - cap_width, top),
537 QPointF(end, y + .5f),
538 QPointF(end - cap_width, bottom),
539 QPointF(start + cap_width, bottom)
542 p.drawConvexPolygon(pts, countof(pts));
544 if (annotations.empty())
547 const int ann_start = start + cap_width;
548 const int ann_end = end - cap_width;
550 const int real_start = max(ann_start, pp.left() + row_title_width);
551 const int real_end = min(ann_end, pp.right());
552 const int real_width = real_end - real_start;
554 QRectF rect(real_start, y - h / 2, real_width, h);
555 if (rect.width() <= 4)
560 // Try to find an annotation that will fit
561 QString best_annotation;
564 for (const QString &a : annotations) {
565 const int w = p.boundingRect(QRectF(), 0, a).width();
566 if (w <= rect.width() && w > best_width)
567 best_annotation = a, best_width = w;
570 if (best_annotation.isEmpty())
571 best_annotation = annotations.back();
573 // If not ellide the last in the list
574 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
575 best_annotation, Qt::ElideRight, rect.width()));
578 void DecodeTrace::draw_error(QPainter &p, const QString &message,
579 const ViewItemPaintParams &pp)
581 const int y = get_visual_y();
583 p.setPen(ErrorBgColour.darker());
584 p.setBrush(ErrorBgColour);
586 const QRectF bounding_rect =
587 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
588 const QRectF text_rect = p.boundingRect(bounding_rect,
589 Qt::AlignCenter, message);
590 const float r = text_rect.height() / 4;
592 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
596 p.drawText(text_rect, message);
599 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
602 using namespace pv::data;
603 using pv::data::decode::Decoder;
605 double samples_per_pixel, pixels_offset;
607 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
609 assert(decoder_stack);
611 shared_ptr<Logic> data;
612 shared_ptr<data::SignalBase> signalbase;
614 const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
616 // We get the logic data of the first channel in the list.
617 // This works because we are currently assuming all
618 // LogicSignals have the same data/segment
619 for (const shared_ptr<Decoder> &dec : stack)
620 if (dec && !dec->channels().empty() &&
621 ((signalbase = (*dec->channels().begin()).second)) &&
622 ((data = signalbase->logic_data())))
625 if (!data || data->logic_segments().empty())
628 const shared_ptr<LogicSegment> segment =
629 data->logic_segments().front();
631 const int64_t sample_count = (int64_t)segment->get_sample_count();
632 if (sample_count == 0)
635 const int64_t samples_decoded = decoder_stack->samples_decoded();
636 if (sample_count == samples_decoded)
639 const int y = get_visual_y();
641 tie(pixels_offset, samples_per_pixel) =
642 get_pixels_offset_samples_per_pixel();
644 const double start = max(samples_decoded /
645 samples_per_pixel - pixels_offset, left - 1.0);
646 const double end = min(sample_count / samples_per_pixel -
647 pixels_offset, right + 1.0);
648 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
650 p.setPen(QPen(Qt::NoPen));
651 p.setBrush(Qt::white);
652 p.drawRect(no_decode_rect);
654 p.setPen(NoDecodeColour);
655 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
656 p.drawRect(no_decode_rect);
659 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
661 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
664 assert(decoder_stack);
666 const View *view = owner_->view();
669 const double scale = view->scale();
672 const double pixels_offset =
673 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
675 double samplerate = decoder_stack->samplerate();
677 // Show sample rate as 1Hz when it is unknown
678 if (samplerate == 0.0)
681 return make_pair(pixels_offset, samplerate * scale);
684 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
685 int x_start, int x_end) const
687 double samples_per_pixel, pixels_offset;
688 tie(pixels_offset, samples_per_pixel) =
689 get_pixels_offset_samples_per_pixel();
691 const uint64_t start = (uint64_t)max(
692 (x_start + pixels_offset) * samples_per_pixel, 0.0);
693 const uint64_t end = (uint64_t)max(
694 (x_end + pixels_offset) * samples_per_pixel, 0.0);
696 return make_pair(start, end);
699 int DecodeTrace::get_row_at_point(const QPoint &point)
704 const int y = (point.y() - get_visual_y() + row_height_ / 2);
706 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
710 const int row = y / row_height_;
712 if (row >= (int)visible_rows_.size())
718 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
720 using namespace pv::data::decode;
725 const pair<uint64_t, uint64_t> sample_range =
726 get_sample_range(point.x(), point.x() + 1);
727 const int row = get_row_at_point(point);
731 vector<pv::data::decode::Annotation> annotations;
733 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
735 assert(decoder_stack);
736 decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
737 sample_range.first, sample_range.second);
739 return (annotations.empty()) ?
740 QString() : annotations[0].annotations().front();
743 void DecodeTrace::hover_point_changed()
747 const View *const view = owner_->view();
750 QPoint hp = view->hover_point();
751 QString ann = get_annotation_at_point(hp);
755 if (!row_height_ || ann.isEmpty()) {
756 QToolTip::hideText();
760 const int hover_row = get_row_at_point(hp);
762 QFontMetrics m(QToolTip::font());
763 const QRect text_size = m.boundingRect(QRect(), 0, ann);
765 // This is OS-specific and unfortunately we can't query it, so
766 // use an approximation to at least try to minimize the error.
767 const int padding = 8;
769 // Make sure the tool tip doesn't overlap with the mouse cursor.
770 // If it did, the tool tip would constantly hide and re-appear.
771 // We also push it up by one row so that it appears above the
772 // decode trace, not below.
773 hp.setX(hp.x() - (text_size.width() / 2) - padding);
775 hp.setY(get_visual_y() - (row_height_ / 2) +
776 (hover_row * row_height_) -
777 row_height_ - text_size.height() - padding);
779 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
782 void DecodeTrace::create_decoder_form(int index,
783 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
789 const srd_decoder *const decoder = dec->decoder();
792 const bool decoder_deletable = index > 0;
794 pv::widgets::DecoderGroupBox *const group =
795 new pv::widgets::DecoderGroupBox(
796 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
797 group->set_decoder_visible(dec->shown());
799 if (decoder_deletable) {
800 delete_mapper_.setMapping(group, index);
801 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
804 show_hide_mapper_.setMapping(group, index);
805 connect(group, SIGNAL(show_hide_decoder()),
806 &show_hide_mapper_, SLOT(map()));
808 QFormLayout *const decoder_form = new QFormLayout;
809 group->add_layout(decoder_form);
811 // Add the mandatory channels
812 for (l = decoder->channels; l; l = l->next) {
813 const struct srd_channel *const pdch =
814 (struct srd_channel *)l->data;
815 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
816 connect(combo, SIGNAL(currentIndexChanged(int)),
817 this, SLOT(on_channel_selected(int)));
818 decoder_form->addRow(tr("<b>%1</b> (%2) *")
819 .arg(QString::fromUtf8(pdch->name),
820 QString::fromUtf8(pdch->desc)), combo);
822 const ChannelSelector s = {combo, dec, pdch};
823 channel_selectors_.push_back(s);
826 // Add the optional channels
827 for (l = decoder->opt_channels; l; l = l->next) {
828 const struct srd_channel *const pdch =
829 (struct srd_channel *)l->data;
830 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
831 connect(combo, SIGNAL(currentIndexChanged(int)),
832 this, SLOT(on_channel_selected(int)));
833 decoder_form->addRow(tr("<b>%1</b> (%2)")
834 .arg(QString::fromUtf8(pdch->name),
835 QString::fromUtf8(pdch->desc)), combo);
837 const ChannelSelector s = {combo, dec, pdch};
838 channel_selectors_.push_back(s);
841 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
844 shared_ptr<binding::Decoder> binding(
845 new binding::Decoder(decoder_stack, dec));
846 binding->add_properties_to_form(decoder_form, true);
848 bindings_.push_back(binding);
851 decoder_forms_.push_back(group);
854 QComboBox* DecodeTrace::create_channel_selector(
855 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
856 const srd_channel *const pdch)
860 const auto sigs(session_.signalbases());
862 vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
863 sort(sig_list.begin(), sig_list.end(),
864 [](const shared_ptr<data::SignalBase> &a,
865 const shared_ptr<data::SignalBase> &b) {
866 return strnatcasecmp(a->name().toStdString(),
867 b->name().toStdString()) < 0; });
869 const auto channel_iter = dec->channels().find(pdch);
871 QComboBox *selector = new QComboBox(parent);
873 selector->addItem("-", qVariantFromValue((void*)nullptr));
875 if (channel_iter == dec->channels().end())
876 selector->setCurrentIndex(0);
878 for (const shared_ptr<data::SignalBase> &b : sig_list) {
880 if (b->logic_data() && b->enabled()) {
881 selector->addItem(b->name(),
882 qVariantFromValue((void*)b.get()));
884 if (channel_iter != dec->channels().end() &&
885 (*channel_iter).second == b)
886 selector->setCurrentIndex(
887 selector->count() - 1);
894 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
898 map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
900 const unordered_set< shared_ptr<data::SignalBase> >
901 sigs(session_.signalbases());
903 for (const ChannelSelector &s : channel_selectors_) {
904 if (s.decoder_ != dec)
907 const data::SignalBase *const selection =
908 (data::SignalBase*)s.combo_->itemData(
909 s.combo_->currentIndex()).value<void*>();
911 for (shared_ptr<data::SignalBase> sig : sigs)
912 if (sig.get() == selection) {
913 channel_map[s.pdch_] = sig;
918 dec->set_channels(channel_map);
921 void DecodeTrace::commit_channels()
923 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
925 assert(decoder_stack);
926 for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
927 commit_decoder_channels(dec);
929 decoder_stack->begin_decode();
932 void DecodeTrace::on_new_decode_data()
935 owner_->row_item_appearance_changed(false, true);
938 void DecodeTrace::delete_pressed()
943 void DecodeTrace::on_delete()
945 session_.remove_decode_signal(base_);
948 void DecodeTrace::on_channel_selected(int)
953 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
955 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
958 assert(decoder_stack);
959 decoder_stack->push(make_shared<data::decode::Decoder>(decoder));
960 decoder_stack->begin_decode();
965 void DecodeTrace::on_delete_decoder(int index)
967 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
969 decoder_stack->remove(index);
974 decoder_stack->begin_decode();
977 void DecodeTrace::on_show_hide_decoder(int index)
979 using pv::data::decode::Decoder;
981 shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
983 const list< shared_ptr<Decoder> > stack(decoder_stack->stack());
985 // Find the decoder in the stack
986 auto iter = stack.cbegin();
987 for (int i = 0; i < index; i++, iter++)
988 assert(iter != stack.end());
990 shared_ptr<Decoder> dec = *iter;
993 const bool show = !dec->shown();
996 assert(index < (int)decoder_forms_.size());
997 decoder_forms_[index]->set_decoder_visible(show);
1000 owner_->row_item_appearance_changed(false, true);
1003 } // namespace TraceView
1004 } // namespace views