]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
Rename 'probe' to 'channel' (libsigrokdecode change).
[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 #include <boost/functional/hash.hpp>
29
30 #include <QAction>
31 #include <QApplication>
32 #include <QComboBox>
33 #include <QFormLayout>
34 #include <QLabel>
35 #include <QMenu>
36 #include <QPushButton>
37
38 #include "decodetrace.h"
39
40 #include <pv/sigsession.h>
41 #include <pv/data/decoderstack.h>
42 #include <pv/data/decode/decoder.h>
43 #include <pv/data/logic.h>
44 #include <pv/data/logicsnapshot.h>
45 #include <pv/data/decode/annotation.h>
46 #include <pv/view/logicsignal.h>
47 #include <pv/view/view.h>
48 #include <pv/widgets/decodergroupbox.h>
49 #include <pv/widgets/decodermenu.h>
50
51 using boost::dynamic_pointer_cast;
52 using boost::shared_ptr;
53 using std::list;
54 using std::max;
55 using std::map;
56 using std::min;
57 using std::vector;
58
59 namespace pv {
60 namespace view {
61
62 const QColor DecodeTrace::DecodeColours[4] = {
63         QColor(0xEF, 0x29, 0x29),       // Red
64         QColor(0xFC, 0xE9, 0x4F),       // Yellow
65         QColor(0x8A, 0xE2, 0x34),       // Green
66         QColor(0x72, 0x9F, 0xCF)        // Blue
67 };
68
69 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
70 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
71
72 const int DecodeTrace::ArrowSize = 4;
73 const double DecodeTrace::EndCapWidth = 5;
74 const int DecodeTrace::DrawPadding = 100;
75
76 const QColor DecodeTrace::Colours[16] = {
77         QColor(0xEF, 0x29, 0x29),
78         QColor(0xF6, 0x6A, 0x32),
79         QColor(0xFC, 0xAE, 0x3E),
80         QColor(0xFB, 0xCA, 0x47),
81         QColor(0xFC, 0xE9, 0x4F),
82         QColor(0xCD, 0xF0, 0x40),
83         QColor(0x8A, 0xE2, 0x34),
84         QColor(0x4E, 0xDC, 0x44),
85         QColor(0x55, 0xD7, 0x95),
86         QColor(0x64, 0xD1, 0xD2),
87         QColor(0x72, 0x9F, 0xCF),
88         QColor(0xD4, 0x76, 0xC4),
89         QColor(0x9D, 0x79, 0xB9),
90         QColor(0xAD, 0x7F, 0xA8),
91         QColor(0xC2, 0x62, 0x9B),
92         QColor(0xD7, 0x47, 0x6F)
93 };
94
95 const QColor DecodeTrace::OutlineColours[16] = {
96         QColor(0x77, 0x14, 0x14),
97         QColor(0x7B, 0x35, 0x19),
98         QColor(0x7E, 0x57, 0x1F),
99         QColor(0x7D, 0x65, 0x23),
100         QColor(0x7E, 0x74, 0x27),
101         QColor(0x66, 0x78, 0x20),
102         QColor(0x45, 0x71, 0x1A),
103         QColor(0x27, 0x6E, 0x22),
104         QColor(0x2A, 0x6B, 0x4A),
105         QColor(0x32, 0x68, 0x69),
106         QColor(0x39, 0x4F, 0x67),
107         QColor(0x6A, 0x3B, 0x62),
108         QColor(0x4E, 0x3C, 0x5C),
109         QColor(0x56, 0x3F, 0x54),
110         QColor(0x61, 0x31, 0x4D),
111         QColor(0x6B, 0x23, 0x37)
112 };
113
114 DecodeTrace::DecodeTrace(pv::SigSession &session,
115         boost::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
116         Trace(QString::fromUtf8(
117                 decoder_stack->stack().front()->decoder()->name)),
118         _session(session),
119         _decoder_stack(decoder_stack),
120         _delete_mapper(this),
121         _show_hide_mapper(this)
122 {
123         assert(_decoder_stack);
124
125         _colour = DecodeColours[index % countof(DecodeColours)];
126
127         connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
128                 this, SLOT(on_new_decode_data()));
129         connect(&_delete_mapper, SIGNAL(mapped(int)),
130                 this, SLOT(on_delete_decoder(int)));
131         connect(&_show_hide_mapper, SIGNAL(mapped(int)),
132                 this, SLOT(on_show_hide_decoder(int)));
133 }
134
135 bool DecodeTrace::enabled() const
136 {
137         return true;
138 }
139
140 const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
141 {
142         return _decoder_stack;
143 }
144
145 void DecodeTrace::set_view(pv::view::View *view)
146 {
147         assert(view);
148         Trace::set_view(view);
149 }
150
151 void DecodeTrace::paint_back(QPainter &p, int left, int right)
152 {
153         Trace::paint_back(p, left, right);
154         paint_axis(p, get_y(), left, right);
155 }
156
157 void DecodeTrace::paint_mid(QPainter &p, int left, int right)
158 {
159         using namespace pv::data::decode;
160
161         const double scale = _view->scale();
162         assert(scale > 0);
163
164         double samplerate = _decoder_stack->samplerate();
165
166         _cur_row_headings.clear();
167
168         // Show sample rate as 1Hz when it is unknown
169         if (samplerate == 0.0)
170                 samplerate = 1.0;
171
172         const double pixels_offset = (_view->offset() -
173                 _decoder_stack->get_start_time()) / scale;
174         const double samples_per_pixel = samplerate * scale;
175
176         const uint64_t start_sample = (uint64_t)max((left + pixels_offset) *
177                 samples_per_pixel, 0.0);
178         const uint64_t end_sample = (uint64_t)max((right + pixels_offset) *
179                 samples_per_pixel, 0.0);
180
181         QFontMetrics m(QApplication::font());
182         const int text_height =  m.boundingRect(QRect(), 0, "Tg").height();
183         const int annotation_height = (text_height * 5) / 4;
184         const int row_height = (text_height * 6) / 4;
185
186         assert(_decoder_stack);
187         const QString err = _decoder_stack->error_message();
188         if (!err.isEmpty())
189         {
190                 draw_unresolved_period(p, annotation_height, left, right,
191                         samples_per_pixel, pixels_offset);
192                 draw_error(p, err, left, right);
193                 return;
194         }
195
196         // Iterate through the rows
197         assert(_view);
198         int y = get_y();
199
200         assert(_decoder_stack);
201
202         const vector<Row> rows(_decoder_stack->get_visible_rows());
203         for (size_t i = 0; i < rows.size(); i++)
204         {
205                 const Row &row = rows[i];
206
207                 size_t base_colour = 0x13579BDF;
208                 boost::hash_combine(base_colour, this);
209                 boost::hash_combine(base_colour, row.decoder());
210                 boost::hash_combine(base_colour, row.row());
211                 base_colour >>= 16;
212
213                 vector<Annotation> annotations;
214                 _decoder_stack->get_annotation_subset(annotations, row,
215                         start_sample, end_sample);
216                 if (!annotations.empty()) {
217                         BOOST_FOREACH(const Annotation &a, annotations)
218                                 draw_annotation(a, p, get_text_colour(),
219                                         annotation_height, left, right,
220                                         samples_per_pixel, pixels_offset, y,
221                                         base_colour);
222                         y += row_height;
223
224                         _cur_row_headings.push_back(row.title());
225                 }
226         }
227
228         // Draw the hatching
229         draw_unresolved_period(p, annotation_height, left, right,
230                 samples_per_pixel, pixels_offset);
231 }
232
233 void DecodeTrace::paint_fore(QPainter &p, int left, int right)
234 {
235         using namespace pv::data::decode;
236
237         (void)right;
238
239         QFontMetrics m(QApplication::font());
240         const int text_height =  m.boundingRect(QRect(), 0, "Tg").height();
241         const int row_height = (text_height * 6) / 4;
242
243         for (size_t i = 0; i < _cur_row_headings.size(); i++)
244         {
245                 const int y = i * row_height + get_y();
246
247                 p.setPen(QPen(Qt::NoPen));
248                 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
249
250                 if (i != 0)
251                 {
252                         const QPointF points[] = {
253                                 QPointF(left, y - ArrowSize),
254                                 QPointF(left + ArrowSize, y),
255                                 QPointF(left, y + ArrowSize)
256                         };
257                         p.drawPolygon(points, countof(points));
258                 }
259
260                 const QRect r(left + ArrowSize * 2, y - row_height / 2,
261                         right - left, row_height);
262                 const QString h(_cur_row_headings[i]);
263                 const int f = Qt::AlignLeft | Qt::AlignVCenter |
264                         Qt::TextDontClip;
265
266                 // Draw the outline
267                 p.setPen(QApplication::palette().color(QPalette::Base));
268                 for (int dx = -1; dx <= 1; dx++)
269                         for (int dy = -1; dy <= 1; dy++)
270                                 if (dx != 0 && dy != 0)
271                                         p.drawText(r.translated(dx, dy), f, h);
272
273                 // Draw the text
274                 p.setPen(QApplication::palette().color(QPalette::WindowText));
275                 p.drawText(r, f, h);
276         }
277 }
278
279 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
280 {
281         using pv::data::decode::Decoder;
282
283         assert(form);
284         assert(parent);
285         assert(_decoder_stack);
286
287         // Add the standard options
288         Trace::populate_popup_form(parent, form);
289
290         // Add the decoder options
291         _bindings.clear();
292         _probe_selectors.clear();
293         _decoder_forms.clear();
294
295         const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
296
297         if (stack.empty())
298         {
299                 QLabel *const l = new QLabel(
300                         tr("<p><i>No decoders in the stack</i></p>"));
301                 l->setAlignment(Qt::AlignCenter);
302                 form->addRow(l);
303         }
304         else
305         {
306                 list< shared_ptr<Decoder> >::const_iterator iter =
307                         stack.begin();
308                 for (int i = 0; i < (int)stack.size(); i++, iter++) {
309                         shared_ptr<Decoder> dec(*iter);
310                         create_decoder_form(i, dec, parent, form);
311                 }
312
313                 form->addRow(new QLabel(
314                         tr("<i>* Required channels</i>"), parent));
315         }
316
317         // Add stacking button
318         pv::widgets::DecoderMenu *const decoder_menu =
319                 new pv::widgets::DecoderMenu(parent);
320         connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
321                 this, SLOT(on_stack_decoder(srd_decoder*)));
322
323         QPushButton *const stack_button =
324                 new QPushButton(tr("Stack Decoder"), parent);
325         stack_button->setMenu(decoder_menu);
326
327         QHBoxLayout *stack_button_box = new QHBoxLayout;
328         stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
329         form->addRow(stack_button_box);
330 }
331
332 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
333 {
334         QMenu *const menu = Trace::create_context_menu(parent);
335
336         menu->addSeparator();
337
338         QAction *const del = new QAction(tr("Delete"), this);
339         del->setShortcuts(QKeySequence::Delete);
340         connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
341         menu->addAction(del);
342
343         return menu;
344 }
345
346 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
347         QPainter &p, QColor text_color, int h, int left, int right,
348         double samples_per_pixel, double pixels_offset, int y,
349         size_t base_colour) const
350 {
351         const double start = a.start_sample() / samples_per_pixel -
352                 pixels_offset;
353         const double end = a.end_sample() / samples_per_pixel -
354                 pixels_offset;
355
356         const size_t colour = (base_colour + a.format()) % countof(Colours);
357         const QColor &fill = Colours[colour];
358         const QColor &outline = OutlineColours[colour];
359
360         if (start > right + DrawPadding || end < left - DrawPadding)
361                 return;
362
363         if (a.start_sample() == a.end_sample())
364                 draw_instant(a, p, fill, outline, text_color, h,
365                         start, y);
366         else
367                 draw_range(a, p, fill, outline, text_color, h,
368                         start, end, y);
369 }
370
371 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
372         QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
373 {
374         const QString text = a.annotations().empty() ?
375                 QString() : a.annotations().back();
376         const double w = min(p.boundingRect(QRectF(), 0, text).width(),
377                 0.0) + h;
378         const QRectF rect(x - w / 2, y - h / 2, w, h);
379
380         p.setPen(outline);
381         p.setBrush(fill);
382         p.drawRoundedRect(rect, h / 2, h / 2);
383
384         p.setPen(text_color);
385         p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
386 }
387
388 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
389         QColor fill, QColor outline, QColor text_color, int h, double start,
390         double end, int y) const
391 {
392         const double top = y + .5 - h / 2;
393         const double bottom = y + .5 + h / 2;
394         const vector<QString> annotations = a.annotations();
395
396         p.setPen(outline);
397         p.setBrush(fill);
398
399         // If the two ends are within 1 pixel, draw a vertical line
400         if (start + 1.0 > end)
401         {
402                 p.drawLine(QPointF(start, top), QPointF(start, bottom));
403                 return;
404         }
405
406         const double cap_width = min((end - start) / 4, EndCapWidth);
407
408         QPointF pts[] = {
409                 QPointF(start, y + .5f),
410                 QPointF(start + cap_width, top),
411                 QPointF(end - cap_width, top),
412                 QPointF(end, y + .5f),
413                 QPointF(end - cap_width, bottom),
414                 QPointF(start + cap_width, bottom)
415         };
416
417         p.drawConvexPolygon(pts, countof(pts));
418
419         if (annotations.empty())
420                 return;
421
422         QRectF rect(start + cap_width, y - h / 2,
423                 end - start - cap_width * 2, h);
424         if (rect.width() <= 4)
425                 return;
426
427         p.setPen(text_color);
428
429         // Try to find an annotation that will fit
430         QString best_annotation;
431         int best_width = 0;
432
433         BOOST_FOREACH(const QString &a, annotations) {
434                 const int w = p.boundingRect(QRectF(), 0, a).width();
435                 if (w <= rect.width() && w > best_width)
436                         best_annotation = a, best_width = w;
437         }
438
439         if (best_annotation.isEmpty())
440                 best_annotation = annotations.back();
441
442         // If not ellide the last in the list
443         p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
444                 best_annotation, Qt::ElideRight, rect.width()));
445 }
446
447 void DecodeTrace::draw_error(QPainter &p, const QString &message,
448         int left, int right)
449 {
450         const int y = get_y();
451
452         p.setPen(ErrorBgColour.darker());
453         p.setBrush(ErrorBgColour);
454
455         const QRectF bounding_rect =
456                 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
457         const QRectF text_rect = p.boundingRect(bounding_rect,
458                 Qt::AlignCenter, message);
459         const float r = text_rect.height() / 4;
460
461         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
462                 Qt::AbsoluteSize);
463
464         p.setPen(get_text_colour());
465         p.drawText(text_rect, message);
466 }
467
468 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
469         int right, double samples_per_pixel, double pixels_offset) 
470 {
471         using namespace pv::data;
472         using pv::data::decode::Decoder;
473
474         assert(_decoder_stack); 
475
476         shared_ptr<Logic> data;
477         shared_ptr<LogicSignal> logic_signal;
478
479         const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
480
481         // We get the logic data of the first probe in the list.
482         // This works because we are currently assuming all
483         // LogicSignals have the same data/snapshot
484         BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
485                 if (dec && !dec->channels().empty() &&
486                         ((logic_signal = (*dec->channels().begin()).second)) &&
487                         ((data = logic_signal->logic_data())))
488                         break;
489
490         if (!data || data->get_snapshots().empty())
491                 return;
492
493         const shared_ptr<LogicSnapshot> snapshot =
494                 data->get_snapshots().front();
495         assert(snapshot);
496         const int64_t sample_count = (int64_t)snapshot->get_sample_count();
497         if (sample_count == 0)
498                 return;
499
500         const int64_t samples_decoded = _decoder_stack->samples_decoded();
501         if (sample_count == samples_decoded)
502                 return;
503
504         const int y = get_y();
505         const double start = max(samples_decoded /
506                 samples_per_pixel - pixels_offset, left - 1.0);
507         const double end = min(sample_count / samples_per_pixel -
508                 pixels_offset, right + 1.0);
509         const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
510
511         p.setPen(QPen(Qt::NoPen));
512         p.setBrush(Qt::white);
513         p.drawRect(no_decode_rect);
514
515         p.setPen(NoDecodeColour);
516         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
517         p.drawRect(no_decode_rect);
518 }
519
520 void DecodeTrace::create_decoder_form(int index,
521         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
522         QFormLayout *form)
523 {
524         const GSList *l;
525
526         assert(dec);
527         const srd_decoder *const decoder = dec->decoder();
528         assert(decoder);
529
530         pv::widgets::DecoderGroupBox *const group =
531                 new pv::widgets::DecoderGroupBox(
532                         QString::fromUtf8(decoder->name));
533         group->set_decoder_visible(dec->shown());
534
535         _delete_mapper.setMapping(group, index);
536         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
537
538         _show_hide_mapper.setMapping(group, index);
539         connect(group, SIGNAL(show_hide_decoder()),
540                 &_show_hide_mapper, SLOT(map()));
541
542         QFormLayout *const decoder_form = new QFormLayout;
543         group->add_layout(decoder_form);
544
545         // Add the mandatory channels
546         for(l = decoder->channels; l; l = l->next) {
547                 const struct srd_channel *const pdch =
548                         (struct srd_channel *)l->data;
549                 QComboBox *const combo = create_probe_selector(parent, dec, pdch);
550                 connect(combo, SIGNAL(currentIndexChanged(int)),
551                         this, SLOT(on_probe_selected(int)));
552                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
553                         .arg(QString::fromUtf8(pdch->name))
554                         .arg(QString::fromUtf8(pdch->desc)), combo);
555
556                 const ProbeSelector s = {combo, dec, pdch};
557                 _probe_selectors.push_back(s);
558         }
559
560         // Add the optional channels
561         for(l = decoder->opt_channels; l; l = l->next) {
562                 const struct srd_channel *const pdch =
563                         (struct srd_channel *)l->data;
564                 QComboBox *const combo = create_probe_selector(parent, dec, pdch);
565                 connect(combo, SIGNAL(currentIndexChanged(int)),
566                         this, SLOT(on_probe_selected(int)));
567                 decoder_form->addRow(tr("<b>%1</b> (%2)")
568                         .arg(QString::fromUtf8(pdch->name))
569                         .arg(QString::fromUtf8(pdch->desc)), combo);
570
571                 const ProbeSelector s = {combo, dec, pdch};
572                 _probe_selectors.push_back(s);
573         }
574
575         // Add the options
576         shared_ptr<prop::binding::DecoderOptions> binding(
577                 new prop::binding::DecoderOptions(_decoder_stack, dec));
578         binding->add_properties_to_form(decoder_form, true);
579
580         _bindings.push_back(binding);
581
582         form->addRow(group);
583         _decoder_forms.push_back(group);
584 }
585
586 QComboBox* DecodeTrace::create_probe_selector(
587         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
588         const srd_channel *const pdch)
589 {
590         assert(dec);
591
592         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
593
594         assert(_decoder_stack);
595         const map<const srd_channel*,
596                 shared_ptr<LogicSignal> >::const_iterator probe_iter =
597                 dec->channels().find(pdch);
598
599         QComboBox *selector = new QComboBox(parent);
600
601         selector->addItem("-", qVariantFromValue((void*)NULL));
602
603         if (probe_iter == dec->channels().end())
604                 selector->setCurrentIndex(0);
605
606         for(size_t i = 0; i < sigs.size(); i++) {
607                 const shared_ptr<view::Signal> s(sigs[i]);
608                 assert(s);
609
610                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
611                 {
612                         selector->addItem(s->get_name(),
613                                 qVariantFromValue((void*)s.get()));
614                         if ((*probe_iter).second == s)
615                                 selector->setCurrentIndex(i + 1);
616                 }
617         }
618
619         return selector;
620 }
621
622 void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
623 {
624         assert(dec);
625
626         map<const srd_channel*, shared_ptr<LogicSignal> > probe_map;
627         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
628
629         BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
630         {
631                 if(s._decoder != dec)
632                         break;
633
634                 const LogicSignal *const selection =
635                         (LogicSignal*)s._combo->itemData(
636                                 s._combo->currentIndex()).value<void*>();
637
638                 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
639                         if(sig.get() == selection) {
640                                 probe_map[s._pdch] =
641                                         dynamic_pointer_cast<LogicSignal>(sig);
642                                 break;
643                         }
644         }
645
646         dec->set_probes(probe_map);
647 }
648
649 void DecodeTrace::commit_probes()
650 {
651         assert(_decoder_stack);
652         BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
653                 _decoder_stack->stack())
654                 commit_decoder_probes(dec);
655
656         _decoder_stack->begin_decode();
657 }
658
659 void DecodeTrace::on_new_decode_data()
660 {
661         if (_view)
662                 _view->update_viewport();
663 }
664
665 void DecodeTrace::delete_pressed()
666 {
667         on_delete();
668 }
669
670 void DecodeTrace::on_delete()
671 {
672         _session.remove_decode_signal(this);
673 }
674
675 void DecodeTrace::on_probe_selected(int)
676 {
677         commit_probes();
678 }
679
680 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
681 {
682         assert(decoder);
683         assert(_decoder_stack);
684         _decoder_stack->push(shared_ptr<data::decode::Decoder>(
685                 new data::decode::Decoder(decoder)));
686         _decoder_stack->begin_decode();
687
688         create_popup_form();
689 }
690
691 void DecodeTrace::on_delete_decoder(int index)
692 {
693         _decoder_stack->remove(index);
694
695         // Update the popup
696         create_popup_form();    
697
698         _decoder_stack->begin_decode();
699 }
700
701 void DecodeTrace::on_show_hide_decoder(int index)
702 {
703         using pv::data::decode::Decoder;
704
705         const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
706
707         // Find the decoder in the stack
708         list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
709         for(int i = 0; i < index; i++, iter++)
710                 assert(iter != stack.end());
711
712         shared_ptr<Decoder> dec = *iter;
713         assert(dec);
714
715         const bool show = !dec->shown();
716         dec->show(show);
717
718         assert(index < (int)_decoder_forms.size());
719         _decoder_forms[index]->set_decoder_visible(show);
720
721         _view->update_viewport();
722 }
723
724 } // namespace view
725 } // namespace pv