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