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