]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
1a629f2c182dbc0bee782ddbbb3feb6acc8726e0
[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 <QComboBox>
31 #include <QFormLayout>
32 #include <QLabel>
33 #include <QMenu>
34 #include <QPushButton>
35
36 #include "decodetrace.h"
37
38 #include <pv/sigsession.h>
39 #include <pv/data/decoderstack.h>
40 #include <pv/data/decode/decoder.h>
41 #include <pv/data/logic.h>
42 #include <pv/data/logicsnapshot.h>
43 #include <pv/view/logicsignal.h>
44 #include <pv/view/view.h>
45 #include <pv/view/decode/annotation.h>
46 #include <pv/widgets/decodergroupbox.h>
47 #include <pv/widgets/decodermenu.h>
48
49 using namespace boost;
50 using namespace std;
51
52 namespace pv {
53 namespace view {
54
55 const QColor DecodeTrace::DecodeColours[4] = {
56         QColor(0xEF, 0x29, 0x29),       // Red
57         QColor(0xFC, 0xE9, 0x4F),       // Yellow
58         QColor(0x8A, 0xE2, 0x34),       // Green
59         QColor(0x72, 0x9F, 0xCF)        // Blue
60 };
61
62 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
63 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
64
65 DecodeTrace::DecodeTrace(pv::SigSession &session,
66         boost::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
67         Trace(session, QString(decoder_stack->stack().front()->decoder()->name)),
68         _decoder_stack(decoder_stack),
69         _delete_mapper(this)
70 {
71         assert(_decoder_stack);
72
73         _colour = DecodeColours[index % countof(DecodeColours)];
74
75         connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
76                 this, SLOT(on_new_decode_data()));
77         connect(&_delete_mapper, SIGNAL(mapped(int)),
78                 this, SLOT(on_delete_decoder(int)));
79 }
80
81 bool DecodeTrace::enabled() const
82 {
83         return true;
84 }
85
86 const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
87 {
88         return _decoder_stack;
89 }
90
91 void DecodeTrace::set_view(pv::view::View *view)
92 {
93         assert(view);
94         Trace::set_view(view);
95 }
96
97 void DecodeTrace::paint_back(QPainter &p, int left, int right)
98 {
99         Trace::paint_back(p, left, right);
100         paint_axis(p, get_y(), left, right);
101 }
102
103 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
104 {
105         using namespace pv::view::decode;
106
107         const double scale = _view->scale();
108         assert(scale > 0);
109
110         double samplerate = _decoder_stack->samplerate();
111
112         // Show sample rate as 1Hz when it is unknown
113         if (samplerate == 0.0)
114                 samplerate = 1.0;
115
116         const double pixels_offset = (_view->offset() -
117                 _decoder_stack->get_start_time()) / scale;
118         const double samples_per_pixel = samplerate * scale;
119
120         const int h = (_text_size.height() * 5) / 4;
121
122         assert(_decoder_stack);
123         const QString err = _decoder_stack->error_message();
124         if (!err.isEmpty())
125         {
126                 draw_error(p, err, left, right);
127                 draw_unresolved_period(p, h, left, right, samples_per_pixel,
128                         pixels_offset);
129                 return;
130         }
131
132         assert(_view);
133         const int y = get_y();
134
135         assert(_decoder_stack);
136         vector< shared_ptr<Annotation> > annotations(_decoder_stack->annotations());
137         BOOST_FOREACH(shared_ptr<Annotation> a, annotations) {
138                 assert(a);
139                 a->paint(p, get_text_colour(), h, left, right,
140                         samples_per_pixel, pixels_offset, y);
141         }
142
143         draw_unresolved_period(p, h, left, right,
144                 samples_per_pixel, pixels_offset);
145 }
146
147 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
148 {
149         using pv::data::decode::Decoder;
150
151         assert(form);
152         assert(parent);
153         assert(_decoder_stack);
154
155         // Add the standard options
156         Trace::populate_popup_form(parent, form);
157
158         // Add the decoder options
159         _bindings.clear();
160         _probe_selectors.clear();
161
162         const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
163
164         if (stack.empty())
165         {
166                 QLabel *const l = new QLabel(
167                         tr("<p><i>No decoders in the stack</i></p>"));
168                 l->setAlignment(Qt::AlignCenter);
169                 form->addRow(l);
170         }
171         else
172         {
173                 list< shared_ptr<Decoder> >::const_iterator iter =
174                         stack.begin();
175                 for (int i = 0; i < (int)stack.size(); i++, iter++) {
176                         shared_ptr<Decoder> dec(*iter);
177                         create_decoder_form(i, dec, parent, form);
178                 }
179
180                 form->addRow(new QLabel(
181                         tr("<i>* Required Probes</i>"), parent));
182         }
183
184         // Add stacking button
185         pv::widgets::DecoderMenu *const decoder_menu =
186                 new pv::widgets::DecoderMenu(parent);
187         connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
188                 this, SLOT(on_stack_decoder(srd_decoder*)));
189
190         QPushButton *const stack_button =
191                 new QPushButton(tr("Stack Decoder"), parent);
192         stack_button->setMenu(decoder_menu);
193
194         QHBoxLayout *stack_button_box = new QHBoxLayout;
195         stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
196         form->addRow(stack_button_box);
197 }
198
199 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
200 {
201         QMenu *const menu = Trace::create_context_menu(parent);
202
203         menu->addSeparator();
204
205         QAction *const del = new QAction(tr("Delete"), this);
206         del->setShortcuts(QKeySequence::Delete);
207         connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
208         menu->addAction(del);
209
210         return menu;
211 }
212
213 void DecodeTrace::draw_error(QPainter &p, const QString &message,
214         int left, int right)
215 {
216         const int y = get_y();
217
218         p.setPen(ErrorBgColour.darker());
219         p.setBrush(ErrorBgColour);
220
221         const QRectF bounding_rect =
222                 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
223         const QRectF text_rect = p.boundingRect(bounding_rect,
224                 Qt::AlignCenter, message);
225         const float r = text_rect.height() / 4;
226
227         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
228                 Qt::AbsoluteSize);
229
230         p.setPen(get_text_colour());
231         p.drawText(text_rect, message);
232 }
233
234 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
235         int right, double samples_per_pixel, double pixels_offset) 
236 {
237         using namespace pv::data;
238         using pv::data::decode::Decoder;
239
240         assert(_decoder_stack); 
241
242         shared_ptr<Logic> data;
243         shared_ptr<LogicSignal> logic_signal;
244
245         const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
246
247         // We get the logic data of the first probe in the list.
248         // This works because we are currently assuming all
249         // LogicSignals have the same data/snapshot
250         BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
251                 if (dec && !dec->probes().empty() &&
252                         ((logic_signal = (*dec->probes().begin()).second)) &&
253                         ((data = logic_signal->logic_data())))
254                         break;
255
256         if (!data || data->get_snapshots().empty())
257                 return;
258
259         const shared_ptr<LogicSnapshot> snapshot =
260                 data->get_snapshots().front();
261         assert(snapshot);
262         const int64_t sample_count = (int64_t)snapshot->get_sample_count();
263         if (sample_count == 0)
264                 return;
265
266         const int64_t samples_decoded = _decoder_stack->samples_decoded();
267         if (sample_count == samples_decoded)
268                 return;
269
270         const int y = get_y();
271         const double start = max(samples_decoded /
272                 samples_per_pixel - pixels_offset, left - 1.0);
273         const double end = min(sample_count / samples_per_pixel -
274                 pixels_offset, right + 1.0);
275         const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
276
277         p.setPen(QPen(Qt::NoPen));
278         p.setBrush(Qt::white);
279         p.drawRect(no_decode_rect);
280
281         p.setPen(NoDecodeColour);
282         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
283         p.drawRect(no_decode_rect);
284 }
285
286 void DecodeTrace::create_decoder_form(int index,
287         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
288         QFormLayout *form)
289 {
290         const GSList *probe;
291
292         assert(dec);
293         const srd_decoder *const decoder = dec->decoder();
294         assert(decoder);
295
296         pv::widgets::DecoderGroupBox *const group =
297                 new pv::widgets::DecoderGroupBox(decoder->name);
298
299         _delete_mapper.setMapping(group, index);
300         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
301
302         QFormLayout *const decoder_form = new QFormLayout;
303         group->add_layout(decoder_form);
304
305         // Add the mandatory probes
306         for(probe = decoder->probes; probe; probe = probe->next) {
307                 const struct srd_probe *const p =
308                         (struct srd_probe *)probe->data;
309                 QComboBox *const combo = create_probe_selector(parent, dec, p);
310                 connect(combo, SIGNAL(currentIndexChanged(int)),
311                         this, SLOT(on_probe_selected(int)));
312                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
313                         .arg(p->name).arg(p->desc), combo);
314
315                 const ProbeSelector s = {combo, dec, p};
316                 _probe_selectors.push_back(s);
317         }
318
319         // Add the optional probes
320         for(probe = decoder->opt_probes; probe; probe = probe->next) {
321                 const struct srd_probe *const p =
322                         (struct srd_probe *)probe->data;
323                 QComboBox *const combo = create_probe_selector(parent, dec, p);
324                 connect(combo, SIGNAL(currentIndexChanged(int)),
325                         this, SLOT(on_probe_selected(int)));
326                 decoder_form->addRow(tr("<b>%1</b> (%2)")
327                         .arg(p->name).arg(p->desc), combo);
328
329                 const ProbeSelector s = {combo, dec, p};
330                 _probe_selectors.push_back(s);
331         }
332
333         // Add the options
334         shared_ptr<prop::binding::DecoderOptions> binding(
335                 new prop::binding::DecoderOptions(_decoder_stack, dec));
336         binding->add_properties_to_form(decoder_form, true);
337
338         _bindings.push_back(binding);
339
340         form->addRow(group);
341 }
342
343 QComboBox* DecodeTrace::create_probe_selector(
344         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
345         const srd_probe *const probe)
346 {
347         assert(dec);
348
349         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
350
351         assert(_decoder_stack);
352         const map<const srd_probe*,
353                 shared_ptr<LogicSignal> >::const_iterator probe_iter =
354                 dec->probes().find(probe);
355
356         QComboBox *selector = new QComboBox(parent);
357
358         selector->addItem("-", qVariantFromValue((void*)NULL));
359
360         if (probe_iter == dec->probes().end())
361                 selector->setCurrentIndex(0);
362
363         for(size_t i = 0; i < sigs.size(); i++) {
364                 const shared_ptr<view::Signal> s(sigs[i]);
365                 assert(s);
366
367                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
368                 {
369                         selector->addItem(s->get_name(),
370                                 qVariantFromValue((void*)s.get()));
371                         if ((*probe_iter).second == s)
372                                 selector->setCurrentIndex(i + 1);
373                 }
374         }
375
376         return selector;
377 }
378
379 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
380 {
381         assert(dec);
382
383         map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
384         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
385
386         BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
387         {
388                 if(s._decoder != dec)
389                         break;
390
391                 const LogicSignal *const selection =
392                         (LogicSignal*)s._combo->itemData(
393                                 s._combo->currentIndex()).value<void*>();
394
395                 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
396                         if(sig.get() == selection) {
397                                 probe_map[s._probe] =
398                                         dynamic_pointer_cast<LogicSignal>(sig);
399                                 break;
400                         }
401         }
402
403         dec->set_probes(probe_map);
404 }
405
406 void DecodeTrace::commit_probes()
407 {
408         assert(_decoder_stack);
409         BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
410                 _decoder_stack->stack())
411                 commit_decoder_probes(dec);
412
413         _decoder_stack->begin_decode();
414 }
415
416 void DecodeTrace::on_new_decode_data()
417 {
418         if (_view)
419                 _view->update_viewport();
420 }
421
422 void DecodeTrace::delete_pressed()
423 {
424         on_delete();
425 }
426
427 void DecodeTrace::on_delete()
428 {
429         _session.remove_decode_signal(this);
430 }
431
432 void DecodeTrace::on_probe_selected(int)
433 {
434         commit_probes();
435 }
436
437 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
438 {
439         assert(decoder);
440         assert(_decoder_stack);
441         _decoder_stack->push(shared_ptr<data::decode::Decoder>(
442                 new data::decode::Decoder(decoder)));
443         _decoder_stack->begin_decode();
444
445         create_popup_form();
446 }
447
448 void DecodeTrace::on_delete_decoder(int index)
449 {
450         _decoder_stack->remove(index);
451
452         // Update the popup
453         create_popup_form();    
454
455         _decoder_stack->begin_decode();
456 }
457
458 } // namespace view
459 } // namespace pv