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