]> sigrok.org Git - pulseview.git/blame_incremental - pv/view/decodetrace.cpp
DecoderStack: Emancipate from SignalData
[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/logicsnapshot.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 text_height_(0),
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 = (RowItemPaintParams::text_height() * 6) / 4;
164 return make_pair(-row_height / 2, row_height * 7 / 2);
165}
166
167void DecodeTrace::paint_back(QPainter &p, const RowItemPaintParams &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 RowItemPaintParams &pp)
174{
175 using namespace pv::data::decode;
176
177 text_height_ = RowItemPaintParams::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, get_text_colour(),
216 annotation_height, 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 RowItemPaintParams &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, QColor text_color, int h, const RowItemPaintParams &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, text_color, h,
357 start, y);
358 else
359 draw_range(a, p, fill, outline, text_color, h,
360 start, end, y);
361}
362
363void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
364 QColor fill, QColor outline, QColor text_color, 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(text_color);
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, QColor text_color, 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(text_color);
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 RowItemPaintParams &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(get_text_colour());
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/snapshot
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_snapshots().empty())
485 return;
486
487 const shared_ptr<LogicSnapshot> snapshot =
488 data->logic_snapshots().front();
489 assert(snapshot);
490 const int64_t sample_count = (int64_t)snapshot->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;
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 row = (point.y() - get_visual_y() + row_height_ / 2) /
562 row_height_;
563 if (row < 0 || row >= (int)visible_rows_.size())
564 return -1;
565
566 return row;
567}
568
569const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
570{
571 using namespace pv::data::decode;
572
573 if (!enabled())
574 return QString();
575
576 const pair<uint64_t, uint64_t> sample_range =
577 get_sample_range(point.x(), point.x() + 1);
578 const int row = get_row_at_point(point);
579 if (row < 0)
580 return QString();
581
582 vector<pv::data::decode::Annotation> annotations;
583
584 assert(decoder_stack_);
585 decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
586 sample_range.first, sample_range.second);
587
588 return (annotations.empty()) ?
589 QString() : annotations[0].annotations().front();
590}
591
592void DecodeTrace::hide_hover_annotation()
593{
594 QToolTip::hideText();
595}
596
597void DecodeTrace::hover_point_changed()
598{
599 assert(owner_);
600
601 const View *const view = owner_->view();
602 assert(view);
603
604 QPoint hp = view->hover_point();
605 QString ann = get_annotation_at_point(hp);
606
607 assert(view);
608
609 if (!row_height_ || ann.isEmpty()) {
610 hide_hover_annotation();
611 return;
612 }
613
614 const int hover_row = get_row_at_point(hp);
615
616 QFontMetrics m(QToolTip::font());
617 const QRect text_size = m.boundingRect(QRect(), 0, ann);
618
619 // This is OS-specific and unfortunately we can't query it, so
620 // use an approximation to at least try to minimize the error.
621 const int padding = 8;
622
623 // Make sure the tool tip doesn't overlap with the mouse cursor.
624 // If it did, the tool tip would constantly hide and re-appear.
625 // We also push it up by one row so that it appears above the
626 // decode trace, not below.
627 hp.setX(hp.x() - (text_size.width() / 2) - padding);
628
629 hp.setY(get_visual_y() - (row_height_ / 2) +
630 (hover_row * row_height_) -
631 row_height_ - text_size.height());
632
633 QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
634}
635
636void DecodeTrace::create_decoder_form(int index,
637 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
638 QFormLayout *form)
639{
640 const GSList *l;
641
642 assert(dec);
643 const srd_decoder *const decoder = dec->decoder();
644 assert(decoder);
645
646 pv::widgets::DecoderGroupBox *const group =
647 new pv::widgets::DecoderGroupBox(
648 QString::fromUtf8(decoder->name));
649 group->set_decoder_visible(dec->shown());
650
651 delete_mapper_.setMapping(group, index);
652 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
653
654 show_hide_mapper_.setMapping(group, index);
655 connect(group, SIGNAL(show_hide_decoder()),
656 &show_hide_mapper_, SLOT(map()));
657
658 QFormLayout *const decoder_form = new QFormLayout;
659 group->add_layout(decoder_form);
660
661 // Add the mandatory channels
662 for(l = decoder->channels; l; l = l->next) {
663 const struct srd_channel *const pdch =
664 (struct srd_channel *)l->data;
665 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
666 connect(combo, SIGNAL(currentIndexChanged(int)),
667 this, SLOT(on_channel_selected(int)));
668 decoder_form->addRow(tr("<b>%1</b> (%2) *")
669 .arg(QString::fromUtf8(pdch->name))
670 .arg(QString::fromUtf8(pdch->desc)), combo);
671
672 const ChannelSelector s = {combo, dec, pdch};
673 channel_selectors_.push_back(s);
674 }
675
676 // Add the optional channels
677 for(l = decoder->opt_channels; l; l = l->next) {
678 const struct srd_channel *const pdch =
679 (struct srd_channel *)l->data;
680 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
681 connect(combo, SIGNAL(currentIndexChanged(int)),
682 this, SLOT(on_channel_selected(int)));
683 decoder_form->addRow(tr("<b>%1</b> (%2)")
684 .arg(QString::fromUtf8(pdch->name))
685 .arg(QString::fromUtf8(pdch->desc)), combo);
686
687 const ChannelSelector s = {combo, dec, pdch};
688 channel_selectors_.push_back(s);
689 }
690
691 // Add the options
692 shared_ptr<prop::binding::DecoderOptions> binding(
693 new prop::binding::DecoderOptions(decoder_stack_, dec));
694 binding->add_properties_to_form(decoder_form, true);
695
696 bindings_.push_back(binding);
697
698 form->addRow(group);
699 decoder_forms_.push_back(group);
700}
701
702QComboBox* DecodeTrace::create_channel_selector(
703 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
704 const srd_channel *const pdch)
705{
706 assert(dec);
707
708 shared_lock<shared_mutex> lock(session_.signals_mutex());
709 const vector< shared_ptr<Signal> > &sigs(session_.signals());
710
711 assert(decoder_stack_);
712 const auto channel_iter = dec->channels().find(pdch);
713
714 QComboBox *selector = new QComboBox(parent);
715
716 selector->addItem("-", qVariantFromValue((void*)NULL));
717
718 if (channel_iter == dec->channels().end())
719 selector->setCurrentIndex(0);
720
721 for(size_t i = 0; i < sigs.size(); i++) {
722 const shared_ptr<view::Signal> s(sigs[i]);
723 assert(s);
724
725 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
726 {
727 selector->addItem(s->name(),
728 qVariantFromValue((void*)s.get()));
729 if ((*channel_iter).second == s)
730 selector->setCurrentIndex(i + 1);
731 }
732 }
733
734 return selector;
735}
736
737void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
738{
739 assert(dec);
740
741 map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
742
743 shared_lock<shared_mutex> lock(session_.signals_mutex());
744 const vector< shared_ptr<Signal> > &sigs(session_.signals());
745
746 for (const ChannelSelector &s : channel_selectors_)
747 {
748 if(s.decoder_ != dec)
749 break;
750
751 const LogicSignal *const selection =
752 (LogicSignal*)s.combo_->itemData(
753 s.combo_->currentIndex()).value<void*>();
754
755 for (shared_ptr<Signal> sig : sigs)
756 if(sig.get() == selection) {
757 channel_map[s.pdch_] =
758 dynamic_pointer_cast<LogicSignal>(sig);
759 break;
760 }
761 }
762
763 dec->set_channels(channel_map);
764}
765
766void DecodeTrace::commit_channels()
767{
768 assert(decoder_stack_);
769 for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
770 commit_decoder_channels(dec);
771
772 decoder_stack_->begin_decode();
773}
774
775void DecodeTrace::on_new_decode_data()
776{
777 if (owner_)
778 owner_->appearance_changed(false, true);
779}
780
781void DecodeTrace::delete_pressed()
782{
783 on_delete();
784}
785
786void DecodeTrace::on_delete()
787{
788 session_.remove_decode_signal(this);
789}
790
791void DecodeTrace::on_channel_selected(int)
792{
793 commit_channels();
794}
795
796void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
797{
798 assert(decoder);
799 assert(decoder_stack_);
800 decoder_stack_->push(shared_ptr<data::decode::Decoder>(
801 new data::decode::Decoder(decoder)));
802 decoder_stack_->begin_decode();
803
804 create_popup_form();
805}
806
807void DecodeTrace::on_delete_decoder(int index)
808{
809 decoder_stack_->remove(index);
810
811 // Update the popup
812 create_popup_form();
813
814 decoder_stack_->begin_decode();
815}
816
817void DecodeTrace::on_show_hide_decoder(int index)
818{
819 using pv::data::decode::Decoder;
820
821 const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
822
823 // Find the decoder in the stack
824 auto iter = stack.cbegin();
825 for(int i = 0; i < index; i++, iter++)
826 assert(iter != stack.end());
827
828 shared_ptr<Decoder> dec = *iter;
829 assert(dec);
830
831 const bool show = !dec->shown();
832 dec->show(show);
833
834 assert(index < (int)decoder_forms_.size());
835 decoder_forms_[index]->set_decoder_visible(show);
836
837 if (owner_)
838 owner_->appearance_changed(false, true);
839}
840
841} // namespace view
842} // namespace pv