]> sigrok.org Git - pulseview.git/blob - pv/view/view.cpp
build: Limit auto-versioning to git revision hash
[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 <algorithm>
28 #include <cassert>
29 #include <climits>
30 #include <cmath>
31 #include <iterator>
32 #include <mutex>
33 #include <unordered_set>
34
35 #include <boost/thread/locks.hpp>
36
37 #include <QApplication>
38 #include <QEvent>
39 #include <QFontMetrics>
40 #include <QMouseEvent>
41 #include <QScrollBar>
42
43 #include <libsigrokcxx/libsigrokcxx.hpp>
44
45 #include "decodetrace.hpp"
46 #include "header.hpp"
47 #include "logicsignal.hpp"
48 #include "ruler.hpp"
49 #include "signal.hpp"
50 #include "tracegroup.hpp"
51 #include "view.hpp"
52 #include "viewport.hpp"
53
54 #include "pv/session.hpp"
55 #include "pv/devices/device.hpp"
56 #include "pv/data/logic.hpp"
57 #include "pv/data/logicsegment.hpp"
58 #include "pv/util.hpp"
59
60 using boost::shared_lock;
61 using boost::shared_mutex;
62
63 using pv::data::SignalData;
64 using pv::data::Segment;
65 using pv::util::TimeUnit;
66 using pv::util::Timestamp;
67
68 using std::back_inserter;
69 using std::copy_if;
70 using std::deque;
71 using std::dynamic_pointer_cast;
72 using std::inserter;
73 using std::list;
74 using std::lock_guard;
75 using std::max;
76 using std::make_pair;
77 using std::make_shared;
78 using std::min;
79 using std::pair;
80 using std::set;
81 using std::set_difference;
82 using std::shared_ptr;
83 using std::unordered_map;
84 using std::unordered_set;
85 using std::vector;
86 using std::weak_ptr;
87
88 namespace pv {
89 namespace view {
90
91 const Timestamp View::MaxScale("1e9");
92 const Timestamp View::MinScale("1e-12");
93
94 const int View::MaxScrollValue = INT_MAX / 2;
95 const int View::MaxViewAutoUpdateRate = 25; // No more than 25 Hz with sticky scrolling
96
97 const int View::ScaleUnits[3] = {1, 2, 5};
98
99 View::View(Session &session, QWidget *parent) :
100         QAbstractScrollArea(parent),
101         session_(session),
102         viewport_(new Viewport(*this)),
103         ruler_(new Ruler(*this)),
104         header_(new Header(*this)),
105         scale_(1e-3),
106         offset_(0),
107         updating_scroll_(false),
108         sticky_scrolling_(false), // Default setting is set in MainWindow::setup_ui()
109         always_zoom_to_fit_(false),
110         tick_period_(0),
111         tick_prefix_(pv::util::SIPrefix::yocto),
112         tick_precision_(0),
113         time_unit_(util::TimeUnit::Time),
114         show_cursors_(false),
115         cursors_(new CursorPair(*this)),
116         next_flag_text_('A'),
117         hover_point_(-1, -1)
118 {
119         connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
120                 this, SLOT(h_scroll_value_changed(int)));
121         connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
122                 this, SLOT(v_scroll_value_changed()));
123
124         connect(&session_, SIGNAL(signals_changed()),
125                 this, SLOT(signals_changed()));
126         connect(&session_, SIGNAL(capture_state_changed(int)),
127                 this, SLOT(capture_state_updated(int)));
128         connect(&session_, SIGNAL(data_received()),
129                 this, SLOT(data_updated()));
130         connect(&session_, SIGNAL(frame_ended()),
131                 this, SLOT(data_updated()));
132
133         connect(header_, SIGNAL(selection_changed()),
134                 ruler_, SLOT(clear_selection()));
135         connect(ruler_, SIGNAL(selection_changed()),
136                 header_, SLOT(clear_selection()));
137
138         connect(header_, SIGNAL(selection_changed()),
139                 this, SIGNAL(selection_changed()));
140         connect(ruler_, SIGNAL(selection_changed()),
141                 this, SIGNAL(selection_changed()));
142
143         connect(this, SIGNAL(hover_point_changed()),
144                 this, SLOT(on_hover_point_changed()));
145
146         connect(&lazy_event_handler_, SIGNAL(timeout()),
147                 this, SLOT(process_sticky_events()));
148         lazy_event_handler_.setSingleShot(true);
149
150         connect(&delayed_view_updater_, SIGNAL(timeout()),
151                 this, SLOT(perform_delayed_view_update()));
152         delayed_view_updater_.setSingleShot(true);
153         delayed_view_updater_.setInterval(1000 / MaxViewAutoUpdateRate);
154
155         setViewport(viewport_);
156
157         viewport_->installEventFilter(this);
158         ruler_->installEventFilter(this);
159         header_->installEventFilter(this);
160
161         // Trigger the initial event manually. The default device has signals
162         // which were created before this object came into being
163         signals_changed();
164
165         // make sure the transparent widgets are on the top
166         ruler_->raise();
167         header_->raise();
168
169         // Update the zoom state
170         calculate_tick_spacing();
171 }
172
173 Session& View::session()
174 {
175         return session_;
176 }
177
178 const Session& View::session() const
179 {
180         return session_;
181 }
182
183 View* View::view()
184 {
185         return this;
186 }
187
188 const View* View::view() const
189 {
190         return this;
191 }
192
193 Viewport* View::viewport()
194 {
195         return viewport_;
196 }
197
198 const Viewport* View::viewport() const
199 {
200         return viewport_;
201 }
202
203 vector< shared_ptr<TimeItem> > View::time_items() const
204 {
205         const vector<shared_ptr<Flag>> f(flags());
206         vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
207         items.push_back(cursors_);
208         items.push_back(cursors_->first());
209         items.push_back(cursors_->second());
210         return items;
211 }
212
213 double View::scale() const
214 {
215         return scale_;
216 }
217
218 void View::set_scale(double scale)
219 {
220         if (scale_ != scale) {
221                 scale_ = scale;
222                 Q_EMIT scale_changed();
223         }
224 }
225
226 const Timestamp& View::offset() const
227 {
228         return offset_;
229 }
230
231 void View::set_offset(const pv::util::Timestamp& offset)
232 {
233         if (offset_ != offset) {
234                 offset_ = offset;
235                 Q_EMIT offset_changed();
236         }
237 }
238
239 int View::owner_visual_v_offset() const
240 {
241         return -verticalScrollBar()->sliderPosition();
242 }
243
244 void View::set_v_offset(int offset)
245 {
246         verticalScrollBar()->setSliderPosition(offset);
247         header_->update();
248         viewport_->update();
249 }
250
251 unsigned int View::depth() const
252 {
253         return 0;
254 }
255
256 pv::util::SIPrefix View::tick_prefix() const
257 {
258         return tick_prefix_;
259 }
260
261 void View::set_tick_prefix(pv::util::SIPrefix tick_prefix)
262 {
263         if (tick_prefix_ != tick_prefix) {
264                 tick_prefix_ = tick_prefix;
265                 Q_EMIT tick_prefix_changed();
266         }
267 }
268
269 unsigned int View::tick_precision() const
270 {
271         return tick_precision_;
272 }
273
274 void View::set_tick_precision(unsigned tick_precision)
275 {
276         if (tick_precision_ != tick_precision) {
277                 tick_precision_ = tick_precision;
278                 Q_EMIT tick_precision_changed();
279         }
280 }
281
282 const pv::util::Timestamp& View::tick_period() const
283 {
284         return tick_period_;
285 }
286
287 void View::set_tick_period(const pv::util::Timestamp& tick_period)
288 {
289         if (tick_period_ != tick_period) {
290                 tick_period_ = tick_period;
291                 Q_EMIT tick_period_changed();
292         }
293 }
294
295 TimeUnit View::time_unit() const
296 {
297         return time_unit_;
298 }
299
300 void View::set_time_unit(pv::util::TimeUnit time_unit)
301 {
302         if (time_unit_ != time_unit) {
303                 time_unit_ = time_unit;
304                 Q_EMIT time_unit_changed();
305         }
306 }
307
308 void View::zoom(double steps)
309 {
310         zoom(steps, viewport_->width() / 2);
311 }
312
313 void View::zoom(double steps, int offset)
314 {
315         set_zoom(scale_ * pow(3.0/2.0, -steps), offset);
316 }
317
318 void View::zoom_fit(bool gui_state)
319 {
320         // Act as one-shot when stopped, toggle along with the GUI otherwise
321         if (session_.get_capture_state() == Session::Stopped) {
322                 always_zoom_to_fit_ = false;
323                 always_zoom_to_fit_changed(false);
324         } else {
325                 always_zoom_to_fit_ = gui_state;
326                 always_zoom_to_fit_changed(gui_state);
327         }
328
329         const pair<Timestamp, Timestamp> extents = get_time_extents();
330         const Timestamp delta = extents.second - extents.first;
331         if (delta < Timestamp("1e-12"))
332                 return;
333
334         assert(viewport_);
335         const int w = viewport_->width();
336         if (w <= 0)
337                 return;
338
339         const Timestamp scale = max(min(delta / w, MaxScale), MinScale);
340         set_scale_offset(scale.convert_to<double>(), extents.first);
341 }
342
343 void View::zoom_one_to_one()
344 {
345         using pv::data::SignalData;
346
347         // Make a set of all the visible data objects
348         set< shared_ptr<SignalData> > visible_data = get_visible_data();
349         if (visible_data.empty())
350                 return;
351
352         double samplerate = 0.0;
353         for (const shared_ptr<SignalData> d : visible_data) {
354                 assert(d);
355                 const vector< shared_ptr<Segment> > segments =
356                         d->segments();
357                 for (const shared_ptr<Segment> &s : segments)
358                         samplerate = max(samplerate, s->samplerate());
359         }
360
361         if (samplerate == 0.0)
362                 return;
363
364         assert(viewport_);
365         const int w = viewport_->width();
366         if (w <= 0)
367                 return;
368
369         set_zoom(1.0 / samplerate, w / 2);
370 }
371
372 void View::set_scale_offset(double scale, const Timestamp& offset)
373 {
374         // Disable sticky scrolling / always zoom to fit when acquisition runs
375         // and user drags the viewport
376         if ((scale_ == scale) && (offset_ != offset) &&
377                         (session_.get_capture_state() == Session::Running)) {
378
379                 if (sticky_scrolling_) {
380                         sticky_scrolling_ = false;
381                         sticky_scrolling_changed(false);
382                 }
383
384                 if (always_zoom_to_fit_) {
385                         always_zoom_to_fit_ = false;
386                         always_zoom_to_fit_changed(false);
387                 }
388         }
389
390         set_scale(scale);
391         set_offset(offset);
392
393         calculate_tick_spacing();
394
395         update_scroll();
396         ruler_->update();
397         viewport_->update();
398 }
399
400 set< shared_ptr<SignalData> > View::get_visible_data() const
401 {
402         const unordered_set< shared_ptr<Signal> > sigs(session().signals());
403
404         // Make a set of all the visible data objects
405         set< shared_ptr<SignalData> > visible_data;
406         for (const shared_ptr<Signal> sig : sigs)
407                 if (sig->enabled())
408                         visible_data.insert(sig->data());
409
410         return visible_data;
411 }
412
413 pair<Timestamp, Timestamp> View::get_time_extents() const
414 {
415         boost::optional<Timestamp> left_time, right_time;
416         const set< shared_ptr<SignalData> > visible_data = get_visible_data();
417         for (const shared_ptr<SignalData> d : visible_data)
418         {
419                 const vector< shared_ptr<Segment> > segments =
420                         d->segments();
421                 for (const shared_ptr<Segment> &s : segments) {
422                         double samplerate = s->samplerate();
423                         samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
424
425                         const Timestamp start_time = s->start_time();
426                         left_time = left_time ?
427                                 min(*left_time, start_time) :
428                                                 start_time;
429                         right_time = right_time ?
430                                 max(*right_time, start_time + d->max_sample_count() / samplerate) :
431                                                  start_time + d->max_sample_count() / samplerate;
432                 }
433         }
434
435         if (!left_time || !right_time)
436                 return make_pair(0, 0);
437
438         assert(*left_time < *right_time);
439         return make_pair(*left_time, *right_time);
440 }
441
442 void View::enable_sticky_scrolling(bool state)
443 {
444         sticky_scrolling_ = state;
445 }
446
447 bool View::cursors_shown() const
448 {
449         return show_cursors_;
450 }
451
452 void View::show_cursors(bool show)
453 {
454         show_cursors_ = show;
455         ruler_->update();
456         viewport_->update();
457 }
458
459 void View::centre_cursors()
460 {
461         const double time_width = scale_ * viewport_->width();
462         cursors_->first()->set_time(offset_ + time_width * 0.4);
463         cursors_->second()->set_time(offset_ + time_width * 0.6);
464         ruler_->update();
465         viewport_->update();
466 }
467
468 std::shared_ptr<CursorPair> View::cursors() const
469 {
470         return cursors_;
471 }
472
473 void View::add_flag(const Timestamp& time)
474 {
475         flags_.push_back(shared_ptr<Flag>(new Flag(*this, time,
476                 QString("%1").arg(next_flag_text_))));
477         next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
478                 (next_flag_text_ + 1);
479         time_item_appearance_changed(true, true);
480 }
481
482 void View::remove_flag(std::shared_ptr<Flag> flag)
483 {
484         flags_.remove(flag);
485         time_item_appearance_changed(true, true);
486 }
487
488 vector< std::shared_ptr<Flag> > View::flags() const
489 {
490         vector< std::shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
491         stable_sort(flags.begin(), flags.end(),
492                 [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
493                         return a->time() < b->time();
494                 });
495
496         return flags;
497 }
498
499 const QPoint& View::hover_point() const
500 {
501         return hover_point_;
502 }
503
504 void View::update_viewport()
505 {
506         assert(viewport_);
507         viewport_->update();
508         header_->update();
509 }
510
511 void View::restack_all_trace_tree_items()
512 {
513         // Make a list of owners that is sorted from deepest first
514         const vector<shared_ptr<TraceTreeItem>> items(
515                 list_by_type<TraceTreeItem>());
516         set< TraceTreeItemOwner* > owners;
517         for (const auto &r : items)
518                 owners.insert(r->owner());
519         vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end());
520         sort(sorted_owners.begin(), sorted_owners.end(),
521                 [](const TraceTreeItemOwner* a, const TraceTreeItemOwner *b) {
522                         return a->depth() > b->depth(); });
523
524         // Restack the items recursively
525         for (auto &o : sorted_owners)
526                 o->restack_items();
527
528         // Animate the items to their destination
529         for (const auto &i : items)
530                 i->animate_to_layout_v_offset();
531 }
532
533 void View::get_scroll_layout(double &length, Timestamp &offset) const
534 {
535         const pair<Timestamp, Timestamp> extents = get_time_extents();
536         length = ((extents.second - extents.first) / scale_).convert_to<double>();
537         offset = offset_ / scale_;
538 }
539
540 void View::set_zoom(double scale, int offset)
541 {
542         // Reset the "always zoom to fit" feature as the user changed the zoom
543         always_zoom_to_fit_ = false;
544         always_zoom_to_fit_changed(false);
545
546         const Timestamp cursor_offset = offset_ + scale_ * offset;
547         const Timestamp new_scale = max(min(Timestamp(scale), MaxScale), MinScale);
548         const Timestamp new_offset = cursor_offset - new_scale * offset;
549         set_scale_offset(new_scale.convert_to<double>(), new_offset);
550 }
551
552 void View::calculate_tick_spacing()
553 {
554         const double SpacingIncrement = 10.0f;
555         const double MinValueSpacing = 40.0f;
556
557         // Figure out the highest numeric value visible on a label
558         const QSize areaSize = viewport_->size();
559         const Timestamp max_time = max(fabs(offset_),
560                 fabs(offset_ + scale_ * areaSize.width()));
561
562         double min_width = SpacingIncrement;
563         double label_width, tick_period_width;
564
565         QFontMetrics m(QApplication::font());
566
567         // Copies of the member variables with the same name, used in the calculation
568         // and written back afterwards, so that we don't emit signals all the time
569         // during the calculation.
570         pv::util::Timestamp tick_period = tick_period_;
571         pv::util::SIPrefix tick_prefix = tick_prefix_;
572         unsigned tick_precision = tick_precision_;
573
574         do {
575                 const double min_period = scale_ * min_width;
576
577                 const int order = (int)floorf(log10f(min_period));
578                 const pv::util::Timestamp order_decimal =
579                         pow(pv::util::Timestamp(10), order);
580
581                 // Allow for a margin of error so that a scale unit of 1 can be used.
582                 // Otherwise, for a SU of 1 the tick period will almost always be below
583                 // the min_period by a small amount - and thus skipped in favor of 2.
584                 // Note: margin assumes that SU[0] and SU[1] contain the smallest values
585                 double tp_margin = (ScaleUnits[0] + ScaleUnits[1]) / 2.0;
586                 double tp_with_margin;
587                 unsigned int unit = 0;
588
589                 do {
590                         tp_with_margin = order_decimal.convert_to<double>() *
591                                 (ScaleUnits[unit++] + tp_margin);
592                 } while (tp_with_margin < min_period && unit < countof(ScaleUnits));
593
594                 tick_period = order_decimal * ScaleUnits[unit - 1];
595                 tick_prefix = static_cast<pv::util::SIPrefix>(
596                         (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3);
597
598                 // Precision is the number of fractional digits required, not
599                 // taking the prefix into account (and it must never be negative)
600                 tick_precision = std::max(ceil(log10(1 / tick_period)).convert_to<int>(), 0);
601
602                 tick_period_width = (tick_period / scale_).convert_to<double>();
603
604                 const QString label_text = Ruler::format_time_with_distance(
605                         tick_period, max_time, tick_prefix, time_unit_, tick_precision);
606
607                 label_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
608                         Qt::AlignLeft | Qt::AlignTop, label_text).width() +
609                                 MinValueSpacing;
610
611                 min_width += SpacingIncrement;
612         } while (tick_period_width < label_width);
613
614         set_tick_period(tick_period);
615         set_tick_prefix(tick_prefix);
616         set_tick_precision(tick_precision);
617 }
618
619 void View::update_scroll()
620 {
621         assert(viewport_);
622
623         const QSize areaSize = viewport_->size();
624
625         // Set the horizontal scroll bar
626         double length = 0;
627         Timestamp offset;
628         get_scroll_layout(length, offset);
629         length = max(length - areaSize.width(), 0.0);
630
631         int major_tick_distance = (tick_period_ / scale_).convert_to<int>();
632
633         horizontalScrollBar()->setPageStep(areaSize.width() / 2);
634         horizontalScrollBar()->setSingleStep(major_tick_distance);
635
636         updating_scroll_ = true;
637
638         if (length < MaxScrollValue) {
639                 horizontalScrollBar()->setRange(0, length);
640                 horizontalScrollBar()->setSliderPosition(offset.convert_to<double>());
641         } else {
642                 horizontalScrollBar()->setRange(0, MaxScrollValue);
643                 horizontalScrollBar()->setSliderPosition(
644                         (offset_ * MaxScrollValue / (scale_ * length)).convert_to<double>());
645         }
646
647         updating_scroll_ = false;
648
649         // Set the vertical scrollbar
650         verticalScrollBar()->setPageStep(areaSize.height());
651         verticalScrollBar()->setSingleStep(areaSize.height() / 8);
652
653         const pair<int, int> extents = v_extents();
654         verticalScrollBar()->setRange(extents.first - (areaSize.height() / 2),
655                 extents.second - (areaSize.height() / 2));
656 }
657
658 void View::update_layout()
659 {
660         setViewportMargins(
661                 header_->sizeHint().width() - pv::view::Header::BaselineOffset,
662                 ruler_->sizeHint().height(), 0, 0);
663         ruler_->setGeometry(viewport_->x(), 0,
664                 viewport_->width(), ruler_->extended_size_hint().height());
665         header_->setGeometry(0, viewport_->y(),
666                 header_->extended_size_hint().width(), viewport_->height());
667         update_scroll();
668 }
669
670 void View::paint_label(QPainter &p, const QRect &rect, bool hover)
671 {
672         (void)p;
673         (void)rect;
674         (void)hover;
675 }
676
677 QRectF View::label_rect(const QRectF &rect)
678 {
679         (void)rect;
680         return QRectF();
681 }
682
683 TraceTreeItemOwner* View::find_prevalent_trace_group(
684         const shared_ptr<sigrok::ChannelGroup> &group,
685         const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
686                 &signal_map)
687 {
688         assert(group);
689
690         unordered_set<TraceTreeItemOwner*> owners;
691         vector<TraceTreeItemOwner*> owner_list;
692
693         // Make a set and a list of all the owners
694         for (const auto &channel : group->channels()) {
695                 const auto iter = signal_map.find(channel);
696                 if (iter == signal_map.end())
697                         continue;
698
699                 TraceTreeItemOwner *const o = (*iter).second->owner();
700                 owner_list.push_back(o);
701                 owners.insert(o);
702         }
703
704         // Iterate through the list of owners, and find the most prevalent
705         size_t max_prevalence = 0;
706         TraceTreeItemOwner *prevalent_owner = nullptr;
707         for (TraceTreeItemOwner *owner : owners) {
708                 const size_t prevalence = std::count_if(
709                         owner_list.begin(), owner_list.end(),
710                         [&](TraceTreeItemOwner *o) { return o == owner; });
711                 if (prevalence > max_prevalence) {
712                         max_prevalence = prevalence;
713                         prevalent_owner = owner;
714                 }
715         }
716
717         return prevalent_owner;
718 }
719
720 vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
721         const vector< shared_ptr<sigrok::Channel> > &channels,
722         const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
723                 &signal_map,
724         set< shared_ptr<Trace> > &add_list)
725 {
726         vector< shared_ptr<Trace> > filtered_traces;
727
728         for (const auto &channel : channels)
729         {
730                 const auto map_iter = signal_map.find(channel);
731                 if (map_iter == signal_map.end())
732                         continue;
733
734                 shared_ptr<Trace> trace = (*map_iter).second;
735                 const auto list_iter = add_list.find(trace);
736                 if (list_iter == add_list.end())
737                         continue;
738
739                 filtered_traces.push_back(trace);
740                 add_list.erase(list_iter);
741         }
742
743         return filtered_traces;
744 }
745
746 void View::determine_time_unit()
747 {
748         // Check whether we know the sample rate and hence can use time as the unit
749         if (time_unit_ == util::TimeUnit::Samples) {
750                 const unordered_set< shared_ptr<Signal> > sigs(session().signals());
751
752                 // Check all signals but...
753                 for (const shared_ptr<Signal> signal : sigs) {
754                         const shared_ptr<SignalData> data = signal->data();
755
756                         // ...only check first segment of each
757                         const vector< shared_ptr<Segment> > segments = data->segments();
758                         if (!segments.empty())
759                                 if (segments[0]->samplerate()) {
760                                         set_time_unit(util::TimeUnit::Time);
761                                         break;
762                                 }
763                 }
764         }
765 }
766
767 bool View::eventFilter(QObject *object, QEvent *event)
768 {
769         const QEvent::Type type = event->type();
770         if (type == QEvent::MouseMove) {
771
772                 const QMouseEvent *const mouse_event = (QMouseEvent*)event;
773                 if (object == viewport_)
774                         hover_point_ = mouse_event->pos();
775                 else if (object == ruler_)
776                         hover_point_ = QPoint(mouse_event->x(), 0);
777                 else if (object == header_)
778                         hover_point_ = QPoint(0, mouse_event->y());
779                 else
780                         hover_point_ = QPoint(-1, -1);
781
782                 hover_point_changed();
783
784         } else if (type == QEvent::Leave) {
785                 hover_point_ = QPoint(-1, -1);
786                 hover_point_changed();
787         }
788
789         return QObject::eventFilter(object, event);
790 }
791
792 bool View::viewportEvent(QEvent *e)
793 {
794         switch(e->type()) {
795         case QEvent::Paint:
796         case QEvent::MouseButtonPress:
797         case QEvent::MouseButtonRelease:
798         case QEvent::MouseButtonDblClick:
799         case QEvent::MouseMove:
800         case QEvent::Wheel:
801         case QEvent::TouchBegin:
802         case QEvent::TouchUpdate:
803         case QEvent::TouchEnd:
804                 return false;
805
806         default:
807                 return QAbstractScrollArea::viewportEvent(e);
808         }
809 }
810
811 void View::resizeEvent(QResizeEvent*)
812 {
813         update_layout();
814 }
815
816 void View::row_item_appearance_changed(bool label, bool content)
817 {
818         if (label)
819                 header_->update();
820         if (content)
821                 viewport_->update();
822 }
823
824 void View::time_item_appearance_changed(bool label, bool content)
825 {
826         if (label)
827                 ruler_->update();
828         if (content)
829                 viewport_->update();
830 }
831
832 void View::extents_changed(bool horz, bool vert)
833 {
834         sticky_events_ |=
835                 (horz ? TraceTreeItemHExtentsChanged : 0) |
836                 (vert ? TraceTreeItemVExtentsChanged : 0);
837         lazy_event_handler_.start();
838 }
839
840 void View::h_scroll_value_changed(int value)
841 {
842         if (updating_scroll_)
843                 return;
844
845         // Disable sticky scrolling when user moves the horizontal scroll bar
846         // during a running acquisition
847         if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) {
848                 sticky_scrolling_ = false;
849                 sticky_scrolling_changed(false);
850         }
851
852         const int range = horizontalScrollBar()->maximum();
853         if (range < MaxScrollValue)
854                 set_offset(scale_ * value);
855         else {
856                 double length = 0;
857                 Timestamp offset;
858                 get_scroll_layout(length, offset);
859                 set_offset(scale_ * length * value / MaxScrollValue);
860         }
861
862         ruler_->update();
863         viewport_->update();
864 }
865
866 void View::v_scroll_value_changed()
867 {
868         header_->update();
869         viewport_->update();
870 }
871
872 void View::signals_changed()
873 {
874         using sigrok::Channel;
875
876         vector< shared_ptr<TraceTreeItem> > new_top_level_items;
877
878         const auto device = session_.device();
879         if (!device)
880                 return;
881
882         shared_ptr<sigrok::Device> sr_dev = device->device();
883         assert(sr_dev);
884
885         const vector< shared_ptr<Channel> > channels(
886                 sr_dev->channels());
887
888         // Make a list of traces that are being added, and a list of traces
889         // that are being removed
890         const vector<shared_ptr<Trace>> prev_trace_list = list_by_type<Trace>();
891         const set<shared_ptr<Trace>> prev_traces(
892                 prev_trace_list.begin(), prev_trace_list.end());
893
894         const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
895
896         set< shared_ptr<Trace> > traces(sigs.begin(), sigs.end());
897
898 #ifdef ENABLE_DECODE
899         const vector< shared_ptr<DecodeTrace> > decode_traces(
900                 session().get_decode_signals());
901         traces.insert(decode_traces.begin(), decode_traces.end());
902 #endif
903
904         set< shared_ptr<Trace> > add_traces;
905         set_difference(traces.begin(), traces.end(),
906                 prev_traces.begin(), prev_traces.end(),
907                 inserter(add_traces, add_traces.begin()));
908
909         set< shared_ptr<Trace> > remove_traces;
910         set_difference(prev_traces.begin(), prev_traces.end(),
911                 traces.begin(), traces.end(),
912                 inserter(remove_traces, remove_traces.begin()));
913
914         // Make a look-up table of sigrok Channels to pulseview Signals
915         unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
916                 signal_map;
917         for (const shared_ptr<Signal> &sig : sigs)
918                 signal_map[sig->channel()] = sig;
919
920         // Populate channel groups
921         for (auto entry : sr_dev->channel_groups())
922         {
923                 const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
924
925                 if (group->channels().size() <= 1)
926                         continue;
927
928                 // Find best trace group to add to
929                 TraceTreeItemOwner *owner = find_prevalent_trace_group(
930                         group, signal_map);
931
932                 // If there is no trace group, create one
933                 shared_ptr<TraceGroup> new_trace_group;
934                 if (!owner) {
935                         new_trace_group.reset(new TraceGroup());
936                         owner = new_trace_group.get();
937                 }
938
939                 // Extract traces for the trace group, removing them from
940                 // the add list
941                 const vector< shared_ptr<Trace> > new_traces_in_group =
942                         extract_new_traces_for_channels(group->channels(),
943                                 signal_map, add_traces);
944
945                 // Add the traces to the group
946                 const pair<int, int> prev_v_extents = owner->v_extents();
947                 int offset = prev_v_extents.second - prev_v_extents.first;
948                 for (shared_ptr<Trace> trace : new_traces_in_group) {
949                         assert(trace);
950                         owner->add_child_item(trace);
951
952                         const pair<int, int> extents = trace->v_extents();
953                         if (trace->enabled())
954                                 offset += -extents.first;
955                         trace->force_to_v_offset(offset);
956                         if (trace->enabled())
957                                 offset += extents.second;
958                 }
959
960                 // If this is a new group, enqueue it in the new top level
961                 // items list
962                 if (!new_traces_in_group.empty() && new_trace_group)
963                         new_top_level_items.push_back(new_trace_group);
964         }
965
966         // Enqueue the remaining logic channels in a group
967         vector< shared_ptr<Channel> > logic_channels;
968         copy_if(channels.begin(), channels.end(), back_inserter(logic_channels),
969                 [](const shared_ptr<Channel>& c) {
970                         return c->type() == sigrok::ChannelType::LOGIC; });
971         const vector< shared_ptr<Trace> > non_grouped_logic_signals =
972                 extract_new_traces_for_channels(logic_channels,
973                         signal_map, add_traces);
974         const shared_ptr<TraceGroup> non_grouped_trace_group(
975                 make_shared<TraceGroup>());
976         for (shared_ptr<Trace> trace : non_grouped_logic_signals)
977                 non_grouped_trace_group->add_child_item(trace);
978         new_top_level_items.push_back(non_grouped_trace_group);
979
980         // Enqueue the remaining channels as free ungrouped traces
981         const vector< shared_ptr<Trace> > new_top_level_signals =
982                 extract_new_traces_for_channels(channels,
983                         signal_map, add_traces);
984         new_top_level_items.insert(new_top_level_items.end(),
985                 new_top_level_signals.begin(), new_top_level_signals.end());
986
987         // Enqueue any remaining traces i.e. decode traces
988         new_top_level_items.insert(new_top_level_items.end(),
989                 add_traces.begin(), add_traces.end());
990
991         // Remove any removed traces
992         for (shared_ptr<Trace> trace : remove_traces) {
993                 TraceTreeItemOwner *const owner = trace->owner();
994                 assert(owner);
995                 owner->remove_child_item(trace);
996         }
997
998         // Add and position the pending top levels items
999         for (auto item : new_top_level_items) {
1000                 add_child_item(item);
1001
1002                 // Position the item after the last present item
1003                 int offset = v_extents().second;
1004                 const pair<int, int> extents = item->v_extents();
1005                 if (item->enabled())
1006                         offset += -extents.first;
1007                 item->force_to_v_offset(offset);
1008                 if (item->enabled())
1009                         offset += extents.second;
1010         }
1011
1012         update_layout();
1013
1014         header_->update();
1015         viewport_->update();
1016 }
1017
1018 void View::capture_state_updated(int state)
1019 {
1020         if (state == Session::Running)
1021                 set_time_unit(util::TimeUnit::Samples);
1022
1023         if (state == Session::Stopped) {
1024                 // After acquisition has stopped we need to re-calculate the ticks once
1025                 // as it's otherwise done when the user pans or zooms, which is too late
1026                 calculate_tick_spacing();
1027
1028                 // Reset "always zoom to fit", the acquisition has stopped
1029                 if (always_zoom_to_fit_) {
1030                         always_zoom_to_fit_ = false;
1031                         always_zoom_to_fit_changed(false);
1032                 }
1033         }
1034 }
1035
1036 void View::data_updated()
1037 {
1038         if (always_zoom_to_fit_ || sticky_scrolling_) {
1039                 if (!delayed_view_updater_.isActive())
1040                         delayed_view_updater_.start();
1041         } else {
1042                 determine_time_unit();
1043                 update_scroll();
1044                 ruler_->update();
1045                 viewport_->update();
1046         }
1047 }
1048
1049 void View::perform_delayed_view_update()
1050 {
1051         if (always_zoom_to_fit_)
1052                 zoom_fit(true);
1053
1054         if (sticky_scrolling_) {
1055                 // Make right side of the view sticky
1056                 double length = 0;
1057                 Timestamp offset;
1058                 get_scroll_layout(length, offset);
1059
1060                 const QSize areaSize = viewport_->size();
1061                 length = max(length - areaSize.width(), 0.0);
1062
1063                 set_offset(scale_ * length);
1064         }
1065
1066         determine_time_unit();
1067         update_scroll();
1068         ruler_->update();
1069         viewport_->update();
1070 }
1071
1072 void View::process_sticky_events()
1073 {
1074         if (sticky_events_ & TraceTreeItemHExtentsChanged)
1075                 update_layout();
1076         if (sticky_events_ & TraceTreeItemVExtentsChanged) {
1077                 restack_all_trace_tree_items();
1078                 update_scroll();
1079         }
1080
1081         // Clear the sticky events
1082         sticky_events_ = 0;
1083 }
1084
1085 void View::on_hover_point_changed()
1086 {
1087         const vector<shared_ptr<TraceTreeItem>> trace_tree_items(
1088                 list_by_type<TraceTreeItem>());
1089         for (shared_ptr<TraceTreeItem> r : trace_tree_items)
1090                 r->hover_point_changed();
1091 }
1092
1093 } // namespace view
1094 } // namespace pv