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(QString::fromUtf8(
115 decoder_stack->stack().front()->decoder()->name)),
117 _decoder_stack(decoder_stack),
118 _delete_mapper(this),
119 _show_hide_mapper(this)
121 assert(_decoder_stack);
123 _colour = DecodeColours[index % countof(DecodeColours)];
125 connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
126 this, SLOT(on_new_decode_data()));
127 connect(&_delete_mapper, SIGNAL(mapped(int)),
128 this, SLOT(on_delete_decoder(int)));
129 connect(&_show_hide_mapper, SIGNAL(mapped(int)),
130 this, SLOT(on_show_hide_decoder(int)));
133 bool DecodeTrace::enabled() const
138 const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
140 return _decoder_stack;
143 void DecodeTrace::set_view(pv::view::View *view)
146 Trace::set_view(view);
149 void DecodeTrace::paint_back(QPainter &p, int left, int right)
151 Trace::paint_back(p, left, right);
152 paint_axis(p, get_y(), left, right);
155 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
157 using namespace pv::data::decode;
159 const double scale = _view->scale();
162 double samplerate = _decoder_stack->samplerate();
164 // Show sample rate as 1Hz when it is unknown
165 if (samplerate == 0.0)
168 const double pixels_offset = (_view->offset() -
169 _decoder_stack->get_start_time()) / scale;
170 const double samples_per_pixel = samplerate * scale;
172 const uint64_t start_sample = (uint64_t)max((left + pixels_offset) *
173 samples_per_pixel, 0.0);
174 const uint64_t end_sample = (uint64_t)max((right + pixels_offset) *
175 samples_per_pixel, 0.0);
177 QFontMetrics m(QApplication::font());
178 const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
179 const int annotation_height = (text_height * 5) / 4;
180 const int row_height = (text_height * 6) / 4;
182 assert(_decoder_stack);
183 const QString err = _decoder_stack->error_message();
186 draw_unresolved_period(p, annotation_height, left, right,
187 samples_per_pixel, pixels_offset);
188 draw_error(p, err, left, right);
192 // Iterate through the rows
196 assert(_decoder_stack);
198 const vector<Row> rows(_decoder_stack->get_visible_rows());
199 for (size_t i = 0; i < rows.size(); i++)
201 const Row &row = rows[i];
203 size_t base_colour = 0x13579BDF;
204 boost::hash_combine(base_colour, this);
205 boost::hash_combine(base_colour, row.decoder());
206 boost::hash_combine(base_colour, row.row());
209 vector<Annotation> annotations;
210 _decoder_stack->get_annotation_subset(annotations, row,
211 start_sample, end_sample);
212 if (!annotations.empty()) {
213 BOOST_FOREACH(const Annotation &a, annotations)
214 draw_annotation(a, p, get_text_colour(),
215 annotation_height, left, right,
216 samples_per_pixel, pixels_offset, y,
223 draw_unresolved_period(p, annotation_height, left, right,
224 samples_per_pixel, pixels_offset);
227 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
229 using pv::data::decode::Decoder;
233 assert(_decoder_stack);
235 // Add the standard options
236 Trace::populate_popup_form(parent, form);
238 // Add the decoder options
240 _probe_selectors.clear();
241 _decoder_forms.clear();
243 const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
247 QLabel *const l = new QLabel(
248 tr("<p><i>No decoders in the stack</i></p>"));
249 l->setAlignment(Qt::AlignCenter);
254 list< shared_ptr<Decoder> >::const_iterator iter =
256 for (int i = 0; i < (int)stack.size(); i++, iter++) {
257 shared_ptr<Decoder> dec(*iter);
258 create_decoder_form(i, dec, parent, form);
261 form->addRow(new QLabel(
262 tr("<i>* Required Probes</i>"), parent));
265 // Add stacking button
266 pv::widgets::DecoderMenu *const decoder_menu =
267 new pv::widgets::DecoderMenu(parent);
268 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
269 this, SLOT(on_stack_decoder(srd_decoder*)));
271 QPushButton *const stack_button =
272 new QPushButton(tr("Stack Decoder"), parent);
273 stack_button->setMenu(decoder_menu);
275 QHBoxLayout *stack_button_box = new QHBoxLayout;
276 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
277 form->addRow(stack_button_box);
280 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
282 QMenu *const menu = Trace::create_context_menu(parent);
284 menu->addSeparator();
286 QAction *const del = new QAction(tr("Delete"), this);
287 del->setShortcuts(QKeySequence::Delete);
288 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
289 menu->addAction(del);
294 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
295 QPainter &p, QColor text_color, int h, int left, int right,
296 double samples_per_pixel, double pixels_offset, int y,
297 size_t base_colour) const
299 const double start = a.start_sample() / samples_per_pixel -
301 const double end = a.end_sample() / samples_per_pixel -
304 const size_t colour = (base_colour + a.format()) % countof(Colours);
305 const QColor &fill = Colours[colour];
306 const QColor &outline = OutlineColours[colour];
308 if (start > right + DrawPadding || end < left - DrawPadding)
311 if (a.start_sample() == a.end_sample())
312 draw_instant(a, p, fill, outline, text_color, h,
315 draw_range(a, p, fill, outline, text_color, h,
319 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
320 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
322 const QString text = a.annotations().empty() ?
323 QString() : a.annotations().back();
324 const double w = min(p.boundingRect(QRectF(), 0, text).width(),
326 const QRectF rect(x - w / 2, y - h / 2, w, h);
330 p.drawRoundedRect(rect, h / 2, h / 2);
332 p.setPen(text_color);
333 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
336 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
337 QColor fill, QColor outline, QColor text_color, int h, double start,
338 double end, int y) const
340 const double top = y + .5 - h / 2;
341 const double bottom = y + .5 + h / 2;
342 const vector<QString> annotations = a.annotations();
347 // If the two ends are within 1 pixel, draw a vertical line
348 if (start + 1.0 > end)
350 p.drawLine(QPointF(start, top), QPointF(start, bottom));
354 const double cap_width = min((end - start) / 4, EndCapWidth);
357 QPointF(start, y + .5f),
358 QPointF(start + cap_width, top),
359 QPointF(end - cap_width, top),
360 QPointF(end, y + .5f),
361 QPointF(end - cap_width, bottom),
362 QPointF(start + cap_width, bottom)
365 p.drawConvexPolygon(pts, countof(pts));
367 if (annotations.empty())
370 QRectF rect(start + cap_width, y - h / 2,
371 end - start - cap_width * 2, h);
372 p.setPen(text_color);
374 // Try to find an annotation that will fit
375 QString best_annotation;
378 BOOST_FOREACH(const QString &a, annotations) {
379 const int w = p.boundingRect(QRectF(), 0, a).width();
380 if (w <= rect.width() && w > best_width)
381 best_annotation = a, best_width = w;
384 if (best_annotation.isEmpty())
385 best_annotation = annotations.back();
387 // If not ellide the last in the list
388 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
389 best_annotation, Qt::ElideRight, rect.width()));
392 void DecodeTrace::draw_error(QPainter &p, const QString &message,
395 const int y = get_y();
397 p.setPen(ErrorBgColour.darker());
398 p.setBrush(ErrorBgColour);
400 const QRectF bounding_rect =
401 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
402 const QRectF text_rect = p.boundingRect(bounding_rect,
403 Qt::AlignCenter, message);
404 const float r = text_rect.height() / 4;
406 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
409 p.setPen(get_text_colour());
410 p.drawText(text_rect, message);
413 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
414 int right, double samples_per_pixel, double pixels_offset)
416 using namespace pv::data;
417 using pv::data::decode::Decoder;
419 assert(_decoder_stack);
421 shared_ptr<Logic> data;
422 shared_ptr<LogicSignal> logic_signal;
424 const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
426 // We get the logic data of the first probe in the list.
427 // This works because we are currently assuming all
428 // LogicSignals have the same data/snapshot
429 BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
430 if (dec && !dec->probes().empty() &&
431 ((logic_signal = (*dec->probes().begin()).second)) &&
432 ((data = logic_signal->logic_data())))
435 if (!data || data->get_snapshots().empty())
438 const shared_ptr<LogicSnapshot> snapshot =
439 data->get_snapshots().front();
441 const int64_t sample_count = (int64_t)snapshot->get_sample_count();
442 if (sample_count == 0)
445 const int64_t samples_decoded = _decoder_stack->samples_decoded();
446 if (sample_count == samples_decoded)
449 const int y = get_y();
450 const double start = max(samples_decoded /
451 samples_per_pixel - pixels_offset, left - 1.0);
452 const double end = min(sample_count / samples_per_pixel -
453 pixels_offset, right + 1.0);
454 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
456 p.setPen(QPen(Qt::NoPen));
457 p.setBrush(Qt::white);
458 p.drawRect(no_decode_rect);
460 p.setPen(NoDecodeColour);
461 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
462 p.drawRect(no_decode_rect);
465 void DecodeTrace::create_decoder_form(int index,
466 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
472 const srd_decoder *const decoder = dec->decoder();
475 pv::widgets::DecoderGroupBox *const group =
476 new pv::widgets::DecoderGroupBox(
477 QString::fromUtf8(decoder->name));
478 group->set_decoder_visible(dec->shown());
480 _delete_mapper.setMapping(group, index);
481 connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
483 _show_hide_mapper.setMapping(group, index);
484 connect(group, SIGNAL(show_hide_decoder()),
485 &_show_hide_mapper, SLOT(map()));
487 QFormLayout *const decoder_form = new QFormLayout;
488 group->add_layout(decoder_form);
490 // Add the mandatory probes
491 for(probe = decoder->probes; probe; probe = probe->next) {
492 const struct srd_probe *const p =
493 (struct srd_probe *)probe->data;
494 QComboBox *const combo = create_probe_selector(parent, dec, p);
495 connect(combo, SIGNAL(currentIndexChanged(int)),
496 this, SLOT(on_probe_selected(int)));
497 decoder_form->addRow(tr("<b>%1</b> (%2) *")
498 .arg(QString::fromUtf8(p->name))
499 .arg(QString::fromUtf8(p->desc)), combo);
501 const ProbeSelector s = {combo, dec, p};
502 _probe_selectors.push_back(s);
505 // Add the optional probes
506 for(probe = decoder->opt_probes; probe; probe = probe->next) {
507 const struct srd_probe *const p =
508 (struct srd_probe *)probe->data;
509 QComboBox *const combo = create_probe_selector(parent, dec, p);
510 connect(combo, SIGNAL(currentIndexChanged(int)),
511 this, SLOT(on_probe_selected(int)));
512 decoder_form->addRow(tr("<b>%1</b> (%2)")
513 .arg(QString::fromUtf8(p->name))
514 .arg(QString::fromUtf8(p->desc)), combo);
516 const ProbeSelector s = {combo, dec, p};
517 _probe_selectors.push_back(s);
521 shared_ptr<prop::binding::DecoderOptions> binding(
522 new prop::binding::DecoderOptions(_decoder_stack, dec));
523 binding->add_properties_to_form(decoder_form, true);
525 _bindings.push_back(binding);
528 _decoder_forms.push_back(group);
531 QComboBox* DecodeTrace::create_probe_selector(
532 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
533 const srd_probe *const probe)
537 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
539 assert(_decoder_stack);
540 const map<const srd_probe*,
541 shared_ptr<LogicSignal> >::const_iterator probe_iter =
542 dec->probes().find(probe);
544 QComboBox *selector = new QComboBox(parent);
546 selector->addItem("-", qVariantFromValue((void*)NULL));
548 if (probe_iter == dec->probes().end())
549 selector->setCurrentIndex(0);
551 for(size_t i = 0; i < sigs.size(); i++) {
552 const shared_ptr<view::Signal> s(sigs[i]);
555 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
557 selector->addItem(s->get_name(),
558 qVariantFromValue((void*)s.get()));
559 if ((*probe_iter).second == s)
560 selector->setCurrentIndex(i + 1);
567 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
571 map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
572 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
574 BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
576 if(s._decoder != dec)
579 const LogicSignal *const selection =
580 (LogicSignal*)s._combo->itemData(
581 s._combo->currentIndex()).value<void*>();
583 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
584 if(sig.get() == selection) {
585 probe_map[s._probe] =
586 dynamic_pointer_cast<LogicSignal>(sig);
591 dec->set_probes(probe_map);
594 void DecodeTrace::commit_probes()
596 assert(_decoder_stack);
597 BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
598 _decoder_stack->stack())
599 commit_decoder_probes(dec);
601 _decoder_stack->begin_decode();
604 void DecodeTrace::on_new_decode_data()
607 _view->update_viewport();
610 void DecodeTrace::delete_pressed()
615 void DecodeTrace::on_delete()
617 _session.remove_decode_signal(this);
620 void DecodeTrace::on_probe_selected(int)
625 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
628 assert(_decoder_stack);
629 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
630 new data::decode::Decoder(decoder)));
631 _decoder_stack->begin_decode();
636 void DecodeTrace::on_delete_decoder(int index)
638 _decoder_stack->remove(index);
643 _decoder_stack->begin_decode();
646 void DecodeTrace::on_show_hide_decoder(int index)
648 using pv::data::decode::Decoder;
650 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
652 // Find the decoder in the stack
653 list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
654 for(int i = 0; i < index; i++, iter++)
655 assert(iter != stack.end());
657 shared_ptr<Decoder> dec = *iter;
660 const bool show = !dec->shown();
663 assert(index < (int)_decoder_forms.size());
664 _decoder_forms[index]->set_decoder_visible(show);
666 _view->update_viewport();