]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
b0e08f6f1cfa16fb957284b99232f4206b7eea67
[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::hover_point_changed()
550 {
551 }
552
553 void DecodeTrace::create_decoder_form(int index,
554         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
555         QFormLayout *form)
556 {
557         const GSList *l;
558
559         assert(dec);
560         const srd_decoder *const decoder = dec->decoder();
561         assert(decoder);
562
563         pv::widgets::DecoderGroupBox *const group =
564                 new pv::widgets::DecoderGroupBox(
565                         QString::fromUtf8(decoder->name));
566         group->set_decoder_visible(dec->shown());
567
568         _delete_mapper.setMapping(group, index);
569         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
570
571         _show_hide_mapper.setMapping(group, index);
572         connect(group, SIGNAL(show_hide_decoder()),
573                 &_show_hide_mapper, SLOT(map()));
574
575         QFormLayout *const decoder_form = new QFormLayout;
576         group->add_layout(decoder_form);
577
578         // Add the mandatory channels
579         for(l = decoder->channels; l; l = l->next) {
580                 const struct srd_channel *const pdch =
581                         (struct srd_channel *)l->data;
582                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
583                 connect(combo, SIGNAL(currentIndexChanged(int)),
584                         this, SLOT(on_channel_selected(int)));
585                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
586                         .arg(QString::fromUtf8(pdch->name))
587                         .arg(QString::fromUtf8(pdch->desc)), combo);
588
589                 const ChannelSelector s = {combo, dec, pdch};
590                 _channel_selectors.push_back(s);
591         }
592
593         // Add the optional channels
594         for(l = decoder->opt_channels; l; l = l->next) {
595                 const struct srd_channel *const pdch =
596                         (struct srd_channel *)l->data;
597                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
598                 connect(combo, SIGNAL(currentIndexChanged(int)),
599                         this, SLOT(on_channel_selected(int)));
600                 decoder_form->addRow(tr("<b>%1</b> (%2)")
601                         .arg(QString::fromUtf8(pdch->name))
602                         .arg(QString::fromUtf8(pdch->desc)), combo);
603
604                 const ChannelSelector s = {combo, dec, pdch};
605                 _channel_selectors.push_back(s);
606         }
607
608         // Add the options
609         shared_ptr<prop::binding::DecoderOptions> binding(
610                 new prop::binding::DecoderOptions(_decoder_stack, dec));
611         binding->add_properties_to_form(decoder_form, true);
612
613         _bindings.push_back(binding);
614
615         form->addRow(group);
616         _decoder_forms.push_back(group);
617 }
618
619 QComboBox* DecodeTrace::create_channel_selector(
620         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
621         const srd_channel *const pdch)
622 {
623         assert(dec);
624
625         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
626
627         assert(_decoder_stack);
628         const auto channel_iter = dec->channels().find(pdch);
629
630         QComboBox *selector = new QComboBox(parent);
631
632         selector->addItem("-", qVariantFromValue((void*)NULL));
633
634         if (channel_iter == dec->channels().end())
635                 selector->setCurrentIndex(0);
636
637         for(size_t i = 0; i < sigs.size(); i++) {
638                 const shared_ptr<view::Signal> s(sigs[i]);
639                 assert(s);
640
641                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
642                 {
643                         selector->addItem(s->get_name(),
644                                 qVariantFromValue((void*)s.get()));
645                         if ((*channel_iter).second == s)
646                                 selector->setCurrentIndex(i + 1);
647                 }
648         }
649
650         return selector;
651 }
652
653 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
654 {
655         assert(dec);
656
657         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
658         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
659
660         for (const ChannelSelector &s : _channel_selectors)
661         {
662                 if(s._decoder != dec)
663                         break;
664
665                 const LogicSignal *const selection =
666                         (LogicSignal*)s._combo->itemData(
667                                 s._combo->currentIndex()).value<void*>();
668
669                 for (shared_ptr<Signal> sig : sigs)
670                         if(sig.get() == selection) {
671                                 channel_map[s._pdch] =
672                                         dynamic_pointer_cast<LogicSignal>(sig);
673                                 break;
674                         }
675         }
676
677         dec->set_channels(channel_map);
678 }
679
680 void DecodeTrace::commit_channels()
681 {
682         assert(_decoder_stack);
683         for (shared_ptr<data::decode::Decoder> dec : _decoder_stack->stack())
684                 commit_decoder_channels(dec);
685
686         _decoder_stack->begin_decode();
687 }
688
689 void DecodeTrace::on_new_decode_data()
690 {
691         if (_view)
692                 _view->update_viewport();
693 }
694
695 void DecodeTrace::delete_pressed()
696 {
697         on_delete();
698 }
699
700 void DecodeTrace::on_delete()
701 {
702         _session.remove_decode_signal(this);
703 }
704
705 void DecodeTrace::on_channel_selected(int)
706 {
707         commit_channels();
708 }
709
710 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
711 {
712         assert(decoder);
713         assert(_decoder_stack);
714         _decoder_stack->push(shared_ptr<data::decode::Decoder>(
715                 new data::decode::Decoder(decoder)));
716         _decoder_stack->begin_decode();
717
718         create_popup_form();
719 }
720
721 void DecodeTrace::on_delete_decoder(int index)
722 {
723         _decoder_stack->remove(index);
724
725         // Update the popup
726         create_popup_form();    
727
728         _decoder_stack->begin_decode();
729 }
730
731 void DecodeTrace::on_show_hide_decoder(int index)
732 {
733         using pv::data::decode::Decoder;
734
735         const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
736
737         // Find the decoder in the stack
738         auto iter = stack.cbegin();
739         for(int i = 0; i < index; i++, iter++)
740                 assert(iter != stack.end());
741
742         shared_ptr<Decoder> dec = *iter;
743         assert(dec);
744
745         const bool show = !dec->shown();
746         dec->show(show);
747
748         assert(index < (int)_decoder_forms.size());
749         _decoder_forms[index]->set_decoder_visible(show);
750
751         _view->update_viewport();
752 }
753
754 } // namespace view
755 } // namespace pv