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