]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
Removed SigSession from Trace
[pulseview.git] / pv / view / decodetrace.cpp
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 extern "C" {
22 #include <libsigrokdecode/libsigrokdecode.h>
23 }
24
25 #include <extdef.h>
26
27 #include <boost/foreach.hpp>
28
29 #include <QAction>
30 #include <QApplication>
31 #include <QComboBox>
32 #include <QFormLayout>
33 #include <QLabel>
34 #include <QMenu>
35 #include <QPushButton>
36
37 #include "decodetrace.h"
38
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>
49
50 using boost::dynamic_pointer_cast;
51 using boost::shared_ptr;
52 using std::list;
53 using std::max;
54 using std::map;
55 using std::min;
56 using std::vector;
57
58 namespace pv {
59 namespace view {
60
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
66 };
67
68 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
69 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
70
71 const double DecodeTrace::EndCapWidth = 5;
72 const int DecodeTrace::DrawPadding = 100;
73
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)
91 };
92
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)
110 };
111
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)),
116         _session(session),
117         _decoder_stack(decoder_stack),
118         _delete_mapper(this),
119         _show_hide_mapper(this)
120 {
121         assert(_decoder_stack);
122
123         _colour = DecodeColours[index % countof(DecodeColours)];
124
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)));
131 }
132
133 bool DecodeTrace::enabled() const
134 {
135         return true;
136 }
137
138 const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
139 {
140         return _decoder_stack;
141 }
142
143 void DecodeTrace::set_view(pv::view::View *view)
144 {
145         assert(view);
146         Trace::set_view(view);
147 }
148
149 void DecodeTrace::paint_back(QPainter &p, int left, int right)
150 {
151         Trace::paint_back(p, left, right);
152         paint_axis(p, get_y(), left, right);
153 }
154
155 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
156 {
157         using namespace pv::data::decode;
158
159         const double scale = _view->scale();
160         assert(scale > 0);
161
162         double samplerate = _decoder_stack->samplerate();
163
164         // Show sample rate as 1Hz when it is unknown
165         if (samplerate == 0.0)
166                 samplerate = 1.0;
167
168         const double pixels_offset = (_view->offset() -
169                 _decoder_stack->get_start_time()) / scale;
170         const double samples_per_pixel = samplerate * scale;
171
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);
176
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;
181
182         assert(_decoder_stack);
183         const QString err = _decoder_stack->error_message();
184         if (!err.isEmpty())
185         {
186                 draw_unresolved_period(p, annotation_height, left, right,
187                         samples_per_pixel, pixels_offset);
188                 draw_error(p, err, left, right);
189                 return;
190         }
191
192         // Iterate through the rows
193         assert(_view);
194         int y = get_y();
195
196         assert(_decoder_stack);
197
198         const vector<Row> rows(_decoder_stack->get_visible_rows());
199         for (size_t i = 0; i < rows.size(); i++)
200         {
201                 const Row &row = rows[i];
202
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());
207                 base_colour >>= 16;
208
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,
217                                         base_colour);
218                         y += row_height;
219                 }
220         }
221
222         // Draw the hatching
223         draw_unresolved_period(p, annotation_height, left, right,
224                 samples_per_pixel, pixels_offset);
225 }
226
227 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
228 {
229         using pv::data::decode::Decoder;
230
231         assert(form);
232         assert(parent);
233         assert(_decoder_stack);
234
235         // Add the standard options
236         Trace::populate_popup_form(parent, form);
237
238         // Add the decoder options
239         _bindings.clear();
240         _probe_selectors.clear();
241         _decoder_forms.clear();
242
243         const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
244
245         if (stack.empty())
246         {
247                 QLabel *const l = new QLabel(
248                         tr("<p><i>No decoders in the stack</i></p>"));
249                 l->setAlignment(Qt::AlignCenter);
250                 form->addRow(l);
251         }
252         else
253         {
254                 list< shared_ptr<Decoder> >::const_iterator iter =
255                         stack.begin();
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);
259                 }
260
261                 form->addRow(new QLabel(
262                         tr("<i>* Required Probes</i>"), parent));
263         }
264
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*)));
270
271         QPushButton *const stack_button =
272                 new QPushButton(tr("Stack Decoder"), parent);
273         stack_button->setMenu(decoder_menu);
274
275         QHBoxLayout *stack_button_box = new QHBoxLayout;
276         stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
277         form->addRow(stack_button_box);
278 }
279
280 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
281 {
282         QMenu *const menu = Trace::create_context_menu(parent);
283
284         menu->addSeparator();
285
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);
290
291         return menu;
292 }
293
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
298 {
299         const double start = a.start_sample() / samples_per_pixel -
300                 pixels_offset;
301         const double end = a.end_sample() / samples_per_pixel -
302                 pixels_offset;
303
304         const size_t colour = (base_colour + a.format()) % countof(Colours);
305         const QColor &fill = Colours[colour];
306         const QColor &outline = OutlineColours[colour];
307
308         if (start > right + DrawPadding || end < left - DrawPadding)
309                 return;
310
311         if (a.start_sample() == a.end_sample())
312                 draw_instant(a, p, fill, outline, text_color, h,
313                         start, y);
314         else
315                 draw_range(a, p, fill, outline, text_color, h,
316                         start, end, y);
317 }
318
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
321 {
322         const QString text = a.annotations().empty() ?
323                 QString() : a.annotations().back();
324         const double w = min(p.boundingRect(QRectF(), 0, text).width(),
325                 0.0) + h;
326         const QRectF rect(x - w / 2, y - h / 2, w, h);
327
328         p.setPen(outline);
329         p.setBrush(fill);
330         p.drawRoundedRect(rect, h / 2, h / 2);
331
332         p.setPen(text_color);
333         p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
334 }
335
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
339 {
340         const double top = y + .5 - h / 2;
341         const double bottom = y + .5 + h / 2;
342         const vector<QString> annotations = a.annotations();
343
344         p.setPen(outline);
345         p.setBrush(fill);
346
347         // If the two ends are within 1 pixel, draw a vertical line
348         if (start + 1.0 > end)
349         {
350                 p.drawLine(QPointF(start, top), QPointF(start, bottom));
351                 return;
352         }
353
354         const double cap_width = min((end - start) / 4, EndCapWidth);
355
356         QPointF pts[] = {
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)
363         };
364
365         p.drawConvexPolygon(pts, countof(pts));
366
367         if (annotations.empty())
368                 return;
369
370         QRectF rect(start + cap_width, y - h / 2,
371                 end - start - cap_width * 2, h);
372         p.setPen(text_color);
373
374         // Try to find an annotation that will fit
375         QString best_annotation;
376         int best_width = 0;
377
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;
382         }
383
384         if (best_annotation.isEmpty())
385                 best_annotation = annotations.back();
386
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()));
390 }
391
392 void DecodeTrace::draw_error(QPainter &p, const QString &message,
393         int left, int right)
394 {
395         const int y = get_y();
396
397         p.setPen(ErrorBgColour.darker());
398         p.setBrush(ErrorBgColour);
399
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;
405
406         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
407                 Qt::AbsoluteSize);
408
409         p.setPen(get_text_colour());
410         p.drawText(text_rect, message);
411 }
412
413 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
414         int right, double samples_per_pixel, double pixels_offset) 
415 {
416         using namespace pv::data;
417         using pv::data::decode::Decoder;
418
419         assert(_decoder_stack); 
420
421         shared_ptr<Logic> data;
422         shared_ptr<LogicSignal> logic_signal;
423
424         const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
425
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())))
433                         break;
434
435         if (!data || data->get_snapshots().empty())
436                 return;
437
438         const shared_ptr<LogicSnapshot> snapshot =
439                 data->get_snapshots().front();
440         assert(snapshot);
441         const int64_t sample_count = (int64_t)snapshot->get_sample_count();
442         if (sample_count == 0)
443                 return;
444
445         const int64_t samples_decoded = _decoder_stack->samples_decoded();
446         if (sample_count == samples_decoded)
447                 return;
448
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);
455
456         p.setPen(QPen(Qt::NoPen));
457         p.setBrush(Qt::white);
458         p.drawRect(no_decode_rect);
459
460         p.setPen(NoDecodeColour);
461         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
462         p.drawRect(no_decode_rect);
463 }
464
465 void DecodeTrace::create_decoder_form(int index,
466         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
467         QFormLayout *form)
468 {
469         const GSList *probe;
470
471         assert(dec);
472         const srd_decoder *const decoder = dec->decoder();
473         assert(decoder);
474
475         pv::widgets::DecoderGroupBox *const group =
476                 new pv::widgets::DecoderGroupBox(
477                         QString::fromUtf8(decoder->name));
478         group->set_decoder_visible(dec->shown());
479
480         _delete_mapper.setMapping(group, index);
481         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
482
483         _show_hide_mapper.setMapping(group, index);
484         connect(group, SIGNAL(show_hide_decoder()),
485                 &_show_hide_mapper, SLOT(map()));
486
487         QFormLayout *const decoder_form = new QFormLayout;
488         group->add_layout(decoder_form);
489
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);
500
501                 const ProbeSelector s = {combo, dec, p};
502                 _probe_selectors.push_back(s);
503         }
504
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);
515
516                 const ProbeSelector s = {combo, dec, p};
517                 _probe_selectors.push_back(s);
518         }
519
520         // Add the options
521         shared_ptr<prop::binding::DecoderOptions> binding(
522                 new prop::binding::DecoderOptions(_decoder_stack, dec));
523         binding->add_properties_to_form(decoder_form, true);
524
525         _bindings.push_back(binding);
526
527         form->addRow(group);
528         _decoder_forms.push_back(group);
529 }
530
531 QComboBox* DecodeTrace::create_probe_selector(
532         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
533         const srd_probe *const probe)
534 {
535         assert(dec);
536
537         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
538
539         assert(_decoder_stack);
540         const map<const srd_probe*,
541                 shared_ptr<LogicSignal> >::const_iterator probe_iter =
542                 dec->probes().find(probe);
543
544         QComboBox *selector = new QComboBox(parent);
545
546         selector->addItem("-", qVariantFromValue((void*)NULL));
547
548         if (probe_iter == dec->probes().end())
549                 selector->setCurrentIndex(0);
550
551         for(size_t i = 0; i < sigs.size(); i++) {
552                 const shared_ptr<view::Signal> s(sigs[i]);
553                 assert(s);
554
555                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
556                 {
557                         selector->addItem(s->get_name(),
558                                 qVariantFromValue((void*)s.get()));
559                         if ((*probe_iter).second == s)
560                                 selector->setCurrentIndex(i + 1);
561                 }
562         }
563
564         return selector;
565 }
566
567 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
568 {
569         assert(dec);
570
571         map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
572         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
573
574         BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
575         {
576                 if(s._decoder != dec)
577                         break;
578
579                 const LogicSignal *const selection =
580                         (LogicSignal*)s._combo->itemData(
581                                 s._combo->currentIndex()).value<void*>();
582
583                 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
584                         if(sig.get() == selection) {
585                                 probe_map[s._probe] =
586                                         dynamic_pointer_cast<LogicSignal>(sig);
587                                 break;
588                         }
589         }
590
591         dec->set_probes(probe_map);
592 }
593
594 void DecodeTrace::commit_probes()
595 {
596         assert(_decoder_stack);
597         BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
598                 _decoder_stack->stack())
599                 commit_decoder_probes(dec);
600
601         _decoder_stack->begin_decode();
602 }
603
604 void DecodeTrace::on_new_decode_data()
605 {
606         if (_view)
607                 _view->update_viewport();
608 }
609
610 void DecodeTrace::delete_pressed()
611 {
612         on_delete();
613 }
614
615 void DecodeTrace::on_delete()
616 {
617         _session.remove_decode_signal(this);
618 }
619
620 void DecodeTrace::on_probe_selected(int)
621 {
622         commit_probes();
623 }
624
625 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
626 {
627         assert(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();
632
633         create_popup_form();
634 }
635
636 void DecodeTrace::on_delete_decoder(int index)
637 {
638         _decoder_stack->remove(index);
639
640         // Update the popup
641         create_popup_form();    
642
643         _decoder_stack->begin_decode();
644 }
645
646 void DecodeTrace::on_show_hide_decoder(int index)
647 {
648         using pv::data::decode::Decoder;
649
650         const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
651
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());
656
657         shared_ptr<Decoder> dec = *iter;
658         assert(dec);
659
660         const bool show = !dec->shown();
661         dec->show(show);
662
663         assert(index < (int)_decoder_forms.size());
664         _decoder_forms[index]->set_decoder_visible(show);
665
666         _view->update_viewport();
667 }
668
669 } // namespace view
670 } // namespace pv