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