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