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