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