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