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