]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
Replace View ownership of traces with RowItemOwner
[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(_owner);
509         assert(_decoder_stack);
510
511         const View *view = _owner->view();
512         assert(view);
513
514         const double scale = view->scale();
515         assert(scale > 0);
516
517         const double pixels_offset =
518                 (view->offset() - _decoder_stack->get_start_time()) / scale;
519
520         double samplerate = _decoder_stack->samplerate();
521
522         // Show sample rate as 1Hz when it is unknown
523         if (samplerate == 0.0)
524                 samplerate = 1.0;
525
526         return make_pair(pixels_offset, samplerate * scale);
527 }
528
529 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
530         int x_start, int x_end) const
531 {
532         double samples_per_pixel, pixels_offset;
533         tie(pixels_offset, samples_per_pixel) =
534                 get_pixels_offset_samples_per_pixel();
535
536         const uint64_t start = (uint64_t)max(
537                 (x_start + pixels_offset) * samples_per_pixel, 0.0);
538         const uint64_t end = (uint64_t)max(
539                 (x_end + pixels_offset) * samples_per_pixel, 0.0);
540
541         return make_pair(start, end);
542 }
543
544 int DecodeTrace::get_row_at_point(const QPoint &point)
545 {
546         if (!_row_height)
547                 return -1;
548
549         const int row = (point.y() - get_y() + _row_height / 2) / _row_height;
550         if (row < 0 || row >= (int)_visible_rows.size())
551                 return -1;
552
553         return row;
554 }
555
556 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
557 {
558         using namespace pv::data::decode;
559
560         if (!enabled())
561                 return QString();
562
563         const pair<uint64_t, uint64_t> sample_range =
564                 get_sample_range(point.x(), point.x() + 1);
565         const int row = get_row_at_point(point);
566         if (row < 0)
567                 return QString();
568
569         vector<pv::data::decode::Annotation> annotations;
570
571         assert(_decoder_stack);
572         _decoder_stack->get_annotation_subset(annotations, _visible_rows[row],
573                 sample_range.first, sample_range.second);
574
575         return (annotations.empty()) ?
576                 QString() : annotations[0].annotations().front();
577 }
578
579 void DecodeTrace::hide_hover_annotation()
580 {
581         QToolTip::hideText();
582 }
583
584 void DecodeTrace::hover_point_changed()
585 {
586         assert(_owner);
587
588         const View *const view = _owner->view();
589         assert(view);
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->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 (_owner)
762                 _owner->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         _owner->update_viewport();
822 }
823
824 } // namespace view
825 } // namespace pv