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