+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);