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/foreach.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 boost::dynamic_pointer_cast;
51 using boost::shared_ptr;
61 const QColor DecodeTrace::DecodeColours[4] = {
62 QColor(0xEF, 0x29, 0x29), // Red
63 QColor(0xFC, 0xE9, 0x4F), // Yellow
64 QColor(0x8A, 0xE2, 0x34), // Green
65 QColor(0x72, 0x9F, 0xCF) // Blue
68 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
69 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
71 const double DecodeTrace::EndCapWidth = 5;
72 const int DecodeTrace::DrawPadding = 100;
74 const QColor DecodeTrace::Colours[16] = {
75 QColor(0xEF, 0x29, 0x29),
76 QColor(0xF6, 0x6A, 0x32),
77 QColor(0xFC, 0xAE, 0x3E),
78 QColor(0xFB, 0xCA, 0x47),
79 QColor(0xFC, 0xE9, 0x4F),
80 QColor(0xCD, 0xF0, 0x40),
81 QColor(0x8A, 0xE2, 0x34),
82 QColor(0x4E, 0xDC, 0x44),
83 QColor(0x55, 0xD7, 0x95),
84 QColor(0x64, 0xD1, 0xD2),
85 QColor(0x72, 0x9F, 0xCF),
86 QColor(0xD4, 0x76, 0xC4),
87 QColor(0x9D, 0x79, 0xB9),
88 QColor(0xAD, 0x7F, 0xA8),
89 QColor(0xC2, 0x62, 0x9B),
90 QColor(0xD7, 0x47, 0x6F)
93 const QColor DecodeTrace::OutlineColours[16] = {
94 QColor(0x77, 0x14, 0x14),
95 QColor(0x7B, 0x35, 0x19),
96 QColor(0x7E, 0x57, 0x1F),
97 QColor(0x7D, 0x65, 0x23),
98 QColor(0x7E, 0x74, 0x27),
99 QColor(0x66, 0x78, 0x20),
100 QColor(0x45, 0x71, 0x1A),
101 QColor(0x27, 0x6E, 0x22),
102 QColor(0x2A, 0x6B, 0x4A),
103 QColor(0x32, 0x68, 0x69),
104 QColor(0x39, 0x4F, 0x67),
105 QColor(0x6A, 0x3B, 0x62),
106 QColor(0x4E, 0x3C, 0x5C),
107 QColor(0x56, 0x3F, 0x54),
108 QColor(0x61, 0x31, 0x4D),
109 QColor(0x6B, 0x23, 0x37)
112 DecodeTrace::DecodeTrace(pv::SigSession &session,
113 boost::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
114 Trace(session, QString::fromUtf8(
115 decoder_stack->stack().front()->decoder()->name)),
116 _decoder_stack(decoder_stack),
117 _delete_mapper(this),
118 _show_hide_mapper(this)
120 assert(_decoder_stack);
122 _colour = DecodeColours[index % countof(DecodeColours)];
124 connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
125 this, SLOT(on_new_decode_data()));
126 connect(&_delete_mapper, SIGNAL(mapped(int)),
127 this, SLOT(on_delete_decoder(int)));
128 connect(&_show_hide_mapper, SIGNAL(mapped(int)),
129 this, SLOT(on_show_hide_decoder(int)));
132 bool DecodeTrace::enabled() const
137 const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
139 return _decoder_stack;
142 void DecodeTrace::set_view(pv::view::View *view)
145 Trace::set_view(view);
148 void DecodeTrace::paint_back(QPainter &p, int left, int right)
150 Trace::paint_back(p, left, right);
151 paint_axis(p, get_y(), left, right);
154 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
156 using namespace pv::data::decode;
158 const double scale = _view->scale();
161 double samplerate = _decoder_stack->samplerate();
163 // Show sample rate as 1Hz when it is unknown
164 if (samplerate == 0.0)
167 const double pixels_offset = (_view->offset() -
168 _decoder_stack->get_start_time()) / scale;
169 const double samples_per_pixel = samplerate * scale;
171 const uint64_t start_sample = (uint64_t)max((left + pixels_offset) *
172 samples_per_pixel, 0.0);
173 const uint64_t end_sample = (uint64_t)max((right + pixels_offset) *
174 samples_per_pixel, 0.0);
176 QFontMetrics m(QApplication::font());
177 const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
178 const int annotation_height = (text_height * 5) / 4;
179 const int row_height = (text_height * 6) / 4;
181 assert(_decoder_stack);
182 const QString err = _decoder_stack->error_message();
185 draw_unresolved_period(p, annotation_height, left, right,
186 samples_per_pixel, pixels_offset);
187 draw_error(p, err, left, right);
191 // Iterate through the rows
195 assert(_decoder_stack);
197 const vector<Row> rows(_decoder_stack->get_visible_rows());
198 for (size_t i = 0; i < rows.size(); i++)
200 const Row &row = rows[i];
202 size_t base_colour = 0x13579BDF;
203 boost::hash_combine(base_colour, this);
204 boost::hash_combine(base_colour, row.decoder());
205 boost::hash_combine(base_colour, row.row());
208 vector<Annotation> annotations;
209 _decoder_stack->get_annotation_subset(annotations, row,
210 start_sample, end_sample);
211 if (!annotations.empty()) {
212 BOOST_FOREACH(const Annotation &a, annotations)
213 draw_annotation(a, p, get_text_colour(),
214 annotation_height, left, right,
215 samples_per_pixel, pixels_offset, y,
222 draw_unresolved_period(p, annotation_height, left, right,
223 samples_per_pixel, pixels_offset);
226 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
228 using pv::data::decode::Decoder;
232 assert(_decoder_stack);
234 // Add the standard options
235 Trace::populate_popup_form(parent, form);
237 // Add the decoder options
239 _probe_selectors.clear();
240 _decoder_forms.clear();
242 const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
246 QLabel *const l = new QLabel(
247 tr("<p><i>No decoders in the stack</i></p>"));
248 l->setAlignment(Qt::AlignCenter);
253 list< shared_ptr<Decoder> >::const_iterator iter =
255 for (int i = 0; i < (int)stack.size(); i++, iter++) {
256 shared_ptr<Decoder> dec(*iter);
257 create_decoder_form(i, dec, parent, form);
260 form->addRow(new QLabel(
261 tr("<i>* Required Probes</i>"), parent));
264 // Add stacking button
265 pv::widgets::DecoderMenu *const decoder_menu =
266 new pv::widgets::DecoderMenu(parent);
267 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
268 this, SLOT(on_stack_decoder(srd_decoder*)));
270 QPushButton *const stack_button =
271 new QPushButton(tr("Stack Decoder"), parent);
272 stack_button->setMenu(decoder_menu);
274 QHBoxLayout *stack_button_box = new QHBoxLayout;
275 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
276 form->addRow(stack_button_box);
279 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
281 QMenu *const menu = Trace::create_context_menu(parent);
283 menu->addSeparator();
285 QAction *const del = new QAction(tr("Delete"), this);
286 del->setShortcuts(QKeySequence::Delete);
287 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
288 menu->addAction(del);
293 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
294 QPainter &p, QColor text_color, int h, int left, int right,
295 double samples_per_pixel, double pixels_offset, int y,
296 size_t base_colour) const
298 const double start = a.start_sample() / samples_per_pixel -
300 const double end = a.end_sample() / samples_per_pixel -
303 const size_t colour = (base_colour + a.format()) % countof(Colours);
304 const QColor &fill = Colours[colour];
305 const QColor &outline = OutlineColours[colour];
307 if (start > right + DrawPadding || end < left - DrawPadding)
310 if (a.start_sample() == a.end_sample())
311 draw_instant(a, p, fill, outline, text_color, h,
314 draw_range(a, p, fill, outline, text_color, h,
318 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
319 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
321 const QString text = a.annotations().empty() ?
322 QString() : a.annotations().back();
323 const double w = min(p.boundingRect(QRectF(), 0, text).width(),
325 const QRectF rect(x - w / 2, y - h / 2, w, h);
329 p.drawRoundedRect(rect, h / 2, h / 2);
331 p.setPen(text_color);
332 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
335 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
336 QColor fill, QColor outline, QColor text_color, int h, double start,
337 double end, int y) const
339 const double top = y + .5 - h / 2;
340 const double bottom = y + .5 + h / 2;
341 const vector<QString> annotations = a.annotations();
346 // If the two ends are within 1 pixel, draw a vertical line
347 if (start + 1.0 > end)
349 p.drawLine(QPointF(start, top), QPointF(start, bottom));
353 const double cap_width = min((end - start) / 4, EndCapWidth);
356 QPointF(start, y + .5f),
357 QPointF(start + cap_width, top),
358 QPointF(end - cap_width, top),
359 QPointF(end, y + .5f),
360 QPointF(end - cap_width, bottom),
361 QPointF(start + cap_width, bottom)
364 p.drawConvexPolygon(pts, countof(pts));
366 if (annotations.empty())
369 QRectF rect(start + cap_width, y - h / 2,
370 end - start - cap_width * 2, h);
371 p.setPen(text_color);
373 // Try to find an annotation that will fit
374 QString best_annotation;
377 BOOST_FOREACH(const QString &a, annotations) {
378 const int w = p.boundingRect(QRectF(), 0, a).width();
379 if (w <= rect.width() && w > best_width)
380 best_annotation = a, best_width = w;
383 if (best_annotation.isEmpty())
384 best_annotation = annotations.back();
386 // If not ellide the last in the list
387 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
388 best_annotation, Qt::ElideRight, rect.width()));
391 void DecodeTrace::draw_error(QPainter &p, const QString &message,
394 const int y = get_y();
396 p.setPen(ErrorBgColour.darker());
397 p.setBrush(ErrorBgColour);
399 const QRectF bounding_rect =
400 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
401 const QRectF text_rect = p.boundingRect(bounding_rect,
402 Qt::AlignCenter, message);
403 const float r = text_rect.height() / 4;
405 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
408 p.setPen(get_text_colour());
409 p.drawText(text_rect, message);
412 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
413 int right, double samples_per_pixel, double pixels_offset)
415 using namespace pv::data;
416 using pv::data::decode::Decoder;
418 assert(_decoder_stack);
420 shared_ptr<Logic> data;
421 shared_ptr<LogicSignal> logic_signal;
423 const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
425 // We get the logic data of the first probe in the list.
426 // This works because we are currently assuming all
427 // LogicSignals have the same data/snapshot
428 BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
429 if (dec && !dec->probes().empty() &&
430 ((logic_signal = (*dec->probes().begin()).second)) &&
431 ((data = logic_signal->logic_data())))
434 if (!data || data->get_snapshots().empty())
437 const shared_ptr<LogicSnapshot> snapshot =
438 data->get_snapshots().front();
440 const int64_t sample_count = (int64_t)snapshot->get_sample_count();
441 if (sample_count == 0)
444 const int64_t samples_decoded = _decoder_stack->samples_decoded();
445 if (sample_count == samples_decoded)
448 const int y = get_y();
449 const double start = max(samples_decoded /
450 samples_per_pixel - pixels_offset, left - 1.0);
451 const double end = min(sample_count / samples_per_pixel -
452 pixels_offset, right + 1.0);
453 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
455 p.setPen(QPen(Qt::NoPen));
456 p.setBrush(Qt::white);
457 p.drawRect(no_decode_rect);
459 p.setPen(NoDecodeColour);
460 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
461 p.drawRect(no_decode_rect);
464 void DecodeTrace::create_decoder_form(int index,
465 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
471 const srd_decoder *const decoder = dec->decoder();
474 pv::widgets::DecoderGroupBox *const group =
475 new pv::widgets::DecoderGroupBox(
476 QString::fromUtf8(decoder->name));
477 group->set_decoder_visible(dec->shown());
479 _delete_mapper.setMapping(group, index);
480 connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
482 _show_hide_mapper.setMapping(group, index);
483 connect(group, SIGNAL(show_hide_decoder()),
484 &_show_hide_mapper, SLOT(map()));
486 QFormLayout *const decoder_form = new QFormLayout;
487 group->add_layout(decoder_form);
489 // Add the mandatory probes
490 for(probe = decoder->probes; probe; probe = probe->next) {
491 const struct srd_probe *const p =
492 (struct srd_probe *)probe->data;
493 QComboBox *const combo = create_probe_selector(parent, dec, p);
494 connect(combo, SIGNAL(currentIndexChanged(int)),
495 this, SLOT(on_probe_selected(int)));
496 decoder_form->addRow(tr("<b>%1</b> (%2) *")
497 .arg(QString::fromUtf8(p->name))
498 .arg(QString::fromUtf8(p->desc)), combo);
500 const ProbeSelector s = {combo, dec, p};
501 _probe_selectors.push_back(s);
504 // Add the optional probes
505 for(probe = decoder->opt_probes; probe; probe = probe->next) {
506 const struct srd_probe *const p =
507 (struct srd_probe *)probe->data;
508 QComboBox *const combo = create_probe_selector(parent, dec, p);
509 connect(combo, SIGNAL(currentIndexChanged(int)),
510 this, SLOT(on_probe_selected(int)));
511 decoder_form->addRow(tr("<b>%1</b> (%2)")
512 .arg(QString::fromUtf8(p->name))
513 .arg(QString::fromUtf8(p->desc)), combo);
515 const ProbeSelector s = {combo, dec, p};
516 _probe_selectors.push_back(s);
520 shared_ptr<prop::binding::DecoderOptions> binding(
521 new prop::binding::DecoderOptions(_decoder_stack, dec));
522 binding->add_properties_to_form(decoder_form, true);
524 _bindings.push_back(binding);
527 _decoder_forms.push_back(group);
530 QComboBox* DecodeTrace::create_probe_selector(
531 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
532 const srd_probe *const probe)
536 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
538 assert(_decoder_stack);
539 const map<const srd_probe*,
540 shared_ptr<LogicSignal> >::const_iterator probe_iter =
541 dec->probes().find(probe);
543 QComboBox *selector = new QComboBox(parent);
545 selector->addItem("-", qVariantFromValue((void*)NULL));
547 if (probe_iter == dec->probes().end())
548 selector->setCurrentIndex(0);
550 for(size_t i = 0; i < sigs.size(); i++) {
551 const shared_ptr<view::Signal> s(sigs[i]);
554 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
556 selector->addItem(s->get_name(),
557 qVariantFromValue((void*)s.get()));
558 if ((*probe_iter).second == s)
559 selector->setCurrentIndex(i + 1);
566 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
570 map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
571 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
573 BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
575 if(s._decoder != dec)
578 const LogicSignal *const selection =
579 (LogicSignal*)s._combo->itemData(
580 s._combo->currentIndex()).value<void*>();
582 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
583 if(sig.get() == selection) {
584 probe_map[s._probe] =
585 dynamic_pointer_cast<LogicSignal>(sig);
590 dec->set_probes(probe_map);
593 void DecodeTrace::commit_probes()
595 assert(_decoder_stack);
596 BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
597 _decoder_stack->stack())
598 commit_decoder_probes(dec);
600 _decoder_stack->begin_decode();
603 void DecodeTrace::on_new_decode_data()
606 _view->update_viewport();
609 void DecodeTrace::delete_pressed()
614 void DecodeTrace::on_delete()
616 _session.remove_decode_signal(this);
619 void DecodeTrace::on_probe_selected(int)
624 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
627 assert(_decoder_stack);
628 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
629 new data::decode::Decoder(decoder)));
630 _decoder_stack->begin_decode();
635 void DecodeTrace::on_delete_decoder(int index)
637 _decoder_stack->remove(index);
642 _decoder_stack->begin_decode();
645 void DecodeTrace::on_show_hide_decoder(int index)
647 using pv::data::decode::Decoder;
649 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
651 // Find the decoder in the stack
652 list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
653 for(int i = 0; i < index; i++, iter++)
654 assert(iter != stack.end());
656 shared_ptr<Decoder> dec = *iter;
659 const bool show = !dec->shown();
662 assert(index < (int)_decoder_forms.size());
663 _decoder_forms[index]->set_decoder_visible(show);
665 _view->update_viewport();