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