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