]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
DecodeTrace: Tidied up get_sample_range
[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(
536         int x_start, int x_end) const
537 {
538         const double samples_per_pixel = get_samples_per_pixel();
539         const double pixels_offset = get_pixels_offset();
540
541         const uint64_t start = (uint64_t)max(
542                 (x_start + pixels_offset) * samples_per_pixel, 0.0);
543         const uint64_t end = (uint64_t)max(
544                 (x_end + pixels_offset) * samples_per_pixel, 0.0);
545
546         return make_pair(start, end);
547 }
548
549 int DecodeTrace::get_row_at_point(const QPoint &point)
550 {
551         if (!_row_height)
552                 return -1;
553
554         const int row = (point.y() - get_y() + _row_height / 2) / _row_height;
555         if (row < 0 || row >= (int)_visible_rows.size())
556                 return -1;
557
558         return row;
559 }
560
561 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
562 {
563         using namespace pv::data::decode;
564
565         if (!enabled())
566                 return QString();
567
568         const pair<uint64_t, uint64_t> sample_range =
569                 get_sample_range(point.x(), point.x() + 1);
570         const int row = get_row_at_point(point);
571         if (row < 0)
572                 return QString();
573
574         vector<pv::data::decode::Annotation> annotations;
575
576         assert(_decoder_stack);
577         _decoder_stack->get_annotation_subset(annotations, _visible_rows[row],
578                 sample_range.first, sample_range.second);
579
580         return (annotations.empty()) ?
581                 QString() : annotations[0].annotations().front();
582 }
583
584 void DecodeTrace::hide_hover_annotation()
585 {
586         QToolTip::hideText();
587 }
588
589 void DecodeTrace::hover_point_changed()
590 {
591         QPoint hp = _view->hover_point();
592         QString ann = get_annotation_at_point(hp);
593
594         assert(_view);
595         assert(_row_height);
596
597         if (ann.isEmpty()) {
598                 hide_hover_annotation();
599                 return;
600         }
601
602         const int hover_row = get_row_at_point(hp);
603
604         QFontMetrics m(QToolTip::font());
605         const QRect text_size = m.boundingRect(QRect(), 0, ann);
606
607         // This is OS-specific and unfortunately we can't query it, so
608         // use an approximation to at least try to minimize the error.
609         const int padding = 8;
610
611         // Make sure the tool tip doesn't overlap with the mouse cursor.
612         // If it did, the tool tip would constantly hide and re-appear.
613         // We also push it up by one row so that it appears above the
614         // decode trace, not below.
615         hp.setX(hp.x() - (text_size.width() / 2) - padding);
616
617         hp.setY(get_y() - (_row_height / 2) + (hover_row * _row_height)
618                 - _row_height - text_size.height());
619
620         QToolTip::showText(_view->viewport()->mapToGlobal(hp), ann);
621 }
622
623 void DecodeTrace::create_decoder_form(int index,
624         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
625         QFormLayout *form)
626 {
627         const GSList *l;
628
629         assert(dec);
630         const srd_decoder *const decoder = dec->decoder();
631         assert(decoder);
632
633         pv::widgets::DecoderGroupBox *const group =
634                 new pv::widgets::DecoderGroupBox(
635                         QString::fromUtf8(decoder->name));
636         group->set_decoder_visible(dec->shown());
637
638         _delete_mapper.setMapping(group, index);
639         connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
640
641         _show_hide_mapper.setMapping(group, index);
642         connect(group, SIGNAL(show_hide_decoder()),
643                 &_show_hide_mapper, SLOT(map()));
644
645         QFormLayout *const decoder_form = new QFormLayout;
646         group->add_layout(decoder_form);
647
648         // Add the mandatory channels
649         for(l = decoder->channels; l; l = l->next) {
650                 const struct srd_channel *const pdch =
651                         (struct srd_channel *)l->data;
652                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
653                 connect(combo, SIGNAL(currentIndexChanged(int)),
654                         this, SLOT(on_channel_selected(int)));
655                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
656                         .arg(QString::fromUtf8(pdch->name))
657                         .arg(QString::fromUtf8(pdch->desc)), combo);
658
659                 const ChannelSelector s = {combo, dec, pdch};
660                 _channel_selectors.push_back(s);
661         }
662
663         // Add the optional channels
664         for(l = decoder->opt_channels; l; l = l->next) {
665                 const struct srd_channel *const pdch =
666                         (struct srd_channel *)l->data;
667                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
668                 connect(combo, SIGNAL(currentIndexChanged(int)),
669                         this, SLOT(on_channel_selected(int)));
670                 decoder_form->addRow(tr("<b>%1</b> (%2)")
671                         .arg(QString::fromUtf8(pdch->name))
672                         .arg(QString::fromUtf8(pdch->desc)), combo);
673
674                 const ChannelSelector s = {combo, dec, pdch};
675                 _channel_selectors.push_back(s);
676         }
677
678         // Add the options
679         shared_ptr<prop::binding::DecoderOptions> binding(
680                 new prop::binding::DecoderOptions(_decoder_stack, dec));
681         binding->add_properties_to_form(decoder_form, true);
682
683         _bindings.push_back(binding);
684
685         form->addRow(group);
686         _decoder_forms.push_back(group);
687 }
688
689 QComboBox* DecodeTrace::create_channel_selector(
690         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
691         const srd_channel *const pdch)
692 {
693         assert(dec);
694
695         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
696
697         assert(_decoder_stack);
698         const auto channel_iter = dec->channels().find(pdch);
699
700         QComboBox *selector = new QComboBox(parent);
701
702         selector->addItem("-", qVariantFromValue((void*)NULL));
703
704         if (channel_iter == dec->channels().end())
705                 selector->setCurrentIndex(0);
706
707         for(size_t i = 0; i < sigs.size(); i++) {
708                 const shared_ptr<view::Signal> s(sigs[i]);
709                 assert(s);
710
711                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
712                 {
713                         selector->addItem(s->get_name(),
714                                 qVariantFromValue((void*)s.get()));
715                         if ((*channel_iter).second == s)
716                                 selector->setCurrentIndex(i + 1);
717                 }
718         }
719
720         return selector;
721 }
722
723 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
724 {
725         assert(dec);
726
727         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
728         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
729
730         for (const ChannelSelector &s : _channel_selectors)
731         {
732                 if(s._decoder != dec)
733                         break;
734
735                 const LogicSignal *const selection =
736                         (LogicSignal*)s._combo->itemData(
737                                 s._combo->currentIndex()).value<void*>();
738
739                 for (shared_ptr<Signal> sig : sigs)
740                         if(sig.get() == selection) {
741                                 channel_map[s._pdch] =
742                                         dynamic_pointer_cast<LogicSignal>(sig);
743                                 break;
744                         }
745         }
746
747         dec->set_channels(channel_map);
748 }
749
750 void DecodeTrace::commit_channels()
751 {
752         assert(_decoder_stack);
753         for (shared_ptr<data::decode::Decoder> dec : _decoder_stack->stack())
754                 commit_decoder_channels(dec);
755
756         _decoder_stack->begin_decode();
757 }
758
759 void DecodeTrace::on_new_decode_data()
760 {
761         if (_view)
762                 _view->update_viewport();
763 }
764
765 void DecodeTrace::delete_pressed()
766 {
767         on_delete();
768 }
769
770 void DecodeTrace::on_delete()
771 {
772         _session.remove_decode_signal(this);
773 }
774
775 void DecodeTrace::on_channel_selected(int)
776 {
777         commit_channels();
778 }
779
780 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
781 {
782         assert(decoder);
783         assert(_decoder_stack);
784         _decoder_stack->push(shared_ptr<data::decode::Decoder>(
785                 new data::decode::Decoder(decoder)));
786         _decoder_stack->begin_decode();
787
788         create_popup_form();
789 }
790
791 void DecodeTrace::on_delete_decoder(int index)
792 {
793         _decoder_stack->remove(index);
794
795         // Update the popup
796         create_popup_form();    
797
798         _decoder_stack->begin_decode();
799 }
800
801 void DecodeTrace::on_show_hide_decoder(int index)
802 {
803         using pv::data::decode::Decoder;
804
805         const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
806
807         // Find the decoder in the stack
808         auto iter = stack.cbegin();
809         for(int i = 0; i < index; i++, iter++)
810                 assert(iter != stack.end());
811
812         shared_ptr<Decoder> dec = *iter;
813         assert(dec);
814
815         const bool show = !dec->shown();
816         dec->show(show);
817
818         assert(index < (int)_decoder_forms.size());
819         _decoder_forms[index]->set_decoder_visible(show);
820
821         _view->update_viewport();
822 }
823
824 } // namespace view
825 } // namespace pv