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