]> sigrok.org Git - pulseview.git/blame_incremental - pv/view/decodetrace.cpp
Add a spin box widget for timestamp values
[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.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/logicsegment.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
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::make_pair;
64using std::map;
65using std::min;
66using std::pair;
67using std::shared_ptr;
68using std::tie;
69using std::unordered_set;
70using std::vector;
71
72namespace pv {
73namespace view {
74
75const QColor DecodeTrace::DecodeColours[4] = {
76 QColor(0xEF, 0x29, 0x29), // Red
77 QColor(0xFC, 0xE9, 0x4F), // Yellow
78 QColor(0x8A, 0xE2, 0x34), // Green
79 QColor(0x72, 0x9F, 0xCF) // Blue
80};
81
82const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
83const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
84
85const int DecodeTrace::ArrowSize = 4;
86const double DecodeTrace::EndCapWidth = 5;
87const int DecodeTrace::DrawPadding = 100;
88
89const QColor DecodeTrace::Colours[16] = {
90 QColor(0xEF, 0x29, 0x29),
91 QColor(0xF6, 0x6A, 0x32),
92 QColor(0xFC, 0xAE, 0x3E),
93 QColor(0xFB, 0xCA, 0x47),
94 QColor(0xFC, 0xE9, 0x4F),
95 QColor(0xCD, 0xF0, 0x40),
96 QColor(0x8A, 0xE2, 0x34),
97 QColor(0x4E, 0xDC, 0x44),
98 QColor(0x55, 0xD7, 0x95),
99 QColor(0x64, 0xD1, 0xD2),
100 QColor(0x72, 0x9F, 0xCF),
101 QColor(0xD4, 0x76, 0xC4),
102 QColor(0x9D, 0x79, 0xB9),
103 QColor(0xAD, 0x7F, 0xA8),
104 QColor(0xC2, 0x62, 0x9B),
105 QColor(0xD7, 0x47, 0x6F)
106};
107
108const QColor DecodeTrace::OutlineColours[16] = {
109 QColor(0x77, 0x14, 0x14),
110 QColor(0x7B, 0x35, 0x19),
111 QColor(0x7E, 0x57, 0x1F),
112 QColor(0x7D, 0x65, 0x23),
113 QColor(0x7E, 0x74, 0x27),
114 QColor(0x66, 0x78, 0x20),
115 QColor(0x45, 0x71, 0x1A),
116 QColor(0x27, 0x6E, 0x22),
117 QColor(0x2A, 0x6B, 0x4A),
118 QColor(0x32, 0x68, 0x69),
119 QColor(0x39, 0x4F, 0x67),
120 QColor(0x6A, 0x3B, 0x62),
121 QColor(0x4E, 0x3C, 0x5C),
122 QColor(0x56, 0x3F, 0x54),
123 QColor(0x61, 0x31, 0x4D),
124 QColor(0x6B, 0x23, 0x37)
125};
126
127DecodeTrace::DecodeTrace(pv::Session &session,
128 std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
129 Trace(QString::fromUtf8(
130 decoder_stack->stack().front()->decoder()->name)),
131 session_(session),
132 decoder_stack_(decoder_stack),
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
149bool DecodeTrace::enabled() const
150{
151 return true;
152}
153
154const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
155{
156 return decoder_stack_;
157}
158
159pair<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 = (ViewItemPaintParams::text_height() * 6) / 4;
164 return make_pair(-row_height / 2, row_height * 7 / 2);
165}
166
167void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
168{
169 Trace::paint_back(p, pp);
170 paint_axis(p, pp, get_visual_y());
171}
172
173void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
174{
175 using namespace pv::data::decode;
176
177 const int text_height = ViewItemPaintParams::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, 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
227void 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 {
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
269void 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
321QMenu* 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
335void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
336 QPainter &p, int h, const ViewItemPaintParams &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, h, start, y);
357 else
358 draw_range(a, p, fill, outline, h, start, end, y);
359}
360
361void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
362 QColor fill, QColor outline, int h, double x, int y) const
363{
364 const QString text = a.annotations().empty() ?
365 QString() : a.annotations().back();
366 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
367 0.0) + h;
368 const QRectF rect(x - w / 2, y - h / 2, w, h);
369
370 p.setPen(outline);
371 p.setBrush(fill);
372 p.drawRoundedRect(rect, h / 2, h / 2);
373
374 p.setPen(Qt::black);
375 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
376}
377
378void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
379 QColor fill, QColor outline, int h, double start,
380 double end, int y) const
381{
382 const double top = y + .5 - h / 2;
383 const double bottom = y + .5 + h / 2;
384 const vector<QString> annotations = a.annotations();
385
386 p.setPen(outline);
387 p.setBrush(fill);
388
389 // If the two ends are within 1 pixel, draw a vertical line
390 if (start + 1.0 > end)
391 {
392 p.drawLine(QPointF(start, top), QPointF(start, bottom));
393 return;
394 }
395
396 const double cap_width = min((end - start) / 4, EndCapWidth);
397
398 QPointF pts[] = {
399 QPointF(start, y + .5f),
400 QPointF(start + cap_width, top),
401 QPointF(end - cap_width, top),
402 QPointF(end, y + .5f),
403 QPointF(end - cap_width, bottom),
404 QPointF(start + cap_width, bottom)
405 };
406
407 p.drawConvexPolygon(pts, countof(pts));
408
409 if (annotations.empty())
410 return;
411
412 QRectF rect(start + cap_width, y - h / 2,
413 end - start - cap_width * 2, h);
414 if (rect.width() <= 4)
415 return;
416
417 p.setPen(Qt::black);
418
419 // Try to find an annotation that will fit
420 QString best_annotation;
421 int best_width = 0;
422
423 for (const QString &a : annotations) {
424 const int w = p.boundingRect(QRectF(), 0, a).width();
425 if (w <= rect.width() && w > best_width)
426 best_annotation = a, best_width = w;
427 }
428
429 if (best_annotation.isEmpty())
430 best_annotation = annotations.back();
431
432 // If not ellide the last in the list
433 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
434 best_annotation, Qt::ElideRight, rect.width()));
435}
436
437void DecodeTrace::draw_error(QPainter &p, const QString &message,
438 const ViewItemPaintParams &pp)
439{
440 const int y = get_visual_y();
441
442 p.setPen(ErrorBgColour.darker());
443 p.setBrush(ErrorBgColour);
444
445 const QRectF bounding_rect =
446 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
447 const QRectF text_rect = p.boundingRect(bounding_rect,
448 Qt::AlignCenter, message);
449 const float r = text_rect.height() / 4;
450
451 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
452 Qt::AbsoluteSize);
453
454 p.setPen(Qt::black);
455 p.drawText(text_rect, message);
456}
457
458void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
459 int right) const
460{
461 using namespace pv::data;
462 using pv::data::decode::Decoder;
463
464 double samples_per_pixel, pixels_offset;
465
466 assert(decoder_stack_);
467
468 shared_ptr<Logic> data;
469 shared_ptr<LogicSignal> logic_signal;
470
471 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
472
473 // We get the logic data of the first channel in the list.
474 // This works because we are currently assuming all
475 // LogicSignals have the same data/segment
476 for (const shared_ptr<Decoder> &dec : stack)
477 if (dec && !dec->channels().empty() &&
478 ((logic_signal = (*dec->channels().begin()).second)) &&
479 ((data = logic_signal->logic_data())))
480 break;
481
482 if (!data || data->logic_segments().empty())
483 return;
484
485 const shared_ptr<LogicSegment> segment =
486 data->logic_segments().front();
487 assert(segment);
488 const int64_t sample_count = (int64_t)segment->get_sample_count();
489 if (sample_count == 0)
490 return;
491
492 const int64_t samples_decoded = decoder_stack_->samples_decoded();
493 if (sample_count == samples_decoded)
494 return;
495
496 const int y = get_visual_y();
497
498 tie(pixels_offset, samples_per_pixel) =
499 get_pixels_offset_samples_per_pixel();
500
501 const double start = max(samples_decoded /
502 samples_per_pixel - pixels_offset, left - 1.0);
503 const double end = min(sample_count / samples_per_pixel -
504 pixels_offset, right + 1.0);
505 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
506
507 p.setPen(QPen(Qt::NoPen));
508 p.setBrush(Qt::white);
509 p.drawRect(no_decode_rect);
510
511 p.setPen(NoDecodeColour);
512 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
513 p.drawRect(no_decode_rect);
514}
515
516pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
517{
518 assert(owner_);
519 assert(decoder_stack_);
520
521 const View *view = owner_->view();
522 assert(view);
523
524 const double scale = view->scale();
525 assert(scale > 0);
526
527 const double pixels_offset =
528 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
529
530 double samplerate = decoder_stack_->samplerate();
531
532 // Show sample rate as 1Hz when it is unknown
533 if (samplerate == 0.0)
534 samplerate = 1.0;
535
536 return make_pair(pixels_offset, samplerate * scale);
537}
538
539pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
540 int x_start, int x_end) const
541{
542 double samples_per_pixel, pixels_offset;
543 tie(pixels_offset, samples_per_pixel) =
544 get_pixels_offset_samples_per_pixel();
545
546 const uint64_t start = (uint64_t)max(
547 (x_start + pixels_offset) * samples_per_pixel, 0.0);
548 const uint64_t end = (uint64_t)max(
549 (x_end + pixels_offset) * samples_per_pixel, 0.0);
550
551 return make_pair(start, end);
552}
553
554int DecodeTrace::get_row_at_point(const QPoint &point)
555{
556 if (!row_height_)
557 return -1;
558
559 const int y = (point.y() - get_visual_y() + row_height_ / 2);
560
561 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
562 if (y < 0)
563 return -1;
564
565 const int row = y / row_height_;
566
567 if (row >= (int)visible_rows_.size())
568 return -1;
569
570 return row;
571}
572
573const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
574{
575 using namespace pv::data::decode;
576
577 if (!enabled())
578 return QString();
579
580 const pair<uint64_t, uint64_t> sample_range =
581 get_sample_range(point.x(), point.x() + 1);
582 const int row = get_row_at_point(point);
583 if (row < 0)
584 return QString();
585
586 vector<pv::data::decode::Annotation> annotations;
587
588 assert(decoder_stack_);
589 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
590 sample_range.first, sample_range.second);
591
592 return (annotations.empty()) ?
593 QString() : annotations[0].annotations().front();
594}
595
596void DecodeTrace::hover_point_changed()
597{
598 assert(owner_);
599
600 const View *const view = owner_->view();
601 assert(view);
602
603 QPoint hp = view->hover_point();
604 QString ann = get_annotation_at_point(hp);
605
606 assert(view);
607
608 if (!row_height_ || ann.isEmpty()) {
609 QToolTip::hideText();
610 return;
611 }
612
613 const int hover_row = get_row_at_point(hp);
614
615 QFontMetrics m(QToolTip::font());
616 const QRect text_size = m.boundingRect(QRect(), 0, ann);
617
618 // This is OS-specific and unfortunately we can't query it, so
619 // use an approximation to at least try to minimize the error.
620 const int padding = 8;
621
622 // Make sure the tool tip doesn't overlap with the mouse cursor.
623 // If it did, the tool tip would constantly hide and re-appear.
624 // We also push it up by one row so that it appears above the
625 // decode trace, not below.
626 hp.setX(hp.x() - (text_size.width() / 2) - padding);
627
628 hp.setY(get_visual_y() - (row_height_ / 2) +
629 (hover_row * row_height_) -
630 row_height_ - text_size.height() - padding);
631
632 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
633}
634
635void DecodeTrace::create_decoder_form(int index,
636 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
637 QFormLayout *form)
638{
639 const GSList *l;
640
641 assert(dec);
642 const srd_decoder *const decoder = dec->decoder();
643 assert(decoder);
644
645 const bool decoder_deletable = index > 0;
646
647 pv::widgets::DecoderGroupBox *const group =
648 new pv::widgets::DecoderGroupBox(
649 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
650 group->set_decoder_visible(dec->shown());
651
652 if (decoder_deletable) {
653 delete_mapper_.setMapping(group, index);
654 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
655 }
656
657 show_hide_mapper_.setMapping(group, index);
658 connect(group, SIGNAL(show_hide_decoder()),
659 &show_hide_mapper_, SLOT(map()));
660
661 QFormLayout *const decoder_form = new QFormLayout;
662 group->add_layout(decoder_form);
663
664 // Add the mandatory channels
665 for (l = decoder->channels; l; l = l->next) {
666 const struct srd_channel *const pdch =
667 (struct srd_channel *)l->data;
668 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
669 connect(combo, SIGNAL(currentIndexChanged(int)),
670 this, SLOT(on_channel_selected(int)));
671 decoder_form->addRow(tr("<b>%1</b> (%2) *")
672 .arg(QString::fromUtf8(pdch->name))
673 .arg(QString::fromUtf8(pdch->desc)), combo);
674
675 const ChannelSelector s = {combo, dec, pdch};
676 channel_selectors_.push_back(s);
677 }
678
679 // Add the optional channels
680 for (l = decoder->opt_channels; l; l = l->next) {
681 const struct srd_channel *const pdch =
682 (struct srd_channel *)l->data;
683 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
684 connect(combo, SIGNAL(currentIndexChanged(int)),
685 this, SLOT(on_channel_selected(int)));
686 decoder_form->addRow(tr("<b>%1</b> (%2)")
687 .arg(QString::fromUtf8(pdch->name))
688 .arg(QString::fromUtf8(pdch->desc)), combo);
689
690 const ChannelSelector s = {combo, dec, pdch};
691 channel_selectors_.push_back(s);
692 }
693
694 // Add the options
695 shared_ptr<binding::Decoder> binding(
696 new binding::Decoder(decoder_stack_, dec));
697 binding->add_properties_to_form(decoder_form, true);
698
699 bindings_.push_back(binding);
700
701 form->addRow(group);
702 decoder_forms_.push_back(group);
703}
704
705QComboBox* DecodeTrace::create_channel_selector(
706 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
707 const srd_channel *const pdch)
708{
709 assert(dec);
710
711 shared_lock<shared_mutex> lock(session_.signals_mutex());
712 const auto &sigs(session_.signals());
713
714 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
715 std::sort(sig_list.begin(), sig_list.end(),
716 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
717 return a->name().compare(b->name()) < 0; });
718
719 assert(decoder_stack_);
720 const auto channel_iter = dec->channels().find(pdch);
721
722 QComboBox *selector = new QComboBox(parent);
723
724 selector->addItem("-", qVariantFromValue((void*)nullptr));
725
726 if (channel_iter == dec->channels().end())
727 selector->setCurrentIndex(0);
728
729 for (const shared_ptr<view::Signal> &s : sig_list) {
730 assert(s);
731 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
732 {
733 selector->addItem(s->name(),
734 qVariantFromValue((void*)s.get()));
735 if ((*channel_iter).second == s)
736 selector->setCurrentIndex(
737 selector->count() - 1);
738 }
739 }
740
741 return selector;
742}
743
744void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
745{
746 assert(dec);
747
748 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
749
750 shared_lock<shared_mutex> lock(session_.signals_mutex());
751 const unordered_set< shared_ptr<Signal> > &sigs(session_.signals());
752
753 for (const ChannelSelector &s : channel_selectors_)
754 {
755 if (s.decoder_ != dec)
756 break;
757
758 const LogicSignal *const selection =
759 (LogicSignal*)s.combo_->itemData(
760 s.combo_->currentIndex()).value<void*>();
761
762 for (shared_ptr<Signal> sig : sigs)
763 if (sig.get() == selection) {
764 channel_map[s.pdch_] =
765 dynamic_pointer_cast<LogicSignal>(sig);
766 break;
767 }
768 }
769
770 dec->set_channels(channel_map);
771}
772
773void DecodeTrace::commit_channels()
774{
775 assert(decoder_stack_);
776 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
777 commit_decoder_channels(dec);
778
779 decoder_stack_->begin_decode();
780}
781
782void DecodeTrace::on_new_decode_data()
783{
784 if (owner_)
785 owner_->row_item_appearance_changed(false, true);
786}
787
788void DecodeTrace::delete_pressed()
789{
790 on_delete();
791}
792
793void DecodeTrace::on_delete()
794{
795 session_.remove_decode_signal(this);
796}
797
798void DecodeTrace::on_channel_selected(int)
799{
800 commit_channels();
801}
802
803void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
804{
805 assert(decoder);
806 assert(decoder_stack_);
807 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
808 new data::decode::Decoder(decoder)));
809 decoder_stack_->begin_decode();
810
811 create_popup_form();
812}
813
814void DecodeTrace::on_delete_decoder(int index)
815{
816 decoder_stack_->remove(index);
817
818 // Update the popup
819 create_popup_form();
820
821 decoder_stack_->begin_decode();
822}
823
824void DecodeTrace::on_show_hide_decoder(int index)
825{
826 using pv::data::decode::Decoder;
827
828 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
829
830 // Find the decoder in the stack
831 auto iter = stack.cbegin();
832 for (int i = 0; i < index; i++, iter++)
833 assert(iter != stack.end());
834
835 shared_ptr<Decoder> dec = *iter;
836 assert(dec);
837
838 const bool show = !dec->shown();
839 dec->show(show);
840
841 assert(index < (int)decoder_forms_.size());
842 decoder_forms_[index]->set_decoder_visible(show);
843
844 if (owner_)
845 owner_->row_item_appearance_changed(false, true);
846}
847
848} // namespace view
849} // namespace pv