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