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::create_decoder_form(int index,
550 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
556 const srd_decoder *const decoder = dec->decoder();
559 pv::widgets::DecoderGroupBox *const group =
560 new pv::widgets::DecoderGroupBox(
561 QString::fromUtf8(decoder->name));
562 group->set_decoder_visible(dec->shown());
564 _delete_mapper.setMapping(group, index);
565 connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
567 _show_hide_mapper.setMapping(group, index);
568 connect(group, SIGNAL(show_hide_decoder()),
569 &_show_hide_mapper, SLOT(map()));
571 QFormLayout *const decoder_form = new QFormLayout;
572 group->add_layout(decoder_form);
574 // Add the mandatory channels
575 for(l = decoder->channels; l; l = l->next) {
576 const struct srd_channel *const pdch =
577 (struct srd_channel *)l->data;
578 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
579 connect(combo, SIGNAL(currentIndexChanged(int)),
580 this, SLOT(on_channel_selected(int)));
581 decoder_form->addRow(tr("<b>%1</b> (%2) *")
582 .arg(QString::fromUtf8(pdch->name))
583 .arg(QString::fromUtf8(pdch->desc)), combo);
585 const ChannelSelector s = {combo, dec, pdch};
586 _channel_selectors.push_back(s);
589 // Add the optional channels
590 for(l = decoder->opt_channels; l; l = l->next) {
591 const struct srd_channel *const pdch =
592 (struct srd_channel *)l->data;
593 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
594 connect(combo, SIGNAL(currentIndexChanged(int)),
595 this, SLOT(on_channel_selected(int)));
596 decoder_form->addRow(tr("<b>%1</b> (%2)")
597 .arg(QString::fromUtf8(pdch->name))
598 .arg(QString::fromUtf8(pdch->desc)), combo);
600 const ChannelSelector s = {combo, dec, pdch};
601 _channel_selectors.push_back(s);
605 shared_ptr<prop::binding::DecoderOptions> binding(
606 new prop::binding::DecoderOptions(_decoder_stack, dec));
607 binding->add_properties_to_form(decoder_form, true);
609 _bindings.push_back(binding);
612 _decoder_forms.push_back(group);
615 QComboBox* DecodeTrace::create_channel_selector(
616 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
617 const srd_channel *const pdch)
621 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
623 assert(_decoder_stack);
624 const auto channel_iter = dec->channels().find(pdch);
626 QComboBox *selector = new QComboBox(parent);
628 selector->addItem("-", qVariantFromValue((void*)NULL));
630 if (channel_iter == dec->channels().end())
631 selector->setCurrentIndex(0);
633 for(size_t i = 0; i < sigs.size(); i++) {
634 const shared_ptr<view::Signal> s(sigs[i]);
637 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
639 selector->addItem(s->get_name(),
640 qVariantFromValue((void*)s.get()));
641 if ((*channel_iter).second == s)
642 selector->setCurrentIndex(i + 1);
649 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
653 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
654 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
656 for (const ChannelSelector &s : _channel_selectors)
658 if(s._decoder != dec)
661 const LogicSignal *const selection =
662 (LogicSignal*)s._combo->itemData(
663 s._combo->currentIndex()).value<void*>();
665 for (shared_ptr<Signal> sig : sigs)
666 if(sig.get() == selection) {
667 channel_map[s._pdch] =
668 dynamic_pointer_cast<LogicSignal>(sig);
673 dec->set_channels(channel_map);
676 void DecodeTrace::commit_channels()
678 assert(_decoder_stack);
679 for (shared_ptr<data::decode::Decoder> dec : _decoder_stack->stack())
680 commit_decoder_channels(dec);
682 _decoder_stack->begin_decode();
685 void DecodeTrace::on_new_decode_data()
688 _view->update_viewport();
691 void DecodeTrace::delete_pressed()
696 void DecodeTrace::on_delete()
698 _session.remove_decode_signal(this);
701 void DecodeTrace::on_channel_selected(int)
706 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
709 assert(_decoder_stack);
710 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
711 new data::decode::Decoder(decoder)));
712 _decoder_stack->begin_decode();
717 void DecodeTrace::on_delete_decoder(int index)
719 _decoder_stack->remove(index);
724 _decoder_stack->begin_decode();
727 void DecodeTrace::on_show_hide_decoder(int index)
729 using pv::data::decode::Decoder;
731 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
733 // Find the decoder in the stack
734 auto iter = stack.cbegin();
735 for(int i = 0; i < index; i++, iter++)
736 assert(iter != stack.end());
738 shared_ptr<Decoder> dec = *iter;
741 const bool show = !dec->shown();
744 assert(index < (int)_decoder_forms.size());
745 _decoder_forms[index]->set_decoder_visible(show);
747 _view->update_viewport();