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