]> sigrok.org Git - pulseview.git/blame_incremental - pv/view/decodetrace.cpp
TraceTreeItemOwner: Removed non-const item_list accessor
[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 {
187 draw_unresolved_period(
188 p, annotation_height, pp.left(), pp.right());
189 draw_error(p, err, pp);
190 return;
191 }
192
193 // Iterate through the rows
194 int y = get_visual_y();
195 pair<uint64_t, uint64_t> sample_range = get_sample_range(
196 pp.left(), pp.right());
197
198 assert(decoder_stack_);
199 const vector<Row> rows(decoder_stack_->get_visible_rows());
200
201 visible_rows_.clear();
202 for (size_t i = 0; i < rows.size(); i++)
203 {
204 const Row &row = rows[i];
205
206 size_t base_colour = 0x13579BDF;
207 boost::hash_combine(base_colour, this);
208 boost::hash_combine(base_colour, row.decoder());
209 boost::hash_combine(base_colour, row.row());
210 base_colour >>= 16;
211
212 vector<Annotation> annotations;
213 decoder_stack_->get_annotation_subset(annotations, row,
214 sample_range.first, sample_range.second);
215 if (!annotations.empty()) {
216 for (const Annotation &a : annotations)
217 draw_annotation(a, p, annotation_height,
218 pp, y, base_colour);
219 y += row_height_;
220
221 visible_rows_.push_back(rows[i]);
222 }
223 }
224
225 // Draw the hatching
226 draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
227}
228
229void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
230{
231 using namespace pv::data::decode;
232
233 assert(row_height_);
234
235 for (size_t i = 0; i < visible_rows_.size(); i++)
236 {
237 const int y = i * row_height_ + get_visual_y();
238
239 p.setPen(QPen(Qt::NoPen));
240 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
241
242 if (i != 0)
243 {
244 const QPointF points[] = {
245 QPointF(pp.left(), y - ArrowSize),
246 QPointF(pp.left() + ArrowSize, y),
247 QPointF(pp.left(), y + ArrowSize)
248 };
249 p.drawPolygon(points, countof(points));
250 }
251
252 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
253 pp.right() - pp.left(), row_height_);
254 const QString h(visible_rows_[i].title());
255 const int f = Qt::AlignLeft | Qt::AlignVCenter |
256 Qt::TextDontClip;
257
258 // Draw the outline
259 p.setPen(QApplication::palette().color(QPalette::Base));
260 for (int dx = -1; dx <= 1; dx++)
261 for (int dy = -1; dy <= 1; dy++)
262 if (dx != 0 && dy != 0)
263 p.drawText(r.translated(dx, dy), f, h);
264
265 // Draw the text
266 p.setPen(QApplication::palette().color(QPalette::WindowText));
267 p.drawText(r, f, h);
268 }
269}
270
271void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
272{
273 using pv::data::decode::Decoder;
274
275 assert(form);
276 assert(parent);
277 assert(decoder_stack_);
278
279 // Add the standard options
280 Trace::populate_popup_form(parent, form);
281
282 // Add the decoder options
283 bindings_.clear();
284 channel_selectors_.clear();
285 decoder_forms_.clear();
286
287 const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
288
289 if (stack.empty())
290 {
291 QLabel *const l = new QLabel(
292 tr("<p><i>No decoders in the stack</i></p>"));
293 l->setAlignment(Qt::AlignCenter);
294 form->addRow(l);
295 }
296 else
297 {
298 auto iter = stack.cbegin();
299 for (int i = 0; i < (int)stack.size(); i++, iter++) {
300 shared_ptr<Decoder> dec(*iter);
301 create_decoder_form(i, dec, parent, form);
302 }
303
304 form->addRow(new QLabel(
305 tr("<i>* Required channels</i>"), parent));
306 }
307
308 // Add stacking button
309 pv::widgets::DecoderMenu *const decoder_menu =
310 new pv::widgets::DecoderMenu(parent);
311 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
312 this, SLOT(on_stack_decoder(srd_decoder*)));
313
314 QPushButton *const stack_button =
315 new QPushButton(tr("Stack Decoder"), parent);
316 stack_button->setMenu(decoder_menu);
317
318 QHBoxLayout *stack_button_box = new QHBoxLayout;
319 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
320 form->addRow(stack_button_box);
321}
322
323QMenu* DecodeTrace::create_context_menu(QWidget *parent)
324{
325 QMenu *const menu = Trace::create_context_menu(parent);
326
327 menu->addSeparator();
328
329 QAction *const del = new QAction(tr("Delete"), this);
330 del->setShortcuts(QKeySequence::Delete);
331 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
332 menu->addAction(del);
333
334 return menu;
335}
336
337void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
338 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
339 size_t base_colour) const
340{
341 double samples_per_pixel, pixels_offset;
342 tie(pixels_offset, samples_per_pixel) =
343 get_pixels_offset_samples_per_pixel();
344
345 const double start = a.start_sample() / samples_per_pixel -
346 pixels_offset;
347 const double end = a.end_sample() / samples_per_pixel -
348 pixels_offset;
349
350 const size_t colour = (base_colour + a.format()) % countof(Colours);
351 const QColor &fill = Colours[colour];
352 const QColor &outline = OutlineColours[colour];
353
354 if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
355 return;
356
357 if (a.start_sample() == a.end_sample())
358 draw_instant(a, p, fill, outline, h, start, y);
359 else
360 draw_range(a, p, fill, outline, h, start, end, y);
361}
362
363void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
364 QColor fill, QColor outline, int h, double x, int y) const
365{
366 const QString text = a.annotations().empty() ?
367 QString() : a.annotations().back();
368 const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
369 0.0) + h;
370 const QRectF rect(x - w / 2, y - h / 2, w, h);
371
372 p.setPen(outline);
373 p.setBrush(fill);
374 p.drawRoundedRect(rect, h / 2, h / 2);
375
376 p.setPen(Qt::black);
377 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
378}
379
380void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
381 QColor fill, QColor outline, int h, double start,
382 double end, int y) const
383{
384 const double top = y + .5 - h / 2;
385 const double bottom = y + .5 + h / 2;
386 const vector<QString> annotations = a.annotations();
387
388 p.setPen(outline);
389 p.setBrush(fill);
390
391 // If the two ends are within 1 pixel, draw a vertical line
392 if (start + 1.0 > end)
393 {
394 p.drawLine(QPointF(start, top), QPointF(start, bottom));
395 return;
396 }
397
398 const double cap_width = min((end - start) / 4, EndCapWidth);
399
400 QPointF pts[] = {
401 QPointF(start, y + .5f),
402 QPointF(start + cap_width, top),
403 QPointF(end - cap_width, top),
404 QPointF(end, y + .5f),
405 QPointF(end - cap_width, bottom),
406 QPointF(start + cap_width, bottom)
407 };
408
409 p.drawConvexPolygon(pts, countof(pts));
410
411 if (annotations.empty())
412 return;
413
414 QRectF rect(start + cap_width, y - h / 2,
415 end - start - cap_width * 2, h);
416 if (rect.width() <= 4)
417 return;
418
419 p.setPen(Qt::black);
420
421 // Try to find an annotation that will fit
422 QString best_annotation;
423 int best_width = 0;
424
425 for (const QString &a : annotations) {
426 const int w = p.boundingRect(QRectF(), 0, a).width();
427 if (w <= rect.width() && w > best_width)
428 best_annotation = a, best_width = w;
429 }
430
431 if (best_annotation.isEmpty())
432 best_annotation = annotations.back();
433
434 // If not ellide the last in the list
435 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
436 best_annotation, Qt::ElideRight, rect.width()));
437}
438
439void DecodeTrace::draw_error(QPainter &p, const QString &message,
440 const ViewItemPaintParams &pp)
441{
442 const int y = get_visual_y();
443
444 p.setPen(ErrorBgColour.darker());
445 p.setBrush(ErrorBgColour);
446
447 const QRectF bounding_rect =
448 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
449 const QRectF text_rect = p.boundingRect(bounding_rect,
450 Qt::AlignCenter, message);
451 const float r = text_rect.height() / 4;
452
453 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
454 Qt::AbsoluteSize);
455
456 p.setPen(Qt::black);
457 p.drawText(text_rect, message);
458}
459
460void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
461 int right) const
462{
463 using namespace pv::data;
464 using pv::data::decode::Decoder;
465
466 double samples_per_pixel, pixels_offset;
467
468 assert(decoder_stack_);
469
470 shared_ptr<Logic> data;
471 shared_ptr<LogicSignal> logic_signal;
472
473 const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
474
475 // We get the logic data of the first channel in the list.
476 // This works because we are currently assuming all
477 // LogicSignals have the same data/segment
478 for (const shared_ptr<Decoder> &dec : stack)
479 if (dec && !dec->channels().empty() &&
480 ((logic_signal = (*dec->channels().begin()).second)) &&
481 ((data = logic_signal->logic_data())))
482 break;
483
484 if (!data || data->logic_segments().empty())
485 return;
486
487 const shared_ptr<LogicSegment> segment =
488 data->logic_segments().front();
489 assert(segment);
490 const int64_t sample_count = (int64_t)segment->get_sample_count();
491 if (sample_count == 0)
492 return;
493
494 const int64_t samples_decoded = decoder_stack_->samples_decoded();
495 if (sample_count == samples_decoded)
496 return;
497
498 const int y = get_visual_y();
499
500 tie(pixels_offset, samples_per_pixel) =
501 get_pixels_offset_samples_per_pixel();
502
503 const double start = max(samples_decoded /
504 samples_per_pixel - pixels_offset, left - 1.0);
505 const double end = min(sample_count / samples_per_pixel -
506 pixels_offset, right + 1.0);
507 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
508
509 p.setPen(QPen(Qt::NoPen));
510 p.setBrush(Qt::white);
511 p.drawRect(no_decode_rect);
512
513 p.setPen(NoDecodeColour);
514 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
515 p.drawRect(no_decode_rect);
516}
517
518pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
519{
520 assert(owner_);
521 assert(decoder_stack_);
522
523 const View *view = owner_->view();
524 assert(view);
525
526 const double scale = view->scale();
527 assert(scale > 0);
528
529 const double pixels_offset =
530 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
531
532 double samplerate = decoder_stack_->samplerate();
533
534 // Show sample rate as 1Hz when it is unknown
535 if (samplerate == 0.0)
536 samplerate = 1.0;
537
538 return make_pair(pixels_offset, samplerate * scale);
539}
540
541pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
542 int x_start, int x_end) const
543{
544 double samples_per_pixel, pixels_offset;
545 tie(pixels_offset, samples_per_pixel) =
546 get_pixels_offset_samples_per_pixel();
547
548 const uint64_t start = (uint64_t)max(
549 (x_start + pixels_offset) * samples_per_pixel, 0.0);
550 const uint64_t end = (uint64_t)max(
551 (x_end + pixels_offset) * samples_per_pixel, 0.0);
552
553 return make_pair(start, end);
554}
555
556int DecodeTrace::get_row_at_point(const QPoint &point)
557{
558 if (!row_height_)
559 return -1;
560
561 const int y = (point.y() - get_visual_y() + row_height_ / 2);
562
563 /* Integer divison of (x-1)/x would yield 0, so we check for this. */
564 if (y < 0)
565 return -1;
566
567 const int row = y / row_height_;
568
569 if (row >= (int)visible_rows_.size())
570 return -1;
571
572 return row;
573}
574
575const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
576{
577 using namespace pv::data::decode;
578
579 if (!enabled())
580 return QString();
581
582 const pair<uint64_t, uint64_t> sample_range =
583 get_sample_range(point.x(), point.x() + 1);
584 const int row = get_row_at_point(point);
585 if (row < 0)
586 return QString();
587
588 vector<pv::data::decode::Annotation> annotations;
589
590 assert(decoder_stack_);
591 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
592 sample_range.first, sample_range.second);
593
594 return (annotations.empty()) ?
595 QString() : annotations[0].annotations().front();
596}
597
598void DecodeTrace::hover_point_changed()
599{
600 assert(owner_);
601
602 const View *const view = owner_->view();
603 assert(view);
604
605 QPoint hp = view->hover_point();
606 QString ann = get_annotation_at_point(hp);
607
608 assert(view);
609
610 if (!row_height_ || ann.isEmpty()) {
611 QToolTip::hideText();
612 return;
613 }
614
615 const int hover_row = get_row_at_point(hp);
616
617 QFontMetrics m(QToolTip::font());
618 const QRect text_size = m.boundingRect(QRect(), 0, ann);
619
620 // This is OS-specific and unfortunately we can't query it, so
621 // use an approximation to at least try to minimize the error.
622 const int padding = 8;
623
624 // Make sure the tool tip doesn't overlap with the mouse cursor.
625 // If it did, the tool tip would constantly hide and re-appear.
626 // We also push it up by one row so that it appears above the
627 // decode trace, not below.
628 hp.setX(hp.x() - (text_size.width() / 2) - padding);
629
630 hp.setY(get_visual_y() - (row_height_ / 2) +
631 (hover_row * row_height_) -
632 row_height_ - text_size.height() - padding);
633
634 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
635}
636
637void DecodeTrace::create_decoder_form(int index,
638 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
639 QFormLayout *form)
640{
641 const GSList *l;
642
643 assert(dec);
644 const srd_decoder *const decoder = dec->decoder();
645 assert(decoder);
646
647 const bool decoder_deletable = index > 0;
648
649 pv::widgets::DecoderGroupBox *const group =
650 new pv::widgets::DecoderGroupBox(
651 QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
652 group->set_decoder_visible(dec->shown());
653
654 if (decoder_deletable) {
655 delete_mapper_.setMapping(group, index);
656 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
657 }
658
659 show_hide_mapper_.setMapping(group, index);
660 connect(group, SIGNAL(show_hide_decoder()),
661 &show_hide_mapper_, SLOT(map()));
662
663 QFormLayout *const decoder_form = new QFormLayout;
664 group->add_layout(decoder_form);
665
666 // Add the mandatory channels
667 for (l = decoder->channels; l; l = l->next) {
668 const struct srd_channel *const pdch =
669 (struct srd_channel *)l->data;
670 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
671 connect(combo, SIGNAL(currentIndexChanged(int)),
672 this, SLOT(on_channel_selected(int)));
673 decoder_form->addRow(tr("<b>%1</b> (%2) *")
674 .arg(QString::fromUtf8(pdch->name))
675 .arg(QString::fromUtf8(pdch->desc)), combo);
676
677 const ChannelSelector s = {combo, dec, pdch};
678 channel_selectors_.push_back(s);
679 }
680
681 // Add the optional channels
682 for (l = decoder->opt_channels; l; l = l->next) {
683 const struct srd_channel *const pdch =
684 (struct srd_channel *)l->data;
685 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
686 connect(combo, SIGNAL(currentIndexChanged(int)),
687 this, SLOT(on_channel_selected(int)));
688 decoder_form->addRow(tr("<b>%1</b> (%2)")
689 .arg(QString::fromUtf8(pdch->name))
690 .arg(QString::fromUtf8(pdch->desc)), combo);
691
692 const ChannelSelector s = {combo, dec, pdch};
693 channel_selectors_.push_back(s);
694 }
695
696 // Add the options
697 shared_ptr<binding::Decoder> binding(
698 new binding::Decoder(decoder_stack_, dec));
699 binding->add_properties_to_form(decoder_form, true);
700
701 bindings_.push_back(binding);
702
703 form->addRow(group);
704 decoder_forms_.push_back(group);
705}
706
707QComboBox* DecodeTrace::create_channel_selector(
708 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
709 const srd_channel *const pdch)
710{
711 assert(dec);
712
713 const auto sigs(session_.signals());
714
715 vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
716 std::sort(sig_list.begin(), sig_list.end(),
717 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
718 return a->name().compare(b->name()) < 0; });
719
720 assert(decoder_stack_);
721 const auto channel_iter = dec->channels().find(pdch);
722
723 QComboBox *selector = new QComboBox(parent);
724
725 selector->addItem("-", qVariantFromValue((void*)nullptr));
726
727 if (channel_iter == dec->channels().end())
728 selector->setCurrentIndex(0);
729
730 for (const shared_ptr<view::Signal> &s : sig_list) {
731 assert(s);
732 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
733 {
734 selector->addItem(s->name(),
735 qVariantFromValue((void*)s.get()));
736 if ((*channel_iter).second == s)
737 selector->setCurrentIndex(
738 selector->count() - 1);
739 }
740 }
741
742 return selector;
743}
744
745void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
746{
747 assert(dec);
748
749 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
750
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