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>
27 #include <boost/functional/hash.hpp>
30 #include <QApplication>
32 #include <QFormLayout>
35 #include <QPushButton>
37 #include "decodetrace.h"
39 #include <pv/sigsession.h>
40 #include <pv/data/decoderstack.h>
41 #include <pv/data/decode/decoder.h>
42 #include <pv/data/logic.h>
43 #include <pv/data/logicsnapshot.h>
44 #include <pv/data/decode/annotation.h>
45 #include <pv/view/logicsignal.h>
46 #include <pv/view/view.h>
47 #include <pv/widgets/decodergroupbox.h>
48 #include <pv/widgets/decodermenu.h>
50 using std::dynamic_pointer_cast;
57 using std::shared_ptr;
63 const QColor DecodeTrace::DecodeColours[4] = {
64 QColor(0xEF, 0x29, 0x29), // Red
65 QColor(0xFC, 0xE9, 0x4F), // Yellow
66 QColor(0x8A, 0xE2, 0x34), // Green
67 QColor(0x72, 0x9F, 0xCF) // Blue
70 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
71 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
73 const int DecodeTrace::ArrowSize = 4;
74 const double DecodeTrace::EndCapWidth = 5;
75 const int DecodeTrace::DrawPadding = 100;
77 const QColor DecodeTrace::Colours[16] = {
78 QColor(0xEF, 0x29, 0x29),
79 QColor(0xF6, 0x6A, 0x32),
80 QColor(0xFC, 0xAE, 0x3E),
81 QColor(0xFB, 0xCA, 0x47),
82 QColor(0xFC, 0xE9, 0x4F),
83 QColor(0xCD, 0xF0, 0x40),
84 QColor(0x8A, 0xE2, 0x34),
85 QColor(0x4E, 0xDC, 0x44),
86 QColor(0x55, 0xD7, 0x95),
87 QColor(0x64, 0xD1, 0xD2),
88 QColor(0x72, 0x9F, 0xCF),
89 QColor(0xD4, 0x76, 0xC4),
90 QColor(0x9D, 0x79, 0xB9),
91 QColor(0xAD, 0x7F, 0xA8),
92 QColor(0xC2, 0x62, 0x9B),
93 QColor(0xD7, 0x47, 0x6F)
96 const QColor DecodeTrace::OutlineColours[16] = {
97 QColor(0x77, 0x14, 0x14),
98 QColor(0x7B, 0x35, 0x19),
99 QColor(0x7E, 0x57, 0x1F),
100 QColor(0x7D, 0x65, 0x23),
101 QColor(0x7E, 0x74, 0x27),
102 QColor(0x66, 0x78, 0x20),
103 QColor(0x45, 0x71, 0x1A),
104 QColor(0x27, 0x6E, 0x22),
105 QColor(0x2A, 0x6B, 0x4A),
106 QColor(0x32, 0x68, 0x69),
107 QColor(0x39, 0x4F, 0x67),
108 QColor(0x6A, 0x3B, 0x62),
109 QColor(0x4E, 0x3C, 0x5C),
110 QColor(0x56, 0x3F, 0x54),
111 QColor(0x61, 0x31, 0x4D),
112 QColor(0x6B, 0x23, 0x37)
115 DecodeTrace::DecodeTrace(pv::SigSession &session,
116 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
117 Trace(QString::fromUtf8(
118 decoder_stack->stack().front()->decoder()->name)),
120 _decoder_stack(decoder_stack),
123 _delete_mapper(this),
124 _show_hide_mapper(this)
126 assert(_decoder_stack);
128 _colour = DecodeColours[index % countof(DecodeColours)];
130 connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
131 this, SLOT(on_new_decode_data()));
132 connect(&_delete_mapper, SIGNAL(mapped(int)),
133 this, SLOT(on_delete_decoder(int)));
134 connect(&_show_hide_mapper, SIGNAL(mapped(int)),
135 this, SLOT(on_show_hide_decoder(int)));
138 bool DecodeTrace::enabled() const
143 const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
145 return _decoder_stack;
148 void DecodeTrace::set_view(pv::view::View *view)
151 Trace::set_view(view);
154 void DecodeTrace::paint_back(QPainter &p, int left, int right)
156 Trace::paint_back(p, left, right);
157 paint_axis(p, get_y(), left, right);
160 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
162 using namespace pv::data::decode;
164 QFontMetrics m(QApplication::font());
165 _text_height = m.boundingRect(QRect(), 0, "Tg").height();
166 _row_height = (_text_height * 6) / 4;
167 const int annotation_height = (_text_height * 5) / 4;
169 assert(_decoder_stack);
170 const QString err = _decoder_stack->error_message();
173 draw_unresolved_period(p, annotation_height, left, right);
174 draw_error(p, err, left, right);
178 // Iterate through the rows
181 pair<uint64_t, uint64_t> sample_range = get_sample_range(left, right);
183 assert(_decoder_stack);
184 const vector<Row> rows(_decoder_stack->get_visible_rows());
186 _cur_row_headings.clear();
187 for (size_t i = 0; i < rows.size(); i++)
189 const Row &row = rows[i];
191 size_t base_colour = 0x13579BDF;
192 boost::hash_combine(base_colour, this);
193 boost::hash_combine(base_colour, row.decoder());
194 boost::hash_combine(base_colour, row.row());
197 vector<Annotation> annotations;
198 _decoder_stack->get_annotation_subset(annotations, row,
199 sample_range.first, sample_range.second);
200 if (!annotations.empty()) {
201 for (const Annotation &a : annotations)
202 draw_annotation(a, p, get_text_colour(),
203 annotation_height, left, right, y,
207 _cur_row_headings.push_back(row.title());
212 draw_unresolved_period(p, annotation_height, left, right);
215 void DecodeTrace::paint_fore(QPainter &p, int left, int right)
217 using namespace pv::data::decode;
223 for (size_t i = 0; i < _cur_row_headings.size(); i++)
225 const int y = i * _row_height + get_y();
227 p.setPen(QPen(Qt::NoPen));
228 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
232 const QPointF points[] = {
233 QPointF(left, y - ArrowSize),
234 QPointF(left + ArrowSize, y),
235 QPointF(left, y + ArrowSize)
237 p.drawPolygon(points, countof(points));
240 const QRect r(left + ArrowSize * 2, y - _row_height / 2,
241 right - left, _row_height);
242 const QString h(_cur_row_headings[i]);
243 const int f = Qt::AlignLeft | Qt::AlignVCenter |
247 p.setPen(QApplication::palette().color(QPalette::Base));
248 for (int dx = -1; dx <= 1; dx++)
249 for (int dy = -1; dy <= 1; dy++)
250 if (dx != 0 && dy != 0)
251 p.drawText(r.translated(dx, dy), f, h);
254 p.setPen(QApplication::palette().color(QPalette::WindowText));
259 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
261 using pv::data::decode::Decoder;
265 assert(_decoder_stack);
267 // Add the standard options
268 Trace::populate_popup_form(parent, form);
270 // Add the decoder options
272 _channel_selectors.clear();
273 _decoder_forms.clear();
275 const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
279 QLabel *const l = new QLabel(
280 tr("<p><i>No decoders in the stack</i></p>"));
281 l->setAlignment(Qt::AlignCenter);
286 auto iter = stack.cbegin();
287 for (int i = 0; i < (int)stack.size(); i++, iter++) {
288 shared_ptr<Decoder> dec(*iter);
289 create_decoder_form(i, dec, parent, form);
292 form->addRow(new QLabel(
293 tr("<i>* Required channels</i>"), parent));
296 // Add stacking button
297 pv::widgets::DecoderMenu *const decoder_menu =
298 new pv::widgets::DecoderMenu(parent);
299 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
300 this, SLOT(on_stack_decoder(srd_decoder*)));
302 QPushButton *const stack_button =
303 new QPushButton(tr("Stack Decoder"), parent);
304 stack_button->setMenu(decoder_menu);
306 QHBoxLayout *stack_button_box = new QHBoxLayout;
307 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
308 form->addRow(stack_button_box);
311 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
313 QMenu *const menu = Trace::create_context_menu(parent);
315 menu->addSeparator();
317 QAction *const del = new QAction(tr("Delete"), this);
318 del->setShortcuts(QKeySequence::Delete);
319 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
320 menu->addAction(del);
325 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
326 QPainter &p, QColor text_color, int h, int left, int right, int y,
327 size_t base_colour) const
329 const double samples_per_pixel = get_samples_per_pixel();
330 const double pixels_offset = get_pixels_offset();
332 const double start = a.start_sample() / samples_per_pixel -
334 const double end = a.end_sample() / samples_per_pixel -
337 const size_t colour = (base_colour + a.format()) % countof(Colours);
338 const QColor &fill = Colours[colour];
339 const QColor &outline = OutlineColours[colour];
341 if (start > right + DrawPadding || end < left - DrawPadding)
344 if (a.start_sample() == a.end_sample())
345 draw_instant(a, p, fill, outline, text_color, h,
348 draw_range(a, p, fill, outline, text_color, h,
352 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
353 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
355 const QString text = a.annotations().empty() ?
356 QString() : a.annotations().back();
357 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
359 const QRectF rect(x - w / 2, y - h / 2, w, h);
363 p.drawRoundedRect(rect, h / 2, h / 2);
365 p.setPen(text_color);
366 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
369 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
370 QColor fill, QColor outline, QColor text_color, int h, double start,
371 double end, int y) const
373 const double top = y + .5 - h / 2;
374 const double bottom = y + .5 + h / 2;
375 const vector<QString> annotations = a.annotations();
380 // If the two ends are within 1 pixel, draw a vertical line
381 if (start + 1.0 > end)
383 p.drawLine(QPointF(start, top), QPointF(start, bottom));
387 const double cap_width = min((end - start) / 4, EndCapWidth);
390 QPointF(start, y + .5f),
391 QPointF(start + cap_width, top),
392 QPointF(end - cap_width, top),
393 QPointF(end, y + .5f),
394 QPointF(end - cap_width, bottom),
395 QPointF(start + cap_width, bottom)
398 p.drawConvexPolygon(pts, countof(pts));
400 if (annotations.empty())
403 QRectF rect(start + cap_width, y - h / 2,
404 end - start - cap_width * 2, h);
405 if (rect.width() <= 4)
408 p.setPen(text_color);
410 // Try to find an annotation that will fit
411 QString best_annotation;
414 for (const QString &a : annotations) {
415 const int w = p.boundingRect(QRectF(), 0, a).width();
416 if (w <= rect.width() && w > best_width)
417 best_annotation = a, best_width = w;
420 if (best_annotation.isEmpty())
421 best_annotation = annotations.back();
423 // If not ellide the last in the list
424 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
425 best_annotation, Qt::ElideRight, rect.width()));
428 void DecodeTrace::draw_error(QPainter &p, const QString &message,
431 const int y = get_y();
433 p.setPen(ErrorBgColour.darker());
434 p.setBrush(ErrorBgColour);
436 const QRectF bounding_rect =
437 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
438 const QRectF text_rect = p.boundingRect(bounding_rect,
439 Qt::AlignCenter, message);
440 const float r = text_rect.height() / 4;
442 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
445 p.setPen(get_text_colour());
446 p.drawText(text_rect, message);
449 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
452 using namespace pv::data;
453 using pv::data::decode::Decoder;
455 assert(_decoder_stack);
457 shared_ptr<Logic> data;
458 shared_ptr<LogicSignal> logic_signal;
460 const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
462 // We get the logic data of the first channel in the list.
463 // This works because we are currently assuming all
464 // LogicSignals have the same data/snapshot
465 for (const shared_ptr<Decoder> &dec : stack)
466 if (dec && !dec->channels().empty() &&
467 ((logic_signal = (*dec->channels().begin()).second)) &&
468 ((data = logic_signal->logic_data())))
471 if (!data || data->get_snapshots().empty())
474 const shared_ptr<LogicSnapshot> snapshot =
475 data->get_snapshots().front();
477 const int64_t sample_count = (int64_t)snapshot->get_sample_count();
478 if (sample_count == 0)
481 const int64_t samples_decoded = _decoder_stack->samples_decoded();
482 if (sample_count == samples_decoded)
485 const int y = get_y();
487 const double samples_per_pixel = get_samples_per_pixel();
488 const double pixels_offset = get_pixels_offset();
490 const double start = max(samples_decoded /
491 samples_per_pixel - pixels_offset, left - 1.0);
492 const double end = min(sample_count / samples_per_pixel -
493 pixels_offset, right + 1.0);
494 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
496 p.setPen(QPen(Qt::NoPen));
497 p.setBrush(Qt::white);
498 p.drawRect(no_decode_rect);
500 p.setPen(NoDecodeColour);
501 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
502 p.drawRect(no_decode_rect);
505 double DecodeTrace::get_pixels_offset() const
508 assert(_decoder_stack);
510 const double scale = _view->scale();
513 return (_view->offset() - _decoder_stack->get_start_time()) / scale;
516 double DecodeTrace::get_samples_per_pixel() const
519 assert(_decoder_stack);
521 const double scale = _view->scale();
524 double samplerate = _decoder_stack->samplerate();
526 // Show sample rate as 1Hz when it is unknown
527 if (samplerate == 0.0)
530 return samplerate * scale;
533 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(int x_start, int x_end) const
536 assert(_decoder_stack);
538 const double samples_per_pixel = get_samples_per_pixel();
539 const double pixels_offset = get_pixels_offset();
543 start = (uint64_t)max((x_start + pixels_offset) * samples_per_pixel, 0.0);
544 end = (uint64_t)max((x_end + pixels_offset) * samples_per_pixel, 0.0);
546 return make_pair(start, end);
549 void DecodeTrace::hover_point_changed()
553 void DecodeTrace::create_decoder_form(int index,
554 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
560 const srd_decoder *const decoder = dec->decoder();
563 pv::widgets::DecoderGroupBox *const group =
564 new pv::widgets::DecoderGroupBox(
565 QString::fromUtf8(decoder->name));
566 group->set_decoder_visible(dec->shown());
568 _delete_mapper.setMapping(group, index);
569 connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
571 _show_hide_mapper.setMapping(group, index);
572 connect(group, SIGNAL(show_hide_decoder()),
573 &_show_hide_mapper, SLOT(map()));
575 QFormLayout *const decoder_form = new QFormLayout;
576 group->add_layout(decoder_form);
578 // Add the mandatory channels
579 for(l = decoder->channels; l; l = l->next) {
580 const struct srd_channel *const pdch =
581 (struct srd_channel *)l->data;
582 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
583 connect(combo, SIGNAL(currentIndexChanged(int)),
584 this, SLOT(on_channel_selected(int)));
585 decoder_form->addRow(tr("<b>%1</b> (%2) *")
586 .arg(QString::fromUtf8(pdch->name))
587 .arg(QString::fromUtf8(pdch->desc)), combo);
589 const ChannelSelector s = {combo, dec, pdch};
590 _channel_selectors.push_back(s);
593 // Add the optional channels
594 for(l = decoder->opt_channels; l; l = l->next) {
595 const struct srd_channel *const pdch =
596 (struct srd_channel *)l->data;
597 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
598 connect(combo, SIGNAL(currentIndexChanged(int)),
599 this, SLOT(on_channel_selected(int)));
600 decoder_form->addRow(tr("<b>%1</b> (%2)")
601 .arg(QString::fromUtf8(pdch->name))
602 .arg(QString::fromUtf8(pdch->desc)), combo);
604 const ChannelSelector s = {combo, dec, pdch};
605 _channel_selectors.push_back(s);
609 shared_ptr<prop::binding::DecoderOptions> binding(
610 new prop::binding::DecoderOptions(_decoder_stack, dec));
611 binding->add_properties_to_form(decoder_form, true);
613 _bindings.push_back(binding);
616 _decoder_forms.push_back(group);
619 QComboBox* DecodeTrace::create_channel_selector(
620 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
621 const srd_channel *const pdch)
625 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
627 assert(_decoder_stack);
628 const auto channel_iter = dec->channels().find(pdch);
630 QComboBox *selector = new QComboBox(parent);
632 selector->addItem("-", qVariantFromValue((void*)NULL));
634 if (channel_iter == dec->channels().end())
635 selector->setCurrentIndex(0);
637 for(size_t i = 0; i < sigs.size(); i++) {
638 const shared_ptr<view::Signal> s(sigs[i]);
641 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
643 selector->addItem(s->get_name(),
644 qVariantFromValue((void*)s.get()));
645 if ((*channel_iter).second == s)
646 selector->setCurrentIndex(i + 1);
653 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
657 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
658 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
660 for (const ChannelSelector &s : _channel_selectors)
662 if(s._decoder != dec)
665 const LogicSignal *const selection =
666 (LogicSignal*)s._combo->itemData(
667 s._combo->currentIndex()).value<void*>();
669 for (shared_ptr<Signal> sig : sigs)
670 if(sig.get() == selection) {
671 channel_map[s._pdch] =
672 dynamic_pointer_cast<LogicSignal>(sig);
677 dec->set_channels(channel_map);
680 void DecodeTrace::commit_channels()
682 assert(_decoder_stack);
683 for (shared_ptr<data::decode::Decoder> dec : _decoder_stack->stack())
684 commit_decoder_channels(dec);
686 _decoder_stack->begin_decode();
689 void DecodeTrace::on_new_decode_data()
692 _view->update_viewport();
695 void DecodeTrace::delete_pressed()
700 void DecodeTrace::on_delete()
702 _session.remove_decode_signal(this);
705 void DecodeTrace::on_channel_selected(int)
710 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
713 assert(_decoder_stack);
714 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
715 new data::decode::Decoder(decoder)));
716 _decoder_stack->begin_decode();
721 void DecodeTrace::on_delete_decoder(int index)
723 _decoder_stack->remove(index);
728 _decoder_stack->begin_decode();
731 void DecodeTrace::on_show_hide_decoder(int index)
733 using pv::data::decode::Decoder;
735 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
737 // Find the decoder in the stack
738 auto iter = stack.cbegin();
739 for(int i = 0; i < index; i++, iter++)
740 assert(iter != stack.end());
742 shared_ptr<Decoder> dec = *iter;
745 const bool show = !dec->shown();
748 assert(index < (int)_decoder_forms.size());
749 _decoder_forms[index]->set_decoder_visible(show);
751 _view->update_viewport();