]> sigrok.org Git - pulseview.git/blob - pv/views/trace/decodetrace.cpp
Shift more methods to DecodeSignal
[pulseview.git] / pv / views / trace / decodetrace.cpp
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, see <http://www.gnu.org/licenses/>.
18  */
19
20 extern "C" {
21 #include <libsigrokdecode/libsigrokdecode.h>
22 }
23
24 #include <mutex>
25
26 #include <extdef.h>
27
28 #include <tuple>
29
30 #include <boost/functional/hash.hpp>
31
32 #include <QAction>
33 #include <QApplication>
34 #include <QComboBox>
35 #include <QFormLayout>
36 #include <QLabel>
37 #include <QMenu>
38 #include <QPushButton>
39 #include <QToolTip>
40
41 #include "decodetrace.hpp"
42 #include "view.hpp"
43 #include "viewport.hpp"
44
45 #include <pv/globalsettings.hpp>
46 #include <pv/session.hpp>
47 #include <pv/strnatcmp.hpp>
48 #include <pv/data/decodesignal.hpp>
49 #include <pv/data/decode/annotation.hpp>
50 #include <pv/data/decode/decoder.hpp>
51 #include <pv/data/decoderstack.hpp>
52 #include <pv/data/logic.hpp>
53 #include <pv/data/logicsegment.hpp>
54 #include <pv/widgets/decodergroupbox.hpp>
55 #include <pv/widgets/decodermenu.hpp>
56
57 using std::all_of;
58 using std::list;
59 using std::make_pair;
60 using std::max;
61 using std::make_pair;
62 using std::map;
63 using std::min;
64 using std::out_of_range;
65 using std::pair;
66 using std::shared_ptr;
67 using std::make_shared;
68 using std::tie;
69 using std::unordered_set;
70 using std::vector;
71
72 using pv::data::decode::Annotation;
73 using pv::data::decode::Row;
74
75 namespace pv {
76 namespace views {
77 namespace trace {
78
79 const QColor DecodeTrace::DecodeColours[4] = {
80         QColor(0xEF, 0x29, 0x29),       // Red
81         QColor(0xFC, 0xE9, 0x4F),       // Yellow
82         QColor(0x8A, 0xE2, 0x34),       // Green
83         QColor(0x72, 0x9F, 0xCF)        // Blue
84 };
85
86 const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
87 const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
88
89 const int DecodeTrace::ArrowSize = 4;
90 const double DecodeTrace::EndCapWidth = 5;
91 const int DecodeTrace::RowTitleMargin = 10;
92 const int DecodeTrace::DrawPadding = 100;
93
94 const QColor DecodeTrace::Colours[16] = {
95         QColor(0xEF, 0x29, 0x29),
96         QColor(0xF6, 0x6A, 0x32),
97         QColor(0xFC, 0xAE, 0x3E),
98         QColor(0xFB, 0xCA, 0x47),
99         QColor(0xFC, 0xE9, 0x4F),
100         QColor(0xCD, 0xF0, 0x40),
101         QColor(0x8A, 0xE2, 0x34),
102         QColor(0x4E, 0xDC, 0x44),
103         QColor(0x55, 0xD7, 0x95),
104         QColor(0x64, 0xD1, 0xD2),
105         QColor(0x72, 0x9F, 0xCF),
106         QColor(0xD4, 0x76, 0xC4),
107         QColor(0x9D, 0x79, 0xB9),
108         QColor(0xAD, 0x7F, 0xA8),
109         QColor(0xC2, 0x62, 0x9B),
110         QColor(0xD7, 0x47, 0x6F)
111 };
112
113 const QColor DecodeTrace::OutlineColours[16] = {
114         QColor(0x77, 0x14, 0x14),
115         QColor(0x7B, 0x35, 0x19),
116         QColor(0x7E, 0x57, 0x1F),
117         QColor(0x7D, 0x65, 0x23),
118         QColor(0x7E, 0x74, 0x27),
119         QColor(0x66, 0x78, 0x20),
120         QColor(0x45, 0x71, 0x1A),
121         QColor(0x27, 0x6E, 0x22),
122         QColor(0x2A, 0x6B, 0x4A),
123         QColor(0x32, 0x68, 0x69),
124         QColor(0x39, 0x4F, 0x67),
125         QColor(0x6A, 0x3B, 0x62),
126         QColor(0x4E, 0x3C, 0x5C),
127         QColor(0x56, 0x3F, 0x54),
128         QColor(0x61, 0x31, 0x4D),
129         QColor(0x6B, 0x23, 0x37)
130 };
131
132 DecodeTrace::DecodeTrace(pv::Session &session,
133         shared_ptr<data::SignalBase> signalbase, int index) :
134         Trace(signalbase),
135         session_(session),
136         row_height_(0),
137         max_visible_rows_(0),
138         delete_mapper_(this),
139         show_hide_mapper_(this)
140 {
141         decode_signal_ = dynamic_pointer_cast<data::DecodeSignal>(base_);
142
143         // Determine shortest string we want to see displayed in full
144         QFontMetrics m(QApplication::font());
145         min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
146
147         base_->set_colour(DecodeColours[index % countof(DecodeColours)]);
148
149         connect(decode_signal_.get(), SIGNAL(new_annotations()),
150                 this, SLOT(on_new_annotations()));
151         connect(&delete_mapper_, SIGNAL(mapped(int)),
152                 this, SLOT(on_delete_decoder(int)));
153         connect(&show_hide_mapper_, SIGNAL(mapped(int)),
154                 this, SLOT(on_show_hide_decoder(int)));
155 }
156
157 bool DecodeTrace::enabled() const
158 {
159         return true;
160 }
161
162 shared_ptr<data::SignalBase> DecodeTrace::base() const
163 {
164         return base_;
165 }
166
167 pair<int, int> DecodeTrace::v_extents() const
168 {
169         const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
170
171         // Make an empty decode trace appear symmetrical
172         const int row_count = max(1, max_visible_rows_);
173
174         return make_pair(-row_height, row_height * row_count);
175 }
176
177 void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
178 {
179         Trace::paint_back(p, pp);
180         paint_axis(p, pp, get_visual_y());
181 }
182
183 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
184 {
185         const int text_height = ViewItemPaintParams::text_height();
186         row_height_ = (text_height * 6) / 4;
187         const int annotation_height = (text_height * 5) / 4;
188
189         const QString err = decode_signal_->error_message();
190         if (!err.isEmpty()) {
191                 draw_unresolved_period(
192                         p, annotation_height, pp.left(), pp.right());
193                 draw_error(p, err, pp);
194                 return;
195         }
196
197         // Set default pen to allow for text width calculation
198         p.setPen(Qt::black);
199
200         // Iterate through the rows
201         int y = get_visual_y();
202         pair<uint64_t, uint64_t> sample_range = get_sample_range(
203                 pp.left(), pp.right());
204
205         const vector<Row> rows = decode_signal_->visible_rows();
206
207         visible_rows_.clear();
208         for (const Row& row : rows) {
209                 // Cache the row title widths
210                 int row_title_width;
211                 try {
212                         row_title_width = row_title_widths_.at(row);
213                 } catch (out_of_range) {
214                         const int w = p.boundingRect(QRectF(), 0, row.title()).width() +
215                                 RowTitleMargin;
216                         row_title_widths_[row] = w;
217                         row_title_width = w;
218                 }
219
220                 // Determine the row's color
221                 size_t base_colour = 0x13579BDF;
222                 boost::hash_combine(base_colour, this);
223                 boost::hash_combine(base_colour, row.decoder());
224                 boost::hash_combine(base_colour, row.row());
225                 base_colour >>= 16;
226
227                 vector<Annotation> annotations;
228                 decode_signal_->get_annotation_subset(annotations, row,
229                         sample_range.first, sample_range.second);
230                 if (!annotations.empty()) {
231                         draw_annotations(annotations, p, annotation_height, pp, y,
232                                 base_colour, row_title_width);
233
234                         y += row_height_;
235
236                         visible_rows_.push_back(row);
237                 }
238         }
239
240         // Draw the hatching
241         draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
242
243         if ((int)visible_rows_.size() > max_visible_rows_)
244                 owner_->extents_changed(false, true);
245
246         // Update the maximum row count if needed
247         max_visible_rows_ = max(max_visible_rows_, (int)visible_rows_.size());
248 }
249
250 void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
251 {
252         assert(row_height_);
253
254         for (size_t i = 0; i < visible_rows_.size(); i++) {
255                 const int y = i * row_height_ + get_visual_y();
256
257                 p.setPen(QPen(Qt::NoPen));
258                 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
259
260                 if (i != 0) {
261                         const QPointF points[] = {
262                                 QPointF(pp.left(), y - ArrowSize),
263                                 QPointF(pp.left() + ArrowSize, y),
264                                 QPointF(pp.left(), y + ArrowSize)
265                         };
266                         p.drawPolygon(points, countof(points));
267                 }
268
269                 const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
270                         pp.right() - pp.left(), row_height_);
271                 const QString h(visible_rows_[i].title());
272                 const int f = Qt::AlignLeft | Qt::AlignVCenter |
273                         Qt::TextDontClip;
274
275                 // Draw the outline
276                 p.setPen(QApplication::palette().color(QPalette::Base));
277                 for (int dx = -1; dx <= 1; dx++)
278                         for (int dy = -1; dy <= 1; dy++)
279                                 if (dx != 0 && dy != 0)
280                                         p.drawText(r.translated(dx, dy), f, h);
281
282                 // Draw the text
283                 p.setPen(QApplication::palette().color(QPalette::WindowText));
284                 p.drawText(r, f, h);
285         }
286 }
287
288 void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
289 {
290         using pv::data::decode::Decoder;
291
292         assert(form);
293
294         // Add the standard options
295         Trace::populate_popup_form(parent, form);
296
297         // Add the decoder options
298         bindings_.clear();
299         channel_selectors_.clear();
300         decoder_forms_.clear();
301
302         const list< shared_ptr<Decoder> >& stack =
303                 decode_signal_->decoder_stack_list();
304
305         if (stack.empty()) {
306                 QLabel *const l = new QLabel(
307                         tr("<p><i>No decoders in the stack</i></p>"));
308                 l->setAlignment(Qt::AlignCenter);
309                 form->addRow(l);
310         } else {
311                 auto iter = stack.cbegin();
312                 for (int i = 0; i < (int)stack.size(); i++, iter++) {
313                         shared_ptr<Decoder> dec(*iter);
314                         create_decoder_form(i, dec, parent, form);
315                 }
316
317                 form->addRow(new QLabel(
318                         tr("<i>* Required channels</i>"), parent));
319         }
320
321         // Add stacking button
322         pv::widgets::DecoderMenu *const decoder_menu =
323                 new pv::widgets::DecoderMenu(parent);
324         connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
325                 this, SLOT(on_stack_decoder(srd_decoder*)));
326
327         QPushButton *const stack_button =
328                 new QPushButton(tr("Stack Decoder"), parent);
329         stack_button->setMenu(decoder_menu);
330         stack_button->setToolTip(tr("Stack a higher-level decoder on top of this one"));
331
332         QHBoxLayout *stack_button_box = new QHBoxLayout;
333         stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
334         form->addRow(stack_button_box);
335 }
336
337 QMenu* DecodeTrace::create_context_menu(QWidget *parent)
338 {
339         QMenu *const menu = Trace::create_context_menu(parent);
340
341         menu->addSeparator();
342
343         QAction *const del = new QAction(tr("Delete"), this);
344         del->setShortcuts(QKeySequence::Delete);
345         connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
346         menu->addAction(del);
347
348         return menu;
349 }
350
351 void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
352                 QPainter &p, int h, const ViewItemPaintParams &pp, int y,
353                 size_t base_colour, int row_title_width)
354 {
355         using namespace pv::data::decode;
356
357         vector<Annotation> a_block;
358         int p_end = INT_MIN;
359
360         double samples_per_pixel, pixels_offset;
361         tie(pixels_offset, samples_per_pixel) =
362                 get_pixels_offset_samples_per_pixel();
363
364         // Sort the annotations by start sample so that decoders
365         // can't confuse us by creating annotations out of order
366         stable_sort(annotations.begin(), annotations.end(),
367                 [](const Annotation &a, const Annotation &b) {
368                         return a.start_sample() < b.start_sample(); });
369
370         // Gather all annotations that form a visual "block" and draw them as such
371         for (const Annotation &a : annotations) {
372
373                 const int a_start = a.start_sample() / samples_per_pixel - pixels_offset;
374                 const int a_end = a.end_sample() / samples_per_pixel - pixels_offset;
375                 const int a_width = a_end - a_start;
376
377                 const int delta = a_end - p_end;
378
379                 bool a_is_separate = false;
380
381                 // Annotation wider than the threshold for a useful label width?
382                 if (a_width >= min_useful_label_width_) {
383                         for (const QString &ann_text : a.annotations()) {
384                                 const int w = p.boundingRect(QRectF(), 0, ann_text).width();
385                                 // Annotation wide enough to fit a label? Don't put it in a block then
386                                 if (w <= a_width) {
387                                         a_is_separate = true;
388                                         break;
389                                 }
390                         }
391                 }
392
393                 // Were the previous and this annotation more than a pixel apart?
394                 if ((abs(delta) > 1) || a_is_separate) {
395                         // Block was broken, draw annotations that form the current block
396                         if (a_block.size() == 1) {
397                                 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
398                                         row_title_width);
399                         }
400                         else
401                                 draw_annotation_block(a_block, p, h, y, base_colour);
402
403                         a_block.clear();
404                 }
405
406                 if (a_is_separate) {
407                         draw_annotation(a, p, h, pp, y, base_colour, row_title_width);
408                         // Next annotation must start a new block. delta will be > 1
409                         // because we set p_end to INT_MIN but that's okay since
410                         // a_block will be empty, so nothing will be drawn
411                         p_end = INT_MIN;
412                 } else {
413                         a_block.push_back(a);
414                         p_end = a_end;
415                 }
416         }
417
418         if (a_block.size() == 1)
419                 draw_annotation(a_block.front(), p, h, pp, y, base_colour,
420                         row_title_width);
421         else
422                 draw_annotation_block(a_block, p, h, y, base_colour);
423 }
424
425 void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
426         QPainter &p, int h, const ViewItemPaintParams &pp, int y,
427         size_t base_colour, int row_title_width) const
428 {
429         double samples_per_pixel, pixels_offset;
430         tie(pixels_offset, samples_per_pixel) =
431                 get_pixels_offset_samples_per_pixel();
432
433         const double start = a.start_sample() / samples_per_pixel -
434                 pixels_offset;
435         const double end = a.end_sample() / samples_per_pixel - pixels_offset;
436
437         const size_t colour = (base_colour + a.format()) % countof(Colours);
438         p.setPen(OutlineColours[colour]);
439         p.setBrush(Colours[colour]);
440
441         if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
442                 return;
443
444         if (a.start_sample() == a.end_sample())
445                 draw_instant(a, p, h, start, y);
446         else
447                 draw_range(a, p, h, start, end, y, pp, row_title_width);
448 }
449
450 void DecodeTrace::draw_annotation_block(
451         vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
452         int y, size_t base_colour) const
453 {
454         using namespace pv::data::decode;
455
456         if (annotations.empty())
457                 return;
458
459         double samples_per_pixel, pixels_offset;
460         tie(pixels_offset, samples_per_pixel) =
461                 get_pixels_offset_samples_per_pixel();
462
463         const double start = annotations.front().start_sample() /
464                 samples_per_pixel - pixels_offset;
465         const double end = annotations.back().end_sample() /
466                 samples_per_pixel - pixels_offset;
467
468         const double top = y + .5 - h / 2;
469         const double bottom = y + .5 + h / 2;
470
471         const size_t colour = (base_colour + annotations.front().format()) %
472                 countof(Colours);
473
474         // Check if all annotations are of the same type (i.e. we can use one color)
475         // or if we should use a neutral color (i.e. gray)
476         const int format = annotations.front().format();
477         const bool single_format = all_of(
478                 annotations.begin(), annotations.end(),
479                 [&](const Annotation &a) { return a.format() == format; });
480
481         const QRectF rect(start, top, end - start, bottom - top);
482         const int r = h / 4;
483
484         p.setPen(QPen(Qt::NoPen));
485         p.setBrush(Qt::white);
486         p.drawRoundedRect(rect, r, r);
487
488         p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
489         p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
490                 Qt::Dense4Pattern));
491         p.drawRoundedRect(rect, r, r);
492 }
493
494 void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
495         int h, double x, int y) const
496 {
497         const QString text = a.annotations().empty() ?
498                 QString() : a.annotations().back();
499         const double w = min((double)p.boundingRect(QRectF(), 0, text).width(),
500                 0.0) + h;
501         const QRectF rect(x - w / 2, y - h / 2, w, h);
502
503         p.drawRoundedRect(rect, h / 2, h / 2);
504
505         p.setPen(Qt::black);
506         p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
507 }
508
509 void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
510         int h, double start, double end, int y, const ViewItemPaintParams &pp,
511         int row_title_width) const
512 {
513         const double top = y + .5 - h / 2;
514         const double bottom = y + .5 + h / 2;
515         const vector<QString> annotations = a.annotations();
516
517         // If the two ends are within 1 pixel, draw a vertical line
518         if (start + 1.0 > end) {
519                 p.drawLine(QPointF(start, top), QPointF(start, bottom));
520                 return;
521         }
522
523         const double cap_width = min((end - start) / 4, EndCapWidth);
524
525         QPointF pts[] = {
526                 QPointF(start, y + .5f),
527                 QPointF(start + cap_width, top),
528                 QPointF(end - cap_width, top),
529                 QPointF(end, y + .5f),
530                 QPointF(end - cap_width, bottom),
531                 QPointF(start + cap_width, bottom)
532         };
533
534         p.drawConvexPolygon(pts, countof(pts));
535
536         if (annotations.empty())
537                 return;
538
539         const int ann_start = start + cap_width;
540         const int ann_end = end - cap_width;
541
542         const int real_start = max(ann_start, pp.left() + row_title_width);
543         const int real_end = min(ann_end, pp.right());
544         const int real_width = real_end - real_start;
545
546         QRectF rect(real_start, y - h / 2, real_width, h);
547         if (rect.width() <= 4)
548                 return;
549
550         p.setPen(Qt::black);
551
552         // Try to find an annotation that will fit
553         QString best_annotation;
554         int best_width = 0;
555
556         for (const QString &a : annotations) {
557                 const int w = p.boundingRect(QRectF(), 0, a).width();
558                 if (w <= rect.width() && w > best_width)
559                         best_annotation = a, best_width = w;
560         }
561
562         if (best_annotation.isEmpty())
563                 best_annotation = annotations.back();
564
565         // If not ellide the last in the list
566         p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
567                 best_annotation, Qt::ElideRight, rect.width()));
568 }
569
570 void DecodeTrace::draw_error(QPainter &p, const QString &message,
571         const ViewItemPaintParams &pp)
572 {
573         const int y = get_visual_y();
574
575         p.setPen(ErrorBgColour.darker());
576         p.setBrush(ErrorBgColour);
577
578         const QRectF bounding_rect =
579                 QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
580         const QRectF text_rect = p.boundingRect(bounding_rect,
581                 Qt::AlignCenter, message);
582         const float r = text_rect.height() / 4;
583
584         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
585                 Qt::AbsoluteSize);
586
587         p.setPen(Qt::black);
588         p.drawText(text_rect, message);
589 }
590
591 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
592         int right) const
593 {
594         using namespace pv::data;
595         using pv::data::decode::Decoder;
596
597         double samples_per_pixel, pixels_offset;
598
599         shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
600
601         assert(decoder_stack);
602
603         shared_ptr<Logic> data;
604         shared_ptr<data::SignalBase> signalbase;
605
606         const list< shared_ptr<Decoder> > &stack = decoder_stack->stack();
607
608         // We get the logic data of the first channel in the list.
609         // This works because we are currently assuming all
610         // LogicSignals have the same data/segment
611         for (const shared_ptr<Decoder> &dec : stack)
612                 if (dec && !dec->channels().empty() &&
613                         ((signalbase = (*dec->channels().begin()).second)) &&
614                         ((data = signalbase->logic_data())))
615                         break;
616
617         if (!data || data->logic_segments().empty())
618                 return;
619
620         const shared_ptr<LogicSegment> segment = data->logic_segments().front();
621         assert(segment);
622         const int64_t sample_count = (int64_t)segment->get_sample_count();
623         if (sample_count == 0)
624                 return;
625
626         const int64_t samples_decoded = decoder_stack->samples_decoded();
627         if (sample_count == samples_decoded)
628                 return;
629
630         const int y = get_visual_y();
631
632         tie(pixels_offset, samples_per_pixel) =
633                 get_pixels_offset_samples_per_pixel();
634
635         const double start = max(samples_decoded /
636                 samples_per_pixel - pixels_offset, left - 1.0);
637         const double end = min(sample_count / samples_per_pixel -
638                 pixels_offset, right + 1.0);
639         const QRectF no_decode_rect(start, y - (h / 2) + 0.5, end - start, h);
640
641         p.setPen(QPen(Qt::NoPen));
642         p.setBrush(Qt::white);
643         p.drawRect(no_decode_rect);
644
645         p.setPen(NoDecodeColour);
646         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
647         p.drawRect(no_decode_rect);
648 }
649
650 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
651 {
652         shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
653
654         assert(owner_);
655         assert(decoder_stack);
656
657         const View *view = owner_->view();
658         assert(view);
659
660         const double scale = view->scale();
661         assert(scale > 0);
662
663         const double pixels_offset =
664                 ((view->offset() - decoder_stack->start_time()) / scale).convert_to<double>();
665
666         double samplerate = decoder_stack->samplerate();
667
668         // Show sample rate as 1Hz when it is unknown
669         if (samplerate == 0.0)
670                 samplerate = 1.0;
671
672         return make_pair(pixels_offset, samplerate * scale);
673 }
674
675 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
676         int x_start, int x_end) const
677 {
678         double samples_per_pixel, pixels_offset;
679         tie(pixels_offset, samples_per_pixel) =
680                 get_pixels_offset_samples_per_pixel();
681
682         const uint64_t start = (uint64_t)max(
683                 (x_start + pixels_offset) * samples_per_pixel, 0.0);
684         const uint64_t end = (uint64_t)max(
685                 (x_end + pixels_offset) * samples_per_pixel, 0.0);
686
687         return make_pair(start, end);
688 }
689
690 int DecodeTrace::get_row_at_point(const QPoint &point)
691 {
692         if (!row_height_)
693                 return -1;
694
695         const int y = (point.y() - get_visual_y() + row_height_ / 2);
696
697         /* Integer divison of (x-1)/x would yield 0, so we check for this. */
698         if (y < 0)
699                 return -1;
700
701         const int row = y / row_height_;
702
703         if (row >= (int)visible_rows_.size())
704                 return -1;
705
706         return row;
707 }
708
709 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
710 {
711         using namespace pv::data::decode;
712
713         if (!enabled())
714                 return QString();
715
716         const pair<uint64_t, uint64_t> sample_range =
717                 get_sample_range(point.x(), point.x() + 1);
718         const int row = get_row_at_point(point);
719         if (row < 0)
720                 return QString();
721
722         vector<pv::data::decode::Annotation> annotations;
723
724         shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
725
726         assert(decoder_stack);
727         decoder_stack->get_annotation_subset(annotations, visible_rows_[row],
728                 sample_range.first, sample_range.second);
729
730         return (annotations.empty()) ?
731                 QString() : annotations[0].annotations().front();
732 }
733
734 void DecodeTrace::hover_point_changed()
735 {
736         assert(owner_);
737
738         const View *const view = owner_->view();
739         assert(view);
740
741         QPoint hp = view->hover_point();
742         QString ann = get_annotation_at_point(hp);
743
744         assert(view);
745
746         if (!row_height_ || ann.isEmpty()) {
747                 QToolTip::hideText();
748                 return;
749         }
750
751         const int hover_row = get_row_at_point(hp);
752
753         QFontMetrics m(QToolTip::font());
754         const QRect text_size = m.boundingRect(QRect(), 0, ann);
755
756         // This is OS-specific and unfortunately we can't query it, so
757         // use an approximation to at least try to minimize the error.
758         const int padding = 8;
759
760         // Make sure the tool tip doesn't overlap with the mouse cursor.
761         // If it did, the tool tip would constantly hide and re-appear.
762         // We also push it up by one row so that it appears above the
763         // decode trace, not below.
764         hp.setX(hp.x() - (text_size.width() / 2) - padding);
765
766         hp.setY(get_visual_y() - (row_height_ / 2) +
767                 (hover_row * row_height_) -
768                 row_height_ - text_size.height() - padding);
769
770         QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
771 }
772
773 void DecodeTrace::create_decoder_form(int index,
774         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
775         QFormLayout *form)
776 {
777         const GSList *l;
778         GlobalSettings settings;
779
780         assert(dec);
781         const srd_decoder *const decoder = dec->decoder();
782         assert(decoder);
783
784         const bool decoder_deletable = index > 0;
785
786         pv::widgets::DecoderGroupBox *const group =
787                 new pv::widgets::DecoderGroupBox(
788                         QString::fromUtf8(decoder->name),
789                         tr("%1:\n%2").arg(QString::fromUtf8(decoder->longname),
790                                 QString::fromUtf8(decoder->desc)),
791                         nullptr, decoder_deletable);
792         group->set_decoder_visible(dec->shown());
793
794         if (decoder_deletable) {
795                 delete_mapper_.setMapping(group, index);
796                 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
797         }
798
799         show_hide_mapper_.setMapping(group, index);
800         connect(group, SIGNAL(show_hide_decoder()),
801                 &show_hide_mapper_, SLOT(map()));
802
803         QFormLayout *const decoder_form = new QFormLayout;
804         group->add_layout(decoder_form);
805
806         // Add the mandatory channels
807         for (l = decoder->channels; l; l = l->next) {
808                 const struct srd_channel *const pdch =
809                         (struct srd_channel *)l->data;
810
811                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
812                 QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
813
814                 connect(combo, SIGNAL(currentIndexChanged(int)),
815                         this, SLOT(on_channel_selected(int)));
816                 connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
817                         this, SLOT(on_initial_pin_selected(int)));
818
819                 QHBoxLayout *const hlayout = new QHBoxLayout;
820                 hlayout->addWidget(combo);
821                 hlayout->addWidget(combo_initial_pin);
822
823                 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
824                         combo_initial_pin->hide();
825
826                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
827                         .arg(QString::fromUtf8(pdch->name),
828                              QString::fromUtf8(pdch->desc)), hlayout);
829
830                 const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
831                 channel_selectors_.push_back(s);
832         }
833
834         // Add the optional channels
835         for (l = decoder->opt_channels; l; l = l->next) {
836                 const struct srd_channel *const pdch =
837                         (struct srd_channel *)l->data;
838
839                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
840                 QComboBox *const combo_initial_pin = create_channel_selector_initial_pin(parent, dec, pdch);
841
842                 connect(combo, SIGNAL(currentIndexChanged(int)),
843                         this, SLOT(on_channel_selected(int)));
844                 connect(combo_initial_pin, SIGNAL(currentIndexChanged(int)),
845                         this, SLOT(on_initial_pin_selected(int)));
846
847                 QHBoxLayout *const hlayout = new QHBoxLayout;
848                 hlayout->addWidget(combo);
849                 hlayout->addWidget(combo_initial_pin);
850
851                 if (!settings.value(GlobalSettings::Key_Dec_InitialStateConfigurable).toBool())
852                         combo_initial_pin->hide();
853
854                 decoder_form->addRow(tr("<b>%1</b> (%2)")
855                         .arg(QString::fromUtf8(pdch->name),
856                              QString::fromUtf8(pdch->desc)), hlayout);
857
858                 const ChannelSelector s = {combo, combo_initial_pin, dec, pdch};
859                 channel_selectors_.push_back(s);
860         }
861
862         shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
863
864         // Add the options
865         shared_ptr<binding::Decoder> binding(
866                 new binding::Decoder(decoder_stack, dec));
867         binding->add_properties_to_form(decoder_form, true);
868
869         bindings_.push_back(binding);
870
871         form->addRow(group);
872         decoder_forms_.push_back(group);
873 }
874
875 QComboBox* DecodeTrace::create_channel_selector(
876         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
877         const srd_channel *const pdch)
878 {
879         assert(dec);
880
881         const auto sigs(session_.signalbases());
882
883         vector< shared_ptr<data::SignalBase> > sig_list(sigs.begin(), sigs.end());
884         sort(sig_list.begin(), sig_list.end(),
885                 [](const shared_ptr<data::SignalBase> &a,
886                 const shared_ptr<data::SignalBase> &b) {
887                         return strnatcasecmp(a->name().toStdString(),
888                                 b->name().toStdString()) < 0; });
889
890         const auto channel_iter = dec->channels().find(pdch);
891
892         QComboBox *selector = new QComboBox(parent);
893
894         selector->addItem("-", qVariantFromValue((void*)nullptr));
895
896         if (channel_iter == dec->channels().end())
897                 selector->setCurrentIndex(0);
898
899         for (const shared_ptr<data::SignalBase> &b : sig_list) {
900                 assert(b);
901                 if (b->logic_data() && b->enabled()) {
902                         selector->addItem(b->name(),
903                                 qVariantFromValue((void*)b.get()));
904
905                         if (channel_iter != dec->channels().end() &&
906                                 (*channel_iter).second == b)
907                                 selector->setCurrentIndex(
908                                         selector->count() - 1);
909                 }
910         }
911
912         return selector;
913 }
914
915 QComboBox* DecodeTrace::create_channel_selector_initial_pin(QWidget *parent,
916         const shared_ptr<data::decode::Decoder> &dec, const srd_channel *const pdch)
917 {
918         QComboBox *selector = new QComboBox(parent);
919
920         selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
921         selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
922         selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
923
924         // Default to index 2 (SRD_INITIAL_PIN_SAME_AS_SAMPLE0).
925         const int idx = (!dec->initial_pins()) ? 2 : dec->initial_pins()->data[pdch->order];
926         selector->setCurrentIndex(idx);
927
928         selector->setToolTip("Initial (assumed) pin value before the first sample");
929
930         return selector;
931 }
932
933 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
934 {
935         assert(dec);
936
937         map<const srd_channel*, shared_ptr<data::SignalBase> > channel_map;
938
939         const unordered_set< shared_ptr<data::SignalBase> >
940                 sigs(session_.signalbases());
941
942         GArray *const initial_pins = g_array_sized_new(FALSE, TRUE,
943                 sizeof(uint8_t), channel_selectors_.size());
944         g_array_set_size(initial_pins, channel_selectors_.size());
945
946         for (const ChannelSelector &s : channel_selectors_) {
947                 if (s.decoder_ != dec)
948                         break;
949
950                 const data::SignalBase *const selection =
951                         (data::SignalBase*)s.combo_->itemData(
952                                 s.combo_->currentIndex()).value<void*>();
953
954                 for (shared_ptr<data::SignalBase> sig : sigs)
955                         if (sig.get() == selection) {
956                                 channel_map[s.pdch_] = sig;
957                                 break;
958                         }
959
960                 int selection_initial_pin = s.combo_initial_pin_->itemData(
961                         s.combo_initial_pin_->currentIndex()).value<int>();
962
963                 initial_pins->data[s.pdch_->order] = selection_initial_pin;
964         }
965
966         dec->set_channels(channel_map);
967         dec->set_initial_pins(initial_pins);
968 }
969
970 void DecodeTrace::commit_channels()
971 {
972         shared_ptr<pv::data::DecoderStack> decoder_stack = base_->decoder_stack();
973
974         assert(decoder_stack);
975         for (shared_ptr<data::decode::Decoder> dec : decoder_stack->stack())
976                 commit_decoder_channels(dec);
977
978         decoder_stack->begin_decode();
979 }
980
981 void DecodeTrace::on_new_annotations()
982 {
983         if (owner_)
984                 owner_->row_item_appearance_changed(false, true);
985 }
986
987 void DecodeTrace::delete_pressed()
988 {
989         on_delete();
990 }
991
992 void DecodeTrace::on_delete()
993 {
994         session_.remove_decode_signal(decode_signal_);
995 }
996
997 void DecodeTrace::on_channel_selected(int)
998 {
999         commit_channels();
1000 }
1001
1002 void DecodeTrace::on_initial_pin_selected(int)
1003 {
1004         commit_channels();
1005 }
1006
1007 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
1008 {
1009         decode_signal_->stack_decoder(decoder);
1010
1011         create_popup_form();
1012 }
1013
1014 void DecodeTrace::on_delete_decoder(int index)
1015 {
1016         decode_signal_->remove_decoder(index);
1017
1018         // Update the popup
1019         create_popup_form();
1020 }
1021
1022 void DecodeTrace::on_show_hide_decoder(int index)
1023 {
1024         const bool state = decode_signal_->toggle_decoder_visibility(index);
1025
1026         assert(index < (int)decoder_forms_.size());
1027         decoder_forms_[index]->set_decoder_visible(state);
1028
1029         if (owner_)
1030                 owner_->row_item_appearance_changed(false, true);
1031 }
1032
1033 } // namespace trace
1034 } // namespace views
1035 } // namespace pv