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