]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
DecodeTrace: Remove hide_hover_annotation() as it's only used once
[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         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         const int row_height = (RowItemPaintParams::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 RowItemPaintParams &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 RowItemPaintParams &pp)
174 {
175         using namespace pv::data::decode;
176
177         text_height_ = RowItemPaintParams::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 RowItemPaintParams &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 RowItemPaintParams &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 RowItemPaintParams &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 row = (point.y() - get_visual_y() + row_height_ / 2) /
560                 row_height_;
561         if (row < 0 || row >= (int)visible_rows_.size())
562                 return -1;
563
564         return row;
565 }
566
567 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
568 {
569         using namespace pv::data::decode;
570
571         if (!enabled())
572                 return QString();
573
574         const pair<uint64_t, uint64_t> sample_range =
575                 get_sample_range(point.x(), point.x() + 1);
576         const int row = get_row_at_point(point);
577         if (row < 0)
578                 return QString();
579
580         vector<pv::data::decode::Annotation> annotations;
581
582         assert(decoder_stack_);
583         decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
584                 sample_range.first, sample_range.second);
585
586         return (annotations.empty()) ?
587                 QString() : annotations[0].annotations().front();
588 }
589
590 void DecodeTrace::hover_point_changed()
591 {
592         assert(owner_);
593
594         const View *const view = owner_->view();
595         assert(view);
596
597         QPoint hp = view->hover_point();
598         QString ann = get_annotation_at_point(hp);
599
600         assert(view);
601
602         if (!row_height_ || ann.isEmpty()) {
603                 QToolTip::hideText();
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_visual_y() - (row_height_ / 2) +
623                 (hover_row * row_height_) -
624                 row_height_ - text_size.height());
625
626         QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
627 }
628
629 void DecodeTrace::create_decoder_form(int index,
630         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
631         QFormLayout *form)
632 {
633         const GSList *l;
634
635         assert(dec);
636         const srd_decoder *const decoder = dec->decoder();
637         assert(decoder);
638
639         pv::widgets::DecoderGroupBox *const group =
640                 new pv::widgets::DecoderGroupBox(
641                         QString::fromUtf8(decoder->name));
642         group->set_decoder_visible(dec->shown());
643
644         delete_mapper_.setMapping(group, index);
645         connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
646
647         show_hide_mapper_.setMapping(group, index);
648         connect(group, SIGNAL(show_hide_decoder()),
649                 &show_hide_mapper_, SLOT(map()));
650
651         QFormLayout *const decoder_form = new QFormLayout;
652         group->add_layout(decoder_form);
653
654         // Add the mandatory channels
655         for(l = decoder->channels; l; l = l->next) {
656                 const struct srd_channel *const pdch =
657                         (struct srd_channel *)l->data;
658                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
659                 connect(combo, SIGNAL(currentIndexChanged(int)),
660                         this, SLOT(on_channel_selected(int)));
661                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
662                         .arg(QString::fromUtf8(pdch->name))
663                         .arg(QString::fromUtf8(pdch->desc)), combo);
664
665                 const ChannelSelector s = {combo, dec, pdch};
666                 channel_selectors_.push_back(s);
667         }
668
669         // Add the optional channels
670         for(l = decoder->opt_channels; l; l = l->next) {
671                 const struct srd_channel *const pdch =
672                         (struct srd_channel *)l->data;
673                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
674                 connect(combo, SIGNAL(currentIndexChanged(int)),
675                         this, SLOT(on_channel_selected(int)));
676                 decoder_form->addRow(tr("<b>%1</b> (%2)")
677                         .arg(QString::fromUtf8(pdch->name))
678                         .arg(QString::fromUtf8(pdch->desc)), combo);
679
680                 const ChannelSelector s = {combo, dec, pdch};
681                 channel_selectors_.push_back(s);
682         }
683
684         // Add the options
685         shared_ptr<prop::binding::DecoderOptions> binding(
686                 new prop::binding::DecoderOptions(decoder_stack_, dec));
687         binding->add_properties_to_form(decoder_form, true);
688
689         bindings_.push_back(binding);
690
691         form->addRow(group);
692         decoder_forms_.push_back(group);
693 }
694
695 QComboBox* DecodeTrace::create_channel_selector(
696         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
697         const srd_channel *const pdch)
698 {
699         assert(dec);
700
701         shared_lock<shared_mutex> lock(session_.signals_mutex());
702         const vector< shared_ptr<Signal> > &sigs(session_.signals());
703
704         assert(decoder_stack_);
705         const auto channel_iter = dec->channels().find(pdch);
706
707         QComboBox *selector = new QComboBox(parent);
708
709         selector->addItem("-", qVariantFromValue((void*)NULL));
710
711         if (channel_iter == dec->channels().end())
712                 selector->setCurrentIndex(0);
713
714         for(size_t i = 0; i < sigs.size(); i++) {
715                 const shared_ptr<view::Signal> s(sigs[i]);
716                 assert(s);
717
718                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
719                 {
720                         selector->addItem(s->name(),
721                                 qVariantFromValue((void*)s.get()));
722                         if ((*channel_iter).second == s)
723                                 selector->setCurrentIndex(i + 1);
724                 }
725         }
726
727         return selector;
728 }
729
730 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
731 {
732         assert(dec);
733
734         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
735
736         shared_lock<shared_mutex> lock(session_.signals_mutex());
737         const vector< shared_ptr<Signal> > &sigs(session_.signals());
738
739         for (const ChannelSelector &s : channel_selectors_)
740         {
741                 if(s.decoder_ != dec)
742                         break;
743
744                 const LogicSignal *const selection =
745                         (LogicSignal*)s.combo_->itemData(
746                                 s.combo_->currentIndex()).value<void*>();
747
748                 for (shared_ptr<Signal> sig : sigs)
749                         if(sig.get() == selection) {
750                                 channel_map[s.pdch_] =
751                                         dynamic_pointer_cast<LogicSignal>(sig);
752                                 break;
753                         }
754         }
755
756         dec->set_channels(channel_map);
757 }
758
759 void DecodeTrace::commit_channels()
760 {
761         assert(decoder_stack_);
762         for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
763                 commit_decoder_channels(dec);
764
765         decoder_stack_->begin_decode();
766 }
767
768 void DecodeTrace::on_new_decode_data()
769 {
770         if (owner_)
771                 owner_->appearance_changed(false, true);
772 }
773
774 void DecodeTrace::delete_pressed()
775 {
776         on_delete();
777 }
778
779 void DecodeTrace::on_delete()
780 {
781         session_.remove_decode_signal(this);
782 }
783
784 void DecodeTrace::on_channel_selected(int)
785 {
786         commit_channels();
787 }
788
789 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
790 {
791         assert(decoder);
792         assert(decoder_stack_);
793         decoder_stack_->push(shared_ptr<data::decode::Decoder>(
794                 new data::decode::Decoder(decoder)));
795         decoder_stack_->begin_decode();
796
797         create_popup_form();
798 }
799
800 void DecodeTrace::on_delete_decoder(int index)
801 {
802         decoder_stack_->remove(index);
803
804         // Update the popup
805         create_popup_form();    
806
807         decoder_stack_->begin_decode();
808 }
809
810 void DecodeTrace::on_show_hide_decoder(int index)
811 {
812         using pv::data::decode::Decoder;
813
814         const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
815
816         // Find the decoder in the stack
817         auto iter = stack.cbegin();
818         for(int i = 0; i < index; i++, iter++)
819                 assert(iter != stack.end());
820
821         shared_ptr<Decoder> dec = *iter;
822         assert(dec);
823
824         const bool show = !dec->shown();
825         dec->show(show);
826
827         assert(index < (int)decoder_forms_.size());
828         decoder_forms_[index]->set_decoder_visible(show);
829
830         if (owner_)
831                 owner_->appearance_changed(false, true);
832 }
833
834 } // namespace view
835 } // namespace pv