]> sigrok.org Git - pulseview.git/blame_incremental - pv/view/decodetrace.cpp
DecodeTrace: Refactor sample helpers and simplify method calls
[pulseview.git] / pv / view / decodetrace.cpp
... / ...
CommitLineData
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
21extern "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
50using std::dynamic_pointer_cast;
51using std::list;
52using std::make_pair;
53using std::max;
54using std::map;
55using std::min;
56using std::pair;
57using std::shared_ptr;
58using std::vector;
59
60namespace pv {
61namespace view {
62
63const 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
70const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
71const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
72
73const int DecodeTrace::ArrowSize = 4;
74const double DecodeTrace::EndCapWidth = 5;
75const int DecodeTrace::DrawPadding = 100;
76
77const 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
96const 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
115DecodeTrace::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
138bool DecodeTrace::enabled() const
139{
140 return true;
141}
142
143const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
144{
145 return _decoder_stack;
146}
147
148void DecodeTrace::set_view(pv::view::View *view)
149{
150 assert(view);
151 Trace::set_view(view);
152}
153
154void 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
160void 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
215void 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
259void 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
311QMenu* 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
325void 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
352void 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
369void 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
428void 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
449void 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
505double 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
516double 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
533pair<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
549void 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
615QComboBox* 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
649void 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
676void 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
685void DecodeTrace::on_new_decode_data()
686{
687 if (_view)
688 _view->update_viewport();
689}
690
691void DecodeTrace::delete_pressed()
692{
693 on_delete();
694}
695
696void DecodeTrace::on_delete()
697{
698 _session.remove_decode_signal(this);
699}
700
701void DecodeTrace::on_channel_selected(int)
702{
703 commit_channels();
704}
705
706void 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
717void 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
727void 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