]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
Replaced duplicated code with Trace::get_y
[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         int y = get_y();
185         pair<uint64_t, uint64_t> sample_range = get_sample_range(left, right);
186
187         assert(_decoder_stack);
188         const vector<Row> rows(_decoder_stack->get_visible_rows());
189
190         _visible_rows.clear();
191         for (size_t i = 0; i < rows.size(); i++)
192         {
193                 const Row &row = rows[i];
194
195                 size_t base_colour = 0x13579BDF;
196                 boost::hash_combine(base_colour, this);
197                 boost::hash_combine(base_colour, row.decoder());
198                 boost::hash_combine(base_colour, row.row());
199                 base_colour >>= 16;
200
201                 vector<Annotation> annotations;
202                 _decoder_stack->get_annotation_subset(annotations, row,
203                         sample_range.first, sample_range.second);
204                 if (!annotations.empty()) {
205                         for (const Annotation &a : annotations)
206                                 draw_annotation(a, p, get_text_colour(),
207                                         annotation_height, left, right, y,
208                                         base_colour);
209                         y += _row_height;
210
211                         _visible_rows.push_back(rows[i]);
212                 }
213         }
214
215         // Draw the hatching
216         draw_unresolved_period(p, annotation_height, left, right);
217 }
218
219 void DecodeTrace::paint_fore(QPainter &p, int left, int right)
220 {
221         using namespace pv::data::decode;
222
223         (void)right;
224
225         assert(_row_height);
226
227         for (size_t i = 0; i < _visible_rows.size(); i++)
228         {
229                 const int y = i * _row_height + get_y();
230
231                 p.setPen(QPen(Qt::NoPen));
232                 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
233
234                 if (i != 0)
235                 {
236                         const QPointF points[] = {
237                                 QPointF(left, y - ArrowSize),
238                                 QPointF(left + ArrowSize, y),
239                                 QPointF(left, y + ArrowSize)
240                         };
241                         p.drawPolygon(points, countof(points));
242                 }
243
244                 const QRect r(left + ArrowSize * 2, y - _row_height / 2,
245                         right - left, _row_height);
246                 const QString h(_visible_rows[i].title());
247                 const int f = Qt::AlignLeft | Qt::AlignVCenter |
248                         Qt::TextDontClip;
249
250                 // Draw the outline
251                 p.setPen(QApplication::palette().color(QPalette::Base));
252                 for (int dx = -1; dx <= 1; dx++)
253                         for (int dy = -1; dy <= 1; dy++)
254                                 if (dx != 0 && dy != 0)
255                                         p.drawText(r.translated(dx, dy), f, h);
256
257                 // Draw the text
258                 p.setPen(QApplication::palette().color(QPalette::WindowText));
259                 p.drawText(r, f, h);
260         }
261 }
262
263 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
264 {
265         using pv::data::decode::Decoder;
266
267         assert(form);
268         assert(parent);
269         assert(_decoder_stack);
270
271         // Add the standard options
272         Trace::populate_popup_form(parent, form);
273
274         // Add the decoder options
275         _bindings.clear();
276         _channel_selectors.clear();
277         _decoder_forms.clear();
278
279         const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
280
281         if (stack.empty())
282         {
283                 QLabel *const l = new QLabel(
284                         tr("<p><i>No decoders in the stack</i></p>"));
285                 l->setAlignment(Qt::AlignCenter);
286                 form->addRow(l);
287         }
288         else
289         {
290                 auto iter = stack.cbegin();
291                 for (int i = 0; i < (int)stack.size(); i++, iter++) {
292                         shared_ptr<Decoder> dec(*iter);
293                         create_decoder_form(i, dec, parent, form);
294                 }
295
296                 form->addRow(new QLabel(
297                         tr("<i>* Required channels</i>"), parent));
298         }
299
300         // Add stacking button
301         pv::widgets::DecoderMenu *const decoder_menu =
302                 new pv::widgets::DecoderMenu(parent);
303         connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
304                 this, SLOT(on_stack_decoder(srd_decoder*)));
305
306         QPushButton *const stack_button =
307                 new QPushButton(tr("Stack Decoder"), parent);
308         stack_button->setMenu(decoder_menu);
309
310         QHBoxLayout *stack_button_box = new QHBoxLayout;
311         stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
312         form->addRow(stack_button_box);
313 }
314
315 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
316 {
317         QMenu *const menu = Trace::create_context_menu(parent);
318
319         menu->addSeparator();
320
321         QAction *const del = new QAction(tr("Delete"), this);
322         del->setShortcuts(QKeySequence::Delete);
323         connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
324         menu->addAction(del);
325
326         return menu;
327 }
328
329 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
330         QPainter &p, QColor text_color, int h, int left, int right, int y,
331         size_t base_colour) const
332 {
333         double samples_per_pixel, pixels_offset;
334         tie(pixels_offset, samples_per_pixel) =
335                 get_pixels_offset_samples_per_pixel();
336
337         const double start = a.start_sample() / samples_per_pixel -
338                 pixels_offset;
339         const double end = a.end_sample() / samples_per_pixel -
340                 pixels_offset;
341
342         const size_t colour = (base_colour + a.format()) % countof(Colours);
343         const QColor &fill = Colours[colour];
344         const QColor &outline = OutlineColours[colour];
345
346         if (start > right + DrawPadding || end < left - DrawPadding)
347                 return;
348
349         if (a.start_sample() == a.end_sample())
350                 draw_instant(a, p, fill, outline, text_color, h,
351                         start, y);
352         else
353                 draw_range(a, p, fill, outline, text_color, h,
354                         start, end, y);
355 }
356
357 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
358         QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
359 {
360         const QString text = a.annotations().empty() ?
361                 QString() : a.annotations().back();
362         const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
363                 0.0) + h;
364         const QRectF rect(x - w / 2, y - h / 2, w, h);
365
366         p.setPen(outline);
367         p.setBrush(fill);
368         p.drawRoundedRect(rect, h / 2, h / 2);
369
370         p.setPen(text_color);
371         p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
372 }
373
374 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
375         QColor fill, QColor outline, QColor text_color, int h, double start,
376         double end, int y) const
377 {
378         const double top = y + .5 - h / 2;
379         const double bottom = y + .5 + h / 2;
380         const vector<QString> annotations = a.annotations();
381
382         p.setPen(outline);
383         p.setBrush(fill);
384
385         // If the two ends are within 1 pixel, draw a vertical line
386         if (start + 1.0 > end)
387         {
388                 p.drawLine(QPointF(start, top), QPointF(start, bottom));
389                 return;
390         }
391
392         const double cap_width = min((end - start) / 4, EndCapWidth);
393
394         QPointF pts[] = {
395                 QPointF(start, y + .5f),
396                 QPointF(start + cap_width, top),
397                 QPointF(end - cap_width, top),
398                 QPointF(end, y + .5f),
399                 QPointF(end - cap_width, bottom),
400                 QPointF(start + cap_width, bottom)
401         };
402
403         p.drawConvexPolygon(pts, countof(pts));
404
405         if (annotations.empty())
406                 return;
407
408         QRectF rect(start + cap_width, y - h / 2,
409                 end - start - cap_width * 2, h);
410         if (rect.width() <= 4)
411                 return;
412
413         p.setPen(text_color);
414
415         // Try to find an annotation that will fit
416         QString best_annotation;
417         int best_width = 0;
418
419         for (const QString &a : annotations) {
420                 const int w = p.boundingRect(QRectF(), 0, a).width();
421                 if (w <= rect.width() && w > best_width)
422                         best_annotation = a, best_width = w;
423         }
424
425         if (best_annotation.isEmpty())
426                 best_annotation = annotations.back();
427
428         // If not ellide the last in the list
429         p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
430                 best_annotation, Qt::ElideRight, rect.width()));
431 }
432
433 void DecodeTrace::draw_error(QPainter &p, const QString &message,
434         int left, int right)
435 {
436         const int y = get_y();
437
438         p.setPen(ErrorBgColour.darker());
439         p.setBrush(ErrorBgColour);
440
441         const QRectF bounding_rect =
442                 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
443         const QRectF text_rect = p.boundingRect(bounding_rect,
444                 Qt::AlignCenter, message);
445         const float r = text_rect.height() / 4;
446
447         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
448                 Qt::AbsoluteSize);
449
450         p.setPen(get_text_colour());
451         p.drawText(text_rect, message);
452 }
453
454 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
455         int right) const
456 {
457         using namespace pv::data;
458         using pv::data::decode::Decoder;
459
460         double samples_per_pixel, pixels_offset;
461
462         assert(_decoder_stack); 
463
464         shared_ptr<Logic> data;
465         shared_ptr<LogicSignal> logic_signal;
466
467         const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
468
469         // We get the logic data of the first channel in the list.
470         // This works because we are currently assuming all
471         // LogicSignals have the same data/snapshot
472         for (const shared_ptr<Decoder> &dec : stack)
473                 if (dec && !dec->channels().empty() &&
474                         ((logic_signal = (*dec->channels().begin()).second)) &&
475                         ((data = logic_signal->logic_data())))
476                         break;
477
478         if (!data || data->get_snapshots().empty())
479                 return;
480
481         const shared_ptr<LogicSnapshot> snapshot =
482                 data->get_snapshots().front();
483         assert(snapshot);
484         const int64_t sample_count = (int64_t)snapshot->get_sample_count();
485         if (sample_count == 0)
486                 return;
487
488         const int64_t samples_decoded = _decoder_stack->samples_decoded();
489         if (sample_count == samples_decoded)
490                 return;
491
492         const int y = get_y();
493
494         tie(pixels_offset, samples_per_pixel) =
495                 get_pixels_offset_samples_per_pixel();
496
497         const double start = max(samples_decoded /
498                 samples_per_pixel - pixels_offset, left - 1.0);
499         const double end = min(sample_count / samples_per_pixel -
500                 pixels_offset, right + 1.0);
501         const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
502
503         p.setPen(QPen(Qt::NoPen));
504         p.setBrush(Qt::white);
505         p.drawRect(no_decode_rect);
506
507         p.setPen(NoDecodeColour);
508         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
509         p.drawRect(no_decode_rect);
510 }
511
512 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
513 {
514         assert(_view);
515         assert(_decoder_stack);
516
517         const double scale = _view->scale();
518         assert(scale > 0);
519
520         const double pixels_offset =
521                 (_view->offset() - _decoder_stack->get_start_time()) / scale;
522
523         double samplerate = _decoder_stack->samplerate();
524
525         // Show sample rate as 1Hz when it is unknown
526         if (samplerate == 0.0)
527                 samplerate = 1.0;
528
529         return make_pair(pixels_offset, samplerate * scale);
530 }
531
532 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
533         int x_start, int x_end) const
534 {
535         double samples_per_pixel, pixels_offset;
536         tie(pixels_offset, samples_per_pixel) =
537                 get_pixels_offset_samples_per_pixel();
538
539         const uint64_t start = (uint64_t)max(
540                 (x_start + pixels_offset) * samples_per_pixel, 0.0);
541         const uint64_t end = (uint64_t)max(
542                 (x_end + pixels_offset) * samples_per_pixel, 0.0);
543
544         return make_pair(start, end);
545 }
546
547 int DecodeTrace::get_row_at_point(const QPoint &point)
548 {
549         if (!_row_height)
550                 return -1;
551
552         const int row = (point.y() - get_y() + _row_height / 2) / _row_height;
553         if (row < 0 || row >= (int)_visible_rows.size())
554                 return -1;
555
556         return row;
557 }
558
559 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
560 {
561         using namespace pv::data::decode;
562
563         if (!enabled())
564                 return QString();
565
566         const pair<uint64_t, uint64_t> sample_range =
567                 get_sample_range(point.x(), point.x() + 1);
568         const int row = get_row_at_point(point);
569         if (row < 0)
570                 return QString();
571
572         vector<pv::data::decode::Annotation> annotations;
573
574         assert(_decoder_stack);
575         _decoder_stack->get_annotation_subset(annotations, _visible_rows[row],
576                 sample_range.first, sample_range.second);
577
578         return (annotations.empty()) ?
579                 QString() : annotations[0].annotations().front();
580 }
581
582 void DecodeTrace::hide_hover_annotation()
583 {
584         QToolTip::hideText();
585 }
586
587 void DecodeTrace::hover_point_changed()
588 {
589         QPoint hp = _view->hover_point();
590         QString ann = get_annotation_at_point(hp);
591
592         assert(_view);
593         assert(_row_height);
594
595         if (ann.isEmpty()) {
596                 hide_hover_annotation();
597                 return;
598         }
599
600         const int hover_row = get_row_at_point(hp);
601
602         QFontMetrics m(QToolTip::font());
603         const QRect text_size = m.boundingRect(QRect(), 0, ann);
604
605         // This is OS-specific and unfortunately we can't query it, so
606         // use an approximation to at least try to minimize the error.
607         const int padding = 8;
608
609         // Make sure the tool tip doesn't overlap with the mouse cursor.
610         // If it did, the tool tip would constantly hide and re-appear.
611         // We also push it up by one row so that it appears above the
612         // decode trace, not below.
613         hp.setX(hp.x() - (text_size.width() / 2) - padding);
614
615         hp.setY(get_y() - (_row_height / 2) + (hover_row * _row_height)
616                 - _row_height - text_size.height());
617
618         QToolTip::showText(_view->viewport()->mapToGlobal(hp), ann);
619 }
620
621 void DecodeTrace::create_decoder_form(int index,
622         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
623         QFormLayout *form)
624 {
625         const GSList *l;
626
627         assert(dec);
628         const srd_decoder *const decoder = dec->decoder();
629         assert(decoder);
630
631         pv::widgets::DecoderGroupBox *const group =
632                 new pv::widgets::DecoderGroupBox(
633                         QString::fromUtf8(decoder->name));
634         group->set_decoder_visible(dec->shown());
635
636         _delete_mapper.setMapping(group, index);
637         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
638
639         _show_hide_mapper.setMapping(group, index);
640         connect(group, SIGNAL(show_hide_decoder()),
641                 &_show_hide_mapper, SLOT(map()));
642
643         QFormLayout *const decoder_form = new QFormLayout;
644         group->add_layout(decoder_form);
645
646         // Add the mandatory channels
647         for(l = decoder->channels; l; l = l->next) {
648                 const struct srd_channel *const pdch =
649                         (struct srd_channel *)l->data;
650                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
651                 connect(combo, SIGNAL(currentIndexChanged(int)),
652                         this, SLOT(on_channel_selected(int)));
653                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
654                         .arg(QString::fromUtf8(pdch->name))
655                         .arg(QString::fromUtf8(pdch->desc)), combo);
656
657                 const ChannelSelector s = {combo, dec, pdch};
658                 _channel_selectors.push_back(s);
659         }
660
661         // Add the optional channels
662         for(l = decoder->opt_channels; l; l = l->next) {
663                 const struct srd_channel *const pdch =
664                         (struct srd_channel *)l->data;
665                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
666                 connect(combo, SIGNAL(currentIndexChanged(int)),
667                         this, SLOT(on_channel_selected(int)));
668                 decoder_form->addRow(tr("<b>%1</b> (%2)")
669                         .arg(QString::fromUtf8(pdch->name))
670                         .arg(QString::fromUtf8(pdch->desc)), combo);
671
672                 const ChannelSelector s = {combo, dec, pdch};
673                 _channel_selectors.push_back(s);
674         }
675
676         // Add the options
677         shared_ptr<prop::binding::DecoderOptions> binding(
678                 new prop::binding::DecoderOptions(_decoder_stack, dec));
679         binding->add_properties_to_form(decoder_form, true);
680
681         _bindings.push_back(binding);
682
683         form->addRow(group);
684         _decoder_forms.push_back(group);
685 }
686
687 QComboBox* DecodeTrace::create_channel_selector(
688         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
689         const srd_channel *const pdch)
690 {
691         assert(dec);
692
693         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
694
695         assert(_decoder_stack);
696         const auto channel_iter = dec->channels().find(pdch);
697
698         QComboBox *selector = new QComboBox(parent);
699
700         selector->addItem("-", qVariantFromValue((void*)NULL));
701
702         if (channel_iter == dec->channels().end())
703                 selector->setCurrentIndex(0);
704
705         for(size_t i = 0; i < sigs.size(); i++) {
706                 const shared_ptr<view::Signal> s(sigs[i]);
707                 assert(s);
708
709                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
710                 {
711                         selector->addItem(s->get_name(),
712                                 qVariantFromValue((void*)s.get()));
713                         if ((*channel_iter).second == s)
714                                 selector->setCurrentIndex(i + 1);
715                 }
716         }
717
718         return selector;
719 }
720
721 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
722 {
723         assert(dec);
724
725         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
726         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
727
728         for (const ChannelSelector &s : _channel_selectors)
729         {
730                 if(s._decoder != dec)
731                         break;
732
733                 const LogicSignal *const selection =
734                         (LogicSignal*)s._combo->itemData(
735                                 s._combo->currentIndex()).value<void*>();
736
737                 for (shared_ptr<Signal> sig : sigs)
738                         if(sig.get() == selection) {
739                                 channel_map[s._pdch] =
740                                         dynamic_pointer_cast<LogicSignal>(sig);
741                                 break;
742                         }
743         }
744
745         dec->set_channels(channel_map);
746 }
747
748 void DecodeTrace::commit_channels()
749 {
750         assert(_decoder_stack);
751         for (shared_ptr<data::decode::Decoder> dec : _decoder_stack->stack())
752                 commit_decoder_channels(dec);
753
754         _decoder_stack->begin_decode();
755 }
756
757 void DecodeTrace::on_new_decode_data()
758 {
759         if (_view)
760                 _view->update_viewport();
761 }
762
763 void DecodeTrace::delete_pressed()
764 {
765         on_delete();
766 }
767
768 void DecodeTrace::on_delete()
769 {
770         _session.remove_decode_signal(this);
771 }
772
773 void DecodeTrace::on_channel_selected(int)
774 {
775         commit_channels();
776 }
777
778 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
779 {
780         assert(decoder);
781         assert(_decoder_stack);
782         _decoder_stack->push(shared_ptr<data::decode::Decoder>(
783                 new data::decode::Decoder(decoder)));
784         _decoder_stack->begin_decode();
785
786         create_popup_form();
787 }
788
789 void DecodeTrace::on_delete_decoder(int index)
790 {
791         _decoder_stack->remove(index);
792
793         // Update the popup
794         create_popup_form();    
795
796         _decoder_stack->begin_decode();
797 }
798
799 void DecodeTrace::on_show_hide_decoder(int index)
800 {
801         using pv::data::decode::Decoder;
802
803         const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
804
805         // Find the decoder in the stack
806         auto iter = stack.cbegin();
807         for(int i = 0; i < index; i++, iter++)
808                 assert(iter != stack.end());
809
810         shared_ptr<Decoder> dec = *iter;
811         assert(dec);
812
813         const bool show = !dec->shown();
814         dec->show(show);
815
816         assert(index < (int)_decoder_forms.size());
817         _decoder_forms[index]->set_decoder_visible(show);
818
819         _view->update_viewport();
820 }
821
822 } // namespace view
823 } // namespace pv