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