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