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