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