]> sigrok.org Git - pulseview.git/blob - pv/view/view.cpp
CursorPair: Moved in ViewportFillColour
[pulseview.git] / pv / view / view.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 #ifdef ENABLE_DECODE
22 #include <libsigrokdecode/libsigrokdecode.h>
23 #endif
24
25 #include <extdef.h>
26
27 #include <cassert>
28 #include <climits>
29 #include <cmath>
30 #include <mutex>
31 #include <unordered_set>
32
33 #include <QApplication>
34 #include <QEvent>
35 #include <QFontMetrics>
36 #include <QMouseEvent>
37 #include <QScrollBar>
38
39 #include <libsigrok/libsigrok.hpp>
40
41 #include "decodetrace.hpp"
42 #include "header.hpp"
43 #include "logicsignal.hpp"
44 #include "ruler.hpp"
45 #include "signal.hpp"
46 #include "tracegroup.hpp"
47 #include "view.hpp"
48 #include "viewport.hpp"
49
50 #include "pv/session.hpp"
51 #include "pv/data/logic.hpp"
52 #include "pv/data/logicsegment.hpp"
53 #include "pv/util.hpp"
54
55 using boost::shared_lock;
56 using boost::shared_mutex;
57
58 using pv::data::SignalData;
59 using pv::data::Segment;
60 using pv::util::format_time;
61
62 using std::deque;
63 using std::dynamic_pointer_cast;
64 using std::list;
65 using std::lock_guard;
66 using std::max;
67 using std::make_pair;
68 using std::min;
69 using std::pair;
70 using std::set;
71 using std::shared_ptr;
72 using std::unordered_map;
73 using std::unordered_set;
74 using std::vector;
75 using std::weak_ptr;
76
77 namespace pv {
78 namespace view {
79
80 const double View::MaxScale = 1e9;
81 const double View::MinScale = 1e-15;
82
83 const int View::MaxScrollValue = INT_MAX / 2;
84
85 const int View::ScaleUnits[3] = {1, 2, 5};
86
87 const QSizeF View::LabelPadding(4, 0);
88
89 View::View(Session &session, QWidget *parent) :
90         QAbstractScrollArea(parent),
91         session_(session),
92         viewport_(new Viewport(*this)),
93         ruler_(new Ruler(*this)),
94         header_(new Header(*this)),
95         scale_(1e-6),
96         offset_(0),
97         v_offset_(0),
98         updating_scroll_(false),
99         tick_period_(0.0),
100         tick_prefix_(0),
101         show_cursors_(false),
102         cursors_(new CursorPair(*this)),
103         next_flag_text_('A'),
104         hover_point_(-1, -1)
105 {
106         connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
107                 this, SLOT(h_scroll_value_changed(int)));
108         connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
109                 this, SLOT(v_scroll_value_changed(int)));
110
111         connect(&session_, SIGNAL(signals_changed()),
112                 this, SLOT(signals_changed()));
113         connect(&session_, SIGNAL(capture_state_changed(int)),
114                 this, SLOT(data_updated()));
115         connect(&session_, SIGNAL(data_received()),
116                 this, SLOT(data_updated()));
117         connect(&session_, SIGNAL(frame_ended()),
118                 this, SLOT(data_updated()));
119
120         connect(header_, SIGNAL(signals_moved()),
121                 this, SLOT(on_signals_moved()));
122
123         connect(header_, SIGNAL(selection_changed()),
124                 ruler_, SLOT(clear_selection()));
125         connect(ruler_, SIGNAL(selection_changed()),
126                 header_, SLOT(clear_selection()));
127
128         connect(header_, SIGNAL(selection_changed()),
129                 this, SIGNAL(selection_changed()));
130         connect(ruler_, SIGNAL(selection_changed()),
131                 this, SIGNAL(selection_changed()));
132
133         connect(this, SIGNAL(hover_point_changed()),
134                 this, SLOT(on_hover_point_changed()));
135
136         connect(&lazy_event_handler_, SIGNAL(timeout()),
137                 this, SLOT(process_sticky_events()));
138         lazy_event_handler_.setSingleShot(true);
139
140         setViewport(viewport_);
141
142         viewport_->installEventFilter(this);
143         ruler_->installEventFilter(this);
144         header_->installEventFilter(this);
145
146         // Trigger the initial event manually. The default device has signals
147         // which were created before this object came into being
148         signals_changed();
149
150         // make sure the transparent widgets are on the top
151         ruler_->raise();
152         header_->raise();
153
154         // Update the zoom state
155         calculate_tick_spacing();
156 }
157
158 Session& View::session()
159 {
160         return session_;
161 }
162
163 const Session& View::session() const
164 {
165         return session_;
166 }
167
168 View* View::view()
169 {
170         return this;
171 }
172
173 const View* View::view() const
174 {
175         return this;
176 }
177
178 Viewport* View::viewport()
179 {
180         return viewport_;
181 }
182
183 const Viewport* View::viewport() const
184 {
185         return viewport_;
186 }
187
188 vector< shared_ptr<TimeItem> > View::time_items() const
189 {
190         const vector<shared_ptr<Flag>> f(flags());
191         vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
192         items.push_back(cursors_);
193         items.push_back(cursors_->first());
194         items.push_back(cursors_->second());
195         return items;
196 }
197
198 double View::scale() const
199 {
200         return scale_;
201 }
202
203 double View::offset() const
204 {
205         return offset_;
206 }
207
208 int View::owner_visual_v_offset() const
209 {
210         return -v_offset_;
211 }
212
213 unsigned int View::depth() const
214 {
215         return 0;
216 }
217
218 unsigned int View::tick_prefix() const
219 {
220         return tick_prefix_;
221 }
222
223 double View::tick_period() const
224 {
225         return tick_period_;
226 }
227
228 void View::zoom(double steps)
229 {
230         zoom(steps, viewport_->width() / 2);
231 }
232
233 void View::zoom(double steps, int offset)
234 {
235         set_zoom(scale_ * pow(3.0/2.0, -steps), offset);
236 }
237
238 void View::zoom_fit()
239 {
240         const pair<double, double> extents = get_time_extents();
241         const double delta = extents.second - extents.first;
242         if (delta < 1e-12)
243                 return;
244
245         assert(viewport_);
246         const int w = viewport_->width();
247         if (w <= 0)
248                 return;
249
250         const double scale = max(min(delta / w, MaxScale), MinScale);
251         set_scale_offset(scale, extents.first);
252 }
253
254 void View::zoom_one_to_one()
255 {
256         using pv::data::SignalData;
257
258         // Make a set of all the visible data objects
259         set< shared_ptr<SignalData> > visible_data = get_visible_data();
260         if (visible_data.empty())
261                 return;
262
263         double samplerate = 0.0;
264         for (const shared_ptr<SignalData> d : visible_data) {
265                 assert(d);
266                 const vector< shared_ptr<Segment> > segments =
267                         d->segments();
268                 for (const shared_ptr<Segment> &s : segments)
269                         samplerate = max(samplerate, s->samplerate());
270         }
271
272         if (samplerate == 0.0)
273                 return;
274
275         assert(viewport_);
276         const int w = viewport_->width();
277         if (w <= 0)
278                 return;
279
280         set_zoom(1.0 / samplerate, w / 2);
281 }
282
283 void View::set_scale_offset(double scale, double offset)
284 {
285         scale_ = scale;
286         offset_ = offset;
287
288         calculate_tick_spacing();
289
290         update_scroll();
291         ruler_->update();
292         viewport_->update();
293         scale_offset_changed();
294 }
295
296 set< shared_ptr<SignalData> > View::get_visible_data() const
297 {
298         shared_lock<shared_mutex> lock(session().signals_mutex());
299         const vector< shared_ptr<Signal> > &sigs(session().signals());
300
301         // Make a set of all the visible data objects
302         set< shared_ptr<SignalData> > visible_data;
303         for (const shared_ptr<Signal> sig : sigs)
304                 if (sig->enabled())
305                         visible_data.insert(sig->data());
306
307         return visible_data;
308 }
309
310 pair<double, double> View::get_time_extents() const
311 {
312         double left_time = DBL_MAX, right_time = DBL_MIN;
313         const set< shared_ptr<SignalData> > visible_data = get_visible_data();
314         for (const shared_ptr<SignalData> d : visible_data)
315         {
316                 const vector< shared_ptr<Segment> > segments =
317                         d->segments();
318                 for (const shared_ptr<Segment> &s : segments) {
319                         double samplerate = s->samplerate();
320                         samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
321
322                         const double start_time = s->start_time();
323                         left_time = min(left_time, start_time);
324                         right_time = max(right_time, start_time +
325                                 d->get_max_sample_count() / samplerate);
326                 }
327         }
328
329         if (left_time == DBL_MAX && right_time == DBL_MIN)
330                 return make_pair(0.0, 0.0);
331
332         assert(left_time < right_time);
333         return make_pair(left_time, right_time);
334 }
335
336 bool View::cursors_shown() const
337 {
338         return show_cursors_;
339 }
340
341 void View::show_cursors(bool show)
342 {
343         show_cursors_ = show;
344         ruler_->update();
345         viewport_->update();
346 }
347
348 void View::centre_cursors()
349 {
350         const double time_width = scale_ * viewport_->width();
351         cursors_->first()->set_time(offset_ + time_width * 0.4);
352         cursors_->second()->set_time(offset_ + time_width * 0.6);
353         ruler_->update();
354         viewport_->update();
355 }
356
357 std::shared_ptr<CursorPair> View::cursors() const
358 {
359         return cursors_;
360 }
361
362 void View::add_flag(double time)
363 {
364         flags_.push_back(shared_ptr<Flag>(new Flag(*this, time,
365                 QString("%1").arg(next_flag_text_))));
366         next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
367                 (next_flag_text_ + 1);
368         time_item_appearance_changed(true, true);
369 }
370
371 void View::remove_flag(std::shared_ptr<Flag> flag)
372 {
373         flags_.remove(flag);
374         time_item_appearance_changed(true, true);
375 }
376
377 vector< std::shared_ptr<Flag> > View::flags() const
378 {
379         vector< std::shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
380         stable_sort(flags.begin(), flags.end(),
381                 [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
382                         return a->time() < b->time();
383                 });
384
385         return flags;
386 }
387
388 const QPoint& View::hover_point() const
389 {
390         return hover_point_;
391 }
392
393 void View::update_viewport()
394 {
395         assert(viewport_);
396         viewport_->update();
397         header_->update();
398 }
399
400 void View::restack_all_row_items()
401 {
402         // Make a set of owners
403         unordered_set< RowItemOwner* > owners;
404         for (const auto &r : *this)
405                 owners.insert(r->owner());
406
407         // Make a list that is sorted from deepest first
408         vector< RowItemOwner* > sorted_owners(owners.begin(), owners.end());
409         sort(sorted_owners.begin(), sorted_owners.end(),
410                 [](const RowItemOwner* a, const RowItemOwner *b) {
411                         return a->depth() > b->depth(); });
412
413         // Restack the items recursively
414         for (auto &o : sorted_owners)
415                 o->restack_items();
416
417         // Animate the items to their destination
418         for (const auto &r : *this)
419                 r->animate_to_layout_v_offset();
420 }
421
422 void View::get_scroll_layout(double &length, double &offset) const
423 {
424         const pair<double, double> extents = get_time_extents();
425         length = (extents.second - extents.first) / scale_;
426         offset = offset_ / scale_;
427 }
428
429 void View::set_zoom(double scale, int offset)
430 {
431         const double cursor_offset = offset_ + scale_ * offset;
432         const double new_scale = max(min(scale, MaxScale), MinScale);
433         const double new_offset = cursor_offset - new_scale * offset;
434         set_scale_offset(new_scale, new_offset);
435 }
436
437 void View::calculate_tick_spacing()
438 {
439         const double SpacingIncrement = 32.0f;
440         const double MinValueSpacing = 32.0f;
441
442         double min_width = SpacingIncrement, typical_width;
443
444         QFontMetrics m(QApplication::font());
445
446         do {
447                 const double min_period = scale_ * min_width;
448
449                 const int order = (int)floorf(log10f(min_period));
450                 const double order_decimal = pow(10.0, order);
451
452                 unsigned int unit = 0;
453
454                 do {
455                         tick_period_ = order_decimal * ScaleUnits[unit++];
456                 } while (tick_period_ < min_period &&
457                         unit < countof(ScaleUnits));
458
459                 tick_prefix_ = (order - pv::util::FirstSIPrefixPower) / 3;
460
461                 typical_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
462                         Qt::AlignLeft | Qt::AlignTop,
463                         format_time(offset_, tick_prefix_)).width() +
464                                 MinValueSpacing;
465
466                 min_width += SpacingIncrement;
467
468         } while(typical_width > tick_period_ / scale_);
469 }
470
471 void View::update_scroll()
472 {
473         assert(viewport_);
474
475         const QSize areaSize = viewport_->size();
476
477         // Set the horizontal scroll bar
478         double length = 0, offset = 0;
479         get_scroll_layout(length, offset);
480         length = max(length - areaSize.width(), 0.0);
481
482         int major_tick_distance = tick_period_ / scale_;
483
484         horizontalScrollBar()->setPageStep(areaSize.width() / 2);
485         horizontalScrollBar()->setSingleStep(major_tick_distance);
486
487         updating_scroll_ = true;
488
489         if (length < MaxScrollValue) {
490                 horizontalScrollBar()->setRange(0, length);
491                 horizontalScrollBar()->setSliderPosition(offset);
492         } else {
493                 horizontalScrollBar()->setRange(0, MaxScrollValue);
494                 horizontalScrollBar()->setSliderPosition(
495                         offset_ * MaxScrollValue / (scale_ * length));
496         }
497
498         updating_scroll_ = false;
499
500         // Set the vertical scrollbar
501         verticalScrollBar()->setPageStep(areaSize.height());
502         verticalScrollBar()->setSingleStep(areaSize.height() / 8);
503
504         const pair<int, int> extents = v_extents();
505         verticalScrollBar()->setRange(extents.first - (areaSize.height() / 2),
506                 extents.second - (areaSize.height() / 2));
507 }
508
509 void View::update_layout()
510 {
511         setViewportMargins(
512                 header_->sizeHint().width() - pv::view::Header::BaselineOffset,
513                 ruler_->sizeHint().height(), 0, 0);
514         ruler_->setGeometry(viewport_->x(), 0,
515                 viewport_->width(), ruler_->extended_size_hint().height());
516         header_->setGeometry(0, viewport_->y(),
517                 header_->extended_size_hint().width(), viewport_->height());
518         update_scroll();
519 }
520
521 void View::paint_label(QPainter &p, const QRect &rect, bool hover)
522 {
523         (void)p;
524         (void)rect;
525         (void)hover;
526 }
527
528 QRectF View::label_rect(const QRectF &rect)
529 {
530         (void)rect;
531         return QRectF();
532 }
533
534 bool View::add_channels_to_owner(
535         const vector< shared_ptr<sigrok::Channel> > &channels,
536         RowItemOwner *owner, int &offset,
537         unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
538                 &signal_map,
539         std::function<bool (shared_ptr<RowItem>)> filter_func)
540 {
541         bool any_added = false;
542
543         assert(owner);
544
545         for (const auto &channel : channels)
546         {
547                 const auto iter = signal_map.find(channel);
548                 if (iter == signal_map.end() ||
549                         (filter_func && !filter_func((*iter).second)))
550                         continue;
551
552                 shared_ptr<RowItem> row_item = (*iter).second;
553                 owner->add_child_item(row_item);
554                 apply_offset(row_item, offset);
555                 signal_map.erase(iter);
556
557                 any_added = true;
558         }
559
560         return any_added;
561 }
562
563 void View::apply_offset(shared_ptr<RowItem> row_item, int &offset) {
564         assert(row_item);
565         const pair<int, int> extents = row_item->v_extents();
566         if (row_item->enabled())
567                 offset += -extents.first;
568         row_item->force_to_v_offset(offset);
569         if (row_item->enabled())
570                 offset += extents.second;
571 }
572
573 bool View::eventFilter(QObject *object, QEvent *event)
574 {
575         const QEvent::Type type = event->type();
576         if (type == QEvent::MouseMove) {
577
578                 const QMouseEvent *const mouse_event = (QMouseEvent*)event;
579                 if (object == viewport_)
580                         hover_point_ = mouse_event->pos();
581                 else if (object == ruler_)
582                         hover_point_ = QPoint(mouse_event->x(), 0);
583                 else if (object == header_)
584                         hover_point_ = QPoint(0, mouse_event->y());
585                 else
586                         hover_point_ = QPoint(-1, -1);
587
588                 hover_point_changed();
589
590         } else if (type == QEvent::Leave) {
591                 hover_point_ = QPoint(-1, -1);
592                 hover_point_changed();
593         }
594
595         return QObject::eventFilter(object, event);
596 }
597
598 bool View::viewportEvent(QEvent *e)
599 {
600         switch(e->type()) {
601         case QEvent::Paint:
602         case QEvent::MouseButtonPress:
603         case QEvent::MouseButtonRelease:
604         case QEvent::MouseButtonDblClick:
605         case QEvent::MouseMove:
606         case QEvent::Wheel:
607         case QEvent::TouchBegin:
608         case QEvent::TouchUpdate:
609         case QEvent::TouchEnd:
610                 return false;
611
612         default:
613                 return QAbstractScrollArea::viewportEvent(e);
614         }
615 }
616
617 void View::resizeEvent(QResizeEvent*)
618 {
619         update_layout();
620 }
621
622 void View::row_item_appearance_changed(bool label, bool content)
623 {
624         if (label)
625                 header_->update();
626         if (content)
627                 viewport_->update();
628 }
629
630 void View::time_item_appearance_changed(bool label, bool content)
631 {
632         if (label)
633                 ruler_->update();
634         if (content)
635                 viewport_->update();
636 }
637
638 void View::extents_changed(bool horz, bool vert)
639 {
640         sticky_events_ |=
641                 (horz ? RowItemHExtentsChanged : 0) |
642                 (vert ? RowItemVExtentsChanged : 0);
643         lazy_event_handler_.start();
644 }
645
646 void View::h_scroll_value_changed(int value)
647 {
648         if (updating_scroll_)
649                 return;
650
651         const int range = horizontalScrollBar()->maximum();
652         if (range < MaxScrollValue)
653                 offset_ = scale_ * value;
654         else {
655                 double length = 0, offset;
656                 get_scroll_layout(length, offset);
657                 offset_ = scale_ * length * value / MaxScrollValue;
658         }
659
660         ruler_->update();
661         viewport_->update();
662 }
663
664 void View::v_scroll_value_changed(int value)
665 {
666         v_offset_ = value;
667         header_->update();
668         viewport_->update();
669 }
670
671 void View::signals_changed()
672 {
673         int offset = 0;
674
675         // Populate the traces
676         clear_child_items();
677
678         shared_ptr<sigrok::Device> device = session_.device();
679         assert(device);
680
681         // Collect a set of signals
682         unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
683                 signal_map;
684
685         shared_lock<shared_mutex> lock(session_.signals_mutex());
686         const vector< shared_ptr<Signal> > &sigs(session_.signals());
687
688         for (const shared_ptr<Signal> &sig : sigs)
689                 signal_map[sig->channel()] = sig;
690
691         // Populate channel groups
692         for (auto entry : device->channel_groups())
693         {
694                 const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
695
696                 if (group->channels().size() <= 1)
697                         continue;
698
699                 shared_ptr<TraceGroup> trace_group(new TraceGroup());
700                 int child_offset = 0;
701                 if (add_channels_to_owner(group->channels(),
702                         trace_group.get(), child_offset, signal_map))
703                 {
704                         add_child_item(trace_group);
705                         apply_offset(trace_group, offset);
706                 }
707         }
708
709         // Add the remaining logic channels
710         shared_ptr<TraceGroup> logic_trace_group(new TraceGroup());
711         int child_offset = 0;
712
713         if (add_channels_to_owner(device->channels(),
714                 logic_trace_group.get(), child_offset, signal_map,
715                 [](shared_ptr<RowItem> r) -> bool {
716                         return dynamic_pointer_cast<LogicSignal>(r) != nullptr;
717                         }))
718
719         {
720                 add_child_item(logic_trace_group);
721                 apply_offset(logic_trace_group, offset);
722         }
723
724         // Add the remaining channels
725         add_channels_to_owner(device->channels(), this, offset, signal_map);
726         assert(signal_map.empty());
727
728         // Add decode signals
729 #ifdef ENABLE_DECODE
730         const vector< shared_ptr<DecodeTrace> > decode_sigs(
731                 session().get_decode_signals());
732         for (auto s : decode_sigs) {
733                 add_child_item(s);
734                 apply_offset(s, offset);
735         }
736 #endif
737
738         update_layout();
739 }
740
741 void View::data_updated()
742 {
743         // Update the scroll bars
744         update_scroll();
745
746         // Repaint the view
747         viewport_->update();
748 }
749
750 void View::on_signals_moved()
751 {
752         update_scroll();
753         signals_moved();
754 }
755
756 void View::process_sticky_events()
757 {
758         if (sticky_events_ & RowItemHExtentsChanged)
759                 update_layout();
760         if (sticky_events_ & RowItemVExtentsChanged)
761                 restack_all_row_items();
762
763         // Clear the sticky events
764         sticky_events_ = 0;
765 }
766
767 void View::on_hover_point_changed()
768 {
769         for (shared_ptr<RowItem> r : *this)
770                 r->hover_point_changed();
771 }
772
773 } // namespace view
774 } // namespace pv