]> sigrok.org Git - pulseview.git/blob - pv/view/decodetrace.cpp
eb1f7f83d01324d1527e81ffdb890e17a2194dcd
[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, pp);
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 ViewItemPaintParams &pp) 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         const int ann_start = start + cap_width;
520         const int ann_end = end - cap_width;
521
522         const int real_start = std::max(ann_start, pp.left());
523         const int real_end = std::min(ann_end, pp.right());
524
525         const int real_width = real_end - real_start;
526
527         QRectF rect(real_start, y - h / 2, real_width, h);
528         if (rect.width() <= 4)
529                 return;
530
531         p.setPen(Qt::black);
532
533         // Try to find an annotation that will fit
534         QString best_annotation;
535         int best_width = 0;
536
537         for (const QString &a : annotations) {
538                 const int w = p.boundingRect(QRectF(), 0, a).width();
539                 if (w <= rect.width() && w > best_width)
540                         best_annotation = a, best_width = w;
541         }
542
543         if (best_annotation.isEmpty())
544                 best_annotation = annotations.back();
545
546         // If not ellide the last in the list
547         p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
548                 best_annotation, Qt::ElideRight, rect.width()));
549 }
550
551 void DecodeTrace::draw_error(QPainter &p, const QString &message,
552         const ViewItemPaintParams &pp)
553 {
554         const int y = get_visual_y();
555
556         p.setPen(ErrorBgColour.darker());
557         p.setBrush(ErrorBgColour);
558
559         const QRectF bounding_rect =
560                 QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
561         const QRectF text_rect = p.boundingRect(bounding_rect,
562                 Qt::AlignCenter, message);
563         const float r = text_rect.height() / 4;
564
565         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
566                 Qt::AbsoluteSize);
567
568         p.setPen(Qt::black);
569         p.drawText(text_rect, message);
570 }
571
572 void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
573         int right) const
574 {
575         using namespace pv::data;
576         using pv::data::decode::Decoder;
577
578         double samples_per_pixel, pixels_offset;
579
580         assert(decoder_stack_); 
581
582         shared_ptr<Logic> data;
583         shared_ptr<LogicSignal> logic_signal;
584
585         const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
586
587         // We get the logic data of the first channel in the list.
588         // This works because we are currently assuming all
589         // LogicSignals have the same data/segment
590         for (const shared_ptr<Decoder> &dec : stack)
591                 if (dec && !dec->channels().empty() &&
592                         ((logic_signal = (*dec->channels().begin()).second)) &&
593                         ((data = logic_signal->logic_data())))
594                         break;
595
596         if (!data || data->logic_segments().empty())
597                 return;
598
599         const shared_ptr<LogicSegment> segment =
600                 data->logic_segments().front();
601         assert(segment);
602         const int64_t sample_count = (int64_t)segment->get_sample_count();
603         if (sample_count == 0)
604                 return;
605
606         const int64_t samples_decoded = decoder_stack_->samples_decoded();
607         if (sample_count == samples_decoded)
608                 return;
609
610         const int y = get_visual_y();
611
612         tie(pixels_offset, samples_per_pixel) =
613                 get_pixels_offset_samples_per_pixel();
614
615         const double start = max(samples_decoded /
616                 samples_per_pixel - pixels_offset, left - 1.0);
617         const double end = min(sample_count / samples_per_pixel -
618                 pixels_offset, right + 1.0);
619         const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
620
621         p.setPen(QPen(Qt::NoPen));
622         p.setBrush(Qt::white);
623         p.drawRect(no_decode_rect);
624
625         p.setPen(NoDecodeColour);
626         p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
627         p.drawRect(no_decode_rect);
628 }
629
630 pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
631 {
632         assert(owner_);
633         assert(decoder_stack_);
634
635         const View *view = owner_->view();
636         assert(view);
637
638         const double scale = view->scale();
639         assert(scale > 0);
640
641         const double pixels_offset =
642                 ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
643
644         double samplerate = decoder_stack_->samplerate();
645
646         // Show sample rate as 1Hz when it is unknown
647         if (samplerate == 0.0)
648                 samplerate = 1.0;
649
650         return make_pair(pixels_offset, samplerate * scale);
651 }
652
653 pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
654         int x_start, int x_end) const
655 {
656         double samples_per_pixel, pixels_offset;
657         tie(pixels_offset, samples_per_pixel) =
658                 get_pixels_offset_samples_per_pixel();
659
660         const uint64_t start = (uint64_t)max(
661                 (x_start + pixels_offset) * samples_per_pixel, 0.0);
662         const uint64_t end = (uint64_t)max(
663                 (x_end + pixels_offset) * samples_per_pixel, 0.0);
664
665         return make_pair(start, end);
666 }
667
668 int DecodeTrace::get_row_at_point(const QPoint &point)
669 {
670         if (!row_height_)
671                 return -1;
672
673         const int y = (point.y() - get_visual_y() + row_height_ / 2);
674
675         /* Integer divison of (x-1)/x would yield 0, so we check for this. */
676         if (y < 0)
677                 return -1;
678
679         const int row = y / row_height_;
680
681         if (row >= (int)visible_rows_.size())
682                 return -1;
683
684         return row;
685 }
686
687 const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
688 {
689         using namespace pv::data::decode;
690
691         if (!enabled())
692                 return QString();
693
694         const pair<uint64_t, uint64_t> sample_range =
695                 get_sample_range(point.x(), point.x() + 1);
696         const int row = get_row_at_point(point);
697         if (row < 0)
698                 return QString();
699
700         vector<pv::data::decode::Annotation> annotations;
701
702         assert(decoder_stack_);
703         decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
704                 sample_range.first, sample_range.second);
705
706         return (annotations.empty()) ?
707                 QString() : annotations[0].annotations().front();
708 }
709
710 void DecodeTrace::hover_point_changed()
711 {
712         assert(owner_);
713
714         const View *const view = owner_->view();
715         assert(view);
716
717         QPoint hp = view->hover_point();
718         QString ann = get_annotation_at_point(hp);
719
720         assert(view);
721
722         if (!row_height_ || ann.isEmpty()) {
723                 QToolTip::hideText();
724                 return;
725         }
726
727         const int hover_row = get_row_at_point(hp);
728
729         QFontMetrics m(QToolTip::font());
730         const QRect text_size = m.boundingRect(QRect(), 0, ann);
731
732         // This is OS-specific and unfortunately we can't query it, so
733         // use an approximation to at least try to minimize the error.
734         const int padding = 8;
735
736         // Make sure the tool tip doesn't overlap with the mouse cursor.
737         // If it did, the tool tip would constantly hide and re-appear.
738         // We also push it up by one row so that it appears above the
739         // decode trace, not below.
740         hp.setX(hp.x() - (text_size.width() / 2) - padding);
741
742         hp.setY(get_visual_y() - (row_height_ / 2) +
743                 (hover_row * row_height_) -
744                 row_height_ - text_size.height() - padding);
745
746         QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
747 }
748
749 void DecodeTrace::create_decoder_form(int index,
750         shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
751         QFormLayout *form)
752 {
753         const GSList *l;
754
755         assert(dec);
756         const srd_decoder *const decoder = dec->decoder();
757         assert(decoder);
758
759         const bool decoder_deletable = index > 0;
760
761         pv::widgets::DecoderGroupBox *const group =
762                 new pv::widgets::DecoderGroupBox(
763                         QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
764         group->set_decoder_visible(dec->shown());
765
766         if (decoder_deletable) {
767                 delete_mapper_.setMapping(group, index);
768                 connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
769         }
770
771         show_hide_mapper_.setMapping(group, index);
772         connect(group, SIGNAL(show_hide_decoder()),
773                 &show_hide_mapper_, SLOT(map()));
774
775         QFormLayout *const decoder_form = new QFormLayout;
776         group->add_layout(decoder_form);
777
778         // Add the mandatory channels
779         for (l = decoder->channels; l; l = l->next) {
780                 const struct srd_channel *const pdch =
781                         (struct srd_channel *)l->data;
782                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
783                 connect(combo, SIGNAL(currentIndexChanged(int)),
784                         this, SLOT(on_channel_selected(int)));
785                 decoder_form->addRow(tr("<b>%1</b> (%2) *")
786                         .arg(QString::fromUtf8(pdch->name))
787                         .arg(QString::fromUtf8(pdch->desc)), combo);
788
789                 const ChannelSelector s = {combo, dec, pdch};
790                 channel_selectors_.push_back(s);
791         }
792
793         // Add the optional channels
794         for (l = decoder->opt_channels; l; l = l->next) {
795                 const struct srd_channel *const pdch =
796                         (struct srd_channel *)l->data;
797                 QComboBox *const combo = create_channel_selector(parent, dec, pdch);
798                 connect(combo, SIGNAL(currentIndexChanged(int)),
799                         this, SLOT(on_channel_selected(int)));
800                 decoder_form->addRow(tr("<b>%1</b> (%2)")
801                         .arg(QString::fromUtf8(pdch->name))
802                         .arg(QString::fromUtf8(pdch->desc)), combo);
803
804                 const ChannelSelector s = {combo, dec, pdch};
805                 channel_selectors_.push_back(s);
806         }
807
808         // Add the options
809         shared_ptr<binding::Decoder> binding(
810                 new binding::Decoder(decoder_stack_, dec));
811         binding->add_properties_to_form(decoder_form, true);
812
813         bindings_.push_back(binding);
814
815         form->addRow(group);
816         decoder_forms_.push_back(group);
817 }
818
819 QComboBox* DecodeTrace::create_channel_selector(
820         QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
821         const srd_channel *const pdch)
822 {
823         assert(dec);
824
825         const auto sigs(session_.signals());
826
827         vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
828         std::sort(sig_list.begin(), sig_list.end(),
829                 [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
830                         return a->name().compare(b->name()) < 0; });
831
832         assert(decoder_stack_);
833         const auto channel_iter = dec->channels().find(pdch);
834
835         QComboBox *selector = new QComboBox(parent);
836
837         selector->addItem("-", qVariantFromValue((void*)nullptr));
838
839         if (channel_iter == dec->channels().end())
840                 selector->setCurrentIndex(0);
841
842         for (const shared_ptr<view::Signal> &s : sig_list) {
843                 assert(s);
844                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
845                         selector->addItem(s->name(),
846                                 qVariantFromValue((void*)s.get()));
847
848                         if (channel_iter != dec->channels().end() &&
849                                 (*channel_iter).second == s)
850                                 selector->setCurrentIndex(
851                                         selector->count() - 1);
852                 }
853         }
854
855         return selector;
856 }
857
858 void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
859 {
860         assert(dec);
861
862         map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
863
864         const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
865
866         for (const ChannelSelector &s : channel_selectors_) {
867                 if (s.decoder_ != dec)
868                         break;
869
870                 const LogicSignal *const selection =
871                         (LogicSignal*)s.combo_->itemData(
872                                 s.combo_->currentIndex()).value<void*>();
873
874                 for (shared_ptr<Signal> sig : sigs)
875                         if (sig.get() == selection) {
876                                 channel_map[s.pdch_] =
877                                         dynamic_pointer_cast<LogicSignal>(sig);
878                                 break;
879                         }
880         }
881
882         dec->set_channels(channel_map);
883 }
884
885 void DecodeTrace::commit_channels()
886 {
887         assert(decoder_stack_);
888         for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
889                 commit_decoder_channels(dec);
890
891         decoder_stack_->begin_decode();
892 }
893
894 void DecodeTrace::on_new_decode_data()
895 {
896         if (owner_)
897                 owner_->row_item_appearance_changed(false, true);
898 }
899
900 void DecodeTrace::delete_pressed()
901 {
902         on_delete();
903 }
904
905 void DecodeTrace::on_delete()
906 {
907         session_.remove_decode_signal(this);
908 }
909
910 void DecodeTrace::on_channel_selected(int)
911 {
912         commit_channels();
913 }
914
915 void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
916 {
917         assert(decoder);
918         assert(decoder_stack_);
919         decoder_stack_->push(shared_ptr<data::decode::Decoder>(
920                 new data::decode::Decoder(decoder)));
921         decoder_stack_->begin_decode();
922
923         create_popup_form();
924 }
925
926 void DecodeTrace::on_delete_decoder(int index)
927 {
928         decoder_stack_->remove(index);
929
930         // Update the popup
931         create_popup_form();    
932
933         decoder_stack_->begin_decode();
934 }
935
936 void DecodeTrace::on_show_hide_decoder(int index)
937 {
938         using pv::data::decode::Decoder;
939
940         const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
941
942         // Find the decoder in the stack
943         auto iter = stack.cbegin();
944         for (int i = 0; i < index; i++, iter++)
945                 assert(iter != stack.end());
946
947         shared_ptr<Decoder> dec = *iter;
948         assert(dec);
949
950         const bool show = !dec->shown();
951         dec->show(show);
952
953         assert(index < (int)decoder_forms_.size());
954         decoder_forms_[index]->set_decoder_visible(show);
955
956         if (owner_)
957                 owner_->row_item_appearance_changed(false, true);
958 }
959
960 } // namespace view
961 } // namespace pv