+ ruler_->update();
+ viewport_->update();
+}
+
+set< shared_ptr<SignalData> > View::get_visible_data() const
+{
+ shared_lock<shared_mutex> lock(session().signals_mutex());
+ const unordered_set< shared_ptr<Signal> > &sigs(session().signals());
+
+ // Make a set of all the visible data objects
+ set< shared_ptr<SignalData> > visible_data;
+ for (const shared_ptr<Signal> sig : sigs)
+ if (sig->enabled())
+ visible_data.insert(sig->data());
+
+ return visible_data;
+}
+
+pair<Timestamp, Timestamp> View::get_time_extents() const
+{
+ boost::optional<Timestamp> left_time, right_time;
+ const set< shared_ptr<SignalData> > visible_data = get_visible_data();
+ for (const shared_ptr<SignalData> d : visible_data)
+ {
+ const vector< shared_ptr<Segment> > segments =
+ d->segments();
+ for (const shared_ptr<Segment> &s : segments) {
+ double samplerate = s->samplerate();
+ samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
+
+ const Timestamp start_time = s->start_time();
+ left_time = left_time ?
+ min(*left_time, start_time) :
+ start_time;
+ right_time = right_time ?
+ max(*right_time, start_time + d->max_sample_count() / samplerate) :
+ start_time + d->max_sample_count() / samplerate;
+ }
+ }
+
+ if (!left_time || !right_time)
+ return make_pair(0, 0);
+
+ assert(*left_time < *right_time);
+ return make_pair(*left_time, *right_time);
+}
+
+void View::enable_sticky_scrolling(bool state)
+{
+ sticky_scrolling_ = state;
+}
+
+bool View::cursors_shown() const
+{
+ return show_cursors_;
+}
+
+void View::show_cursors(bool show)
+{
+ show_cursors_ = show;
+ ruler_->update();
+ viewport_->update();
+}
+
+void View::centre_cursors()
+{
+ const double time_width = scale_ * viewport_->width();
+ cursors_->first()->set_time(offset_ + time_width * 0.4);
+ cursors_->second()->set_time(offset_ + time_width * 0.6);
+ ruler_->update();
+ viewport_->update();
+}
+
+std::shared_ptr<CursorPair> View::cursors() const
+{
+ return cursors_;
+}
+
+void View::add_flag(const Timestamp& time)
+{
+ flags_.push_back(shared_ptr<Flag>(new Flag(*this, time,
+ QString("%1").arg(next_flag_text_))));
+ next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
+ (next_flag_text_ + 1);
+ time_item_appearance_changed(true, true);
+}
+
+void View::remove_flag(std::shared_ptr<Flag> flag)
+{
+ flags_.remove(flag);
+ time_item_appearance_changed(true, true);
+}
+
+vector< std::shared_ptr<Flag> > View::flags() const
+{
+ vector< std::shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
+ stable_sort(flags.begin(), flags.end(),
+ [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
+ return a->time() < b->time();
+ });
+
+ return flags;
+}
+
+const QPoint& View::hover_point() const
+{
+ return hover_point_;
+}
+
+void View::update_viewport()
+{
+ assert(viewport_);
+ viewport_->update();
+ header_->update();
+}
+
+void View::restack_all_trace_tree_items()
+{
+ // Make a list of owners that is sorted from deepest first
+ set< TraceTreeItemOwner* > owners;
+ for (const auto &r : *this)
+ owners.insert(r->owner());
+ vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end());
+ sort(sorted_owners.begin(), sorted_owners.end(),
+ [](const TraceTreeItemOwner* a, const TraceTreeItemOwner *b) {
+ return a->depth() > b->depth(); });
+
+ // Restack the items recursively
+ for (auto &o : sorted_owners)
+ o->restack_items();
+
+ // Animate the items to their destination
+ const vector< shared_ptr<TraceTreeItem> > items(
+ list_by_type<TraceTreeItem>());
+ for (const auto &i : items)
+ i->animate_to_layout_v_offset();
+}
+
+void View::get_scroll_layout(double &length, Timestamp &offset) const
+{
+ const pair<Timestamp, Timestamp> extents = get_time_extents();
+ length = ((extents.second - extents.first) / scale_).convert_to<double>();
+ offset = offset_ / scale_;
+}
+
+void View::set_zoom(double scale, int offset)
+{
+ // Reset the "always zoom to fit" feature as the user changed the zoom
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+
+ const Timestamp cursor_offset = offset_ + scale_ * offset;
+ const Timestamp new_scale = max(min(Timestamp(scale), MaxScale), MinScale);
+ const Timestamp new_offset = cursor_offset - new_scale * offset;
+ set_scale_offset(new_scale.convert_to<double>(), new_offset);
+}
+
+void View::calculate_tick_spacing()
+{
+ const double SpacingIncrement = 10.0f;
+ const double MinValueSpacing = 40.0f;
+
+ // Figure out the highest numeric value visible on a label
+ const QSize areaSize = viewport_->size();
+ const Timestamp max_time = max(fabs(offset_),
+ fabs(offset_ + scale_ * areaSize.width()));
+
+ double min_width = SpacingIncrement;
+ double label_width, tick_period_width;
+
+ QFontMetrics m(QApplication::font());
+
+ // Copies of the member variables with the same name, used in the calculation
+ // and written back afterwards, so that we don't emit signals all the time
+ // during the calculation.
+ pv::util::Timestamp tick_period = tick_period_;
+ pv::util::SIPrefix tick_prefix = tick_prefix_;
+ unsigned tick_precision = tick_precision_;
+
+ do {
+ const double min_period = scale_ * min_width;
+
+ const int order = (int)floorf(log10f(min_period));
+ const pv::util::Timestamp order_decimal =
+ pow(pv::util::Timestamp(10), order);
+
+ // Allow for a margin of error so that a scale unit of 1 can be used.
+ // Otherwise, for a SU of 1 the tick period will almost always be below
+ // the min_period by a small amount - and thus skipped in favor of 2.
+ // Note: margin assumes that SU[0] and SU[1] contain the smallest values
+ double tp_margin = (ScaleUnits[0] + ScaleUnits[1]) / 2.0;
+ double tp_with_margin;
+ unsigned int unit = 0;
+
+ do {
+ tp_with_margin = order_decimal.convert_to<double>() *
+ (ScaleUnits[unit++] + tp_margin);
+ } while (tp_with_margin < min_period && unit < countof(ScaleUnits));
+
+ tick_period = order_decimal * ScaleUnits[unit - 1];
+ tick_prefix = static_cast<pv::util::SIPrefix>(
+ (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3);
+
+ // Precision is the number of fractional digits required, not
+ // taking the prefix into account (and it must never be negative)
+ tick_precision = std::max(ceil(log10(1 / tick_period)).convert_to<int>(), 0);
+
+ tick_period_width = (tick_period / scale_).convert_to<double>();
+
+ const QString label_text = Ruler::format_time_with_distance(
+ tick_period, max_time, tick_prefix, time_unit_, tick_precision);
+
+ label_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
+ Qt::AlignLeft | Qt::AlignTop, label_text).width() +
+ MinValueSpacing;
+
+ min_width += SpacingIncrement;
+ } while (tick_period_width < label_width);
+
+ set_tick_period(tick_period);
+ set_tick_prefix(tick_prefix);
+ set_tick_precision(tick_precision);