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