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