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