]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
Make member variable underscores a suffix instead of a prefix
[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_visual_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_visual_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_visual_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_visual_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_visual_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_visual_y() + row_height_ / 2) /
566                 row_height_;
567         if (row < 0 || 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::hide_hover_annotation()
597 {
598         QToolTip::hideText();
599 }
600
601 void DecodeTrace::hover_point_changed()
602 {
603         assert(owner_);
604
605         const View *const view = owner_->view();
606         assert(view);
607
608         QPoint hp = view->hover_point();
609         QString ann = get_annotation_at_point(hp);
610
611         assert(view);
612         assert(row_height_);
613
614         if (ann.isEmpty()) {
615                 hide_hover_annotation();
616                 return;
617         }
618
619         const int hover_row = get_row_at_point(hp);
620
621         QFontMetrics m(QToolTip::font());
622         const QRect text_size = m.boundingRect(QRect(), 0, ann);
623
624         // This is OS-specific and unfortunately we can't query it, so
625         // use an approximation to at least try to minimize the error.
626         const int padding = 8;
627
628         // Make sure the tool tip doesn't overlap with the mouse cursor.
629         // If it did, the tool tip would constantly hide and re-appear.
630         // We also push it up by one row so that it appears above the
631         // decode trace, not below.
632         hp.setX(hp.x() - (text_size.width() / 2) - padding);
633
634         hp.setY(get_visual_y() - (row_height_ / 2) +
635                 (hover_row * row_height_) -
636                 row_height_ - text_size.height());
637
638         QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
639 }
640
641 void DecodeTrace::create_decoder_form(int index,
642         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
643         QFormLayout *form)
644 {
645         const GSList *l;
646
647         assert(dec);
648         const srd_decoder *const decoder = dec->decoder();
649         assert(decoder);
650
651         pv::widgets::DecoderGroupBox *const group =
652                 new pv::widgets::DecoderGroupBox(
653                         QString::fromUtf8(decoder->name));
654         group->set_decoder_visible(dec->shown());
655
656         delete_mapper_.setMapping(group, index);
657         connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
658
659         show_hide_mapper_.setMapping(group, index);
660         connect(group, SIGNAL(show_hide_decoder()),
661                 &show_hide_mapper_, SLOT(map()));
662
663         QFormLayout *const decoder_form = new QFormLayout;
664         group->add_layout(decoder_form);
665
666         // Add the mandatory channels
667         for(l = decoder->channels; l; l = l->next) {
668                 const struct srd_channel *const pdch =
669                         (struct srd_channel *)l->data;
670                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
671                 connect(combo, SIGNAL(currentIndexChanged(int)),
672                         this, SLOT(on_channel_selected(int)));
673                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
674                         .arg(QString::fromUtf8(pdch->name))
675                         .arg(QString::fromUtf8(pdch->desc)), combo);
676
677                 const ChannelSelector s = {combo, dec, pdch};
678                 channel_selectors_.push_back(s);
679         }
680
681         // Add the optional channels
682         for(l = decoder->opt_channels; l; l = l->next) {
683                 const struct srd_channel *const pdch =
684                         (struct srd_channel *)l->data;
685                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
686                 connect(combo, SIGNAL(currentIndexChanged(int)),
687                         this, SLOT(on_channel_selected(int)));
688                 decoder_form->addRow(tr("<b>%1</b> (%2)")
689                         .arg(QString::fromUtf8(pdch->name))
690                         .arg(QString::fromUtf8(pdch->desc)), combo);
691
692                 const ChannelSelector s = {combo, dec, pdch};
693                 channel_selectors_.push_back(s);
694         }
695
696         // Add the options
697         shared_ptr<prop::binding::DecoderOptions> binding(
698                 new prop::binding::DecoderOptions(decoder_stack_, dec));
699         binding->add_properties_to_form(decoder_form, true);
700
701         bindings_.push_back(binding);
702
703         form->addRow(group);
704         decoder_forms_.push_back(group);
705 }
706
707 QComboBox* DecodeTrace::create_channel_selector(
708         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
709         const srd_channel *const pdch)
710 {
711         assert(dec);
712
713         shared_lock<shared_mutex> lock(session_.signals_mutex());
714         const vector< shared_ptr<Signal> > &sigs(session_.signals());
715
716         assert(decoder_stack_);
717         const auto channel_iter = dec->channels().find(pdch);
718
719         QComboBox *selector = new QComboBox(parent);
720
721         selector->addItem("-", qVariantFromValue((void*)NULL));
722
723         if (channel_iter == dec->channels().end())
724                 selector->setCurrentIndex(0);
725
726         for(size_t i = 0; i < sigs.size(); i++) {
727                 const shared_ptr<view::Signal> s(sigs[i]);
728                 assert(s);
729
730                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
731                 {
732                         selector->addItem(s->name(),
733                                 qVariantFromValue((void*)s.get()));
734                         if ((*channel_iter).second == s)
735                                 selector->setCurrentIndex(i + 1);
736                 }
737         }
738
739         return selector;
740 }
741
742 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
743 {
744         assert(dec);
745
746         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
747
748         shared_lock<shared_mutex> lock(session_.signals_mutex());
749         const vector< shared_ptr<Signal> > &sigs(session_.signals());
750
751         for (const ChannelSelector &s : channel_selectors_)
752         {
753                 if(s.decoder_ != dec)
754                         break;
755
756                 const LogicSignal *const selection =
757                         (LogicSignal*)s.combo_->itemData(
758                                 s.combo_->currentIndex()).value<void*>();
759
760                 for (shared_ptr<Signal> sig : sigs)
761                         if(sig.get() == selection) {
762                                 channel_map[s.pdch_] =
763                                         dynamic_pointer_cast<LogicSignal>(sig);
764                                 break;
765                         }
766         }
767
768         dec->set_channels(channel_map);
769 }
770
771 void DecodeTrace::commit_channels()
772 {
773         assert(decoder_stack_);
774         for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
775                 commit_decoder_channels(dec);
776
777         decoder_stack_->begin_decode();
778 }
779
780 void DecodeTrace::on_new_decode_data()
781 {
782         if (owner_)
783                 owner_->appearance_changed(false, true);
784 }
785
786 void DecodeTrace::delete_pressed()
787 {
788         on_delete();
789 }
790
791 void DecodeTrace::on_delete()
792 {
793         session_.remove_decode_signal(this);
794 }
795
796 void DecodeTrace::on_channel_selected(int)
797 {
798         commit_channels();
799 }
800
801 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
802 {
803         assert(decoder);
804         assert(decoder_stack_);
805         decoder_stack_->push(shared_ptr<data::decode::Decoder>(
806                 new data::decode::Decoder(decoder)));
807         decoder_stack_->begin_decode();
808
809         create_popup_form();
810 }
811
812 void DecodeTrace::on_delete_decoder(int index)
813 {
814         decoder_stack_->remove(index);
815
816         // Update the popup
817         create_popup_form();    
818
819         decoder_stack_->begin_decode();
820 }
821
822 void DecodeTrace::on_show_hide_decoder(int index)
823 {
824         using pv::data::decode::Decoder;
825
826         const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
827
828         // Find the decoder in the stack
829         auto iter = stack.cbegin();
830         for(int i = 0; i < index; i++, iter++)
831                 assert(iter != stack.end());
832
833         shared_ptr<Decoder> dec = *iter;
834         assert(dec);
835
836         const bool show = !dec->shown();
837         dec->show(show);
838
839         assert(index < (int)decoder_forms_.size());
840         decoder_forms_[index]->set_decoder_visible(show);
841
842         if (owner_)
843                 owner_->appearance_changed(false, true);
844 }
845
846 } // namespace view
847 } // namespace pv