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