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_error(p, err, left, right);
186 draw_unresolved_period(p, annotation_height, left, right,
187 samples_per_pixel, pixels_offset);
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(p->name).arg(p->desc), combo);
499 const ProbeSelector s = {combo, dec, p};
500 _probe_selectors.push_back(s);
503 // Add the optional probes
504 for(probe = decoder->opt_probes; probe; probe = probe->next) {
505 const struct srd_probe *const p =
506 (struct srd_probe *)probe->data;
507 QComboBox *const combo = create_probe_selector(parent, dec, p);
508 connect(combo, SIGNAL(currentIndexChanged(int)),
509 this, SLOT(on_probe_selected(int)));
510 decoder_form->addRow(tr("<b>%1</b> (%2)")
511 .arg(p->name).arg(p->desc), combo);
513 const ProbeSelector s = {combo, dec, p};
514 _probe_selectors.push_back(s);
518 shared_ptr<prop::binding::DecoderOptions> binding(
519 new prop::binding::DecoderOptions(_decoder_stack, dec));
520 binding->add_properties_to_form(decoder_form, true);
522 _bindings.push_back(binding);
525 _decoder_forms.push_back(group);
528 QComboBox* DecodeTrace::create_probe_selector(
529 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
530 const srd_probe *const probe)
534 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
536 assert(_decoder_stack);
537 const map<const srd_probe*,
538 shared_ptr<LogicSignal> >::const_iterator probe_iter =
539 dec->probes().find(probe);
541 QComboBox *selector = new QComboBox(parent);
543 selector->addItem("-", qVariantFromValue((void*)NULL));
545 if (probe_iter == dec->probes().end())
546 selector->setCurrentIndex(0);
548 for(size_t i = 0; i < sigs.size(); i++) {
549 const shared_ptr<view::Signal> s(sigs[i]);
552 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
554 selector->addItem(s->get_name(),
555 qVariantFromValue((void*)s.get()));
556 if ((*probe_iter).second == s)
557 selector->setCurrentIndex(i + 1);
564 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
568 map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
569 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
571 BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
573 if(s._decoder != dec)
576 const LogicSignal *const selection =
577 (LogicSignal*)s._combo->itemData(
578 s._combo->currentIndex()).value<void*>();
580 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
581 if(sig.get() == selection) {
582 probe_map[s._probe] =
583 dynamic_pointer_cast<LogicSignal>(sig);
588 dec->set_probes(probe_map);
591 void DecodeTrace::commit_probes()
593 assert(_decoder_stack);
594 BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
595 _decoder_stack->stack())
596 commit_decoder_probes(dec);
598 _decoder_stack->begin_decode();
601 void DecodeTrace::on_new_decode_data()
604 _view->update_viewport();
607 void DecodeTrace::delete_pressed()
612 void DecodeTrace::on_delete()
614 _session.remove_decode_signal(this);
617 void DecodeTrace::on_probe_selected(int)
622 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
625 assert(_decoder_stack);
626 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
627 new data::decode::Decoder(decoder)));
628 _decoder_stack->begin_decode();
633 void DecodeTrace::on_delete_decoder(int index)
635 _decoder_stack->remove(index);
640 _decoder_stack->begin_decode();
643 void DecodeTrace::on_show_hide_decoder(int index)
645 using pv::data::decode::Decoder;
647 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
649 // Find the decoder in the stack
650 list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
651 for(int i = 0; i < index; i++, iter++)
652 assert(iter != stack.end());
654 shared_ptr<Decoder> dec = *iter;
657 const bool show = !dec->shown();
660 assert(index < (int)_decoder_forms.size());
661 _decoder_forms[index]->set_decoder_visible(show);
663 _view->update_viewport();