+ return nullptr;
+}
+
+int TraceGroup::owner_visual_v_offset() const
+{
+ return owner_ ? visual_v_offset() + owner_->owner_visual_v_offset() : 0;
+}
+
+void TraceGroup::restack_items()
+{
+ vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+
+ // Sort by the centre line of the extents
+ stable_sort(items.begin(), items.end(),
+ [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
+ const auto aext = a->v_extents();
+ const auto bext = b->v_extents();
+ return a->layout_v_offset() +
+ (aext.first + aext.second) / 2 <
+ b->layout_v_offset() +
+ (bext.first + bext.second) / 2;
+ });
+
+ int total_offset = 0;
+ for (shared_ptr<TraceTreeItem> r : items) {
+ const pair<int, int> extents = r->v_extents();
+ if (extents.first == 0 && extents.second == 0)
+ continue;
+
+ // We position disabled traces, so that they are close to the
+ // animation target positon should they be re-enabled
+ if (r->enabled())
+ total_offset += -extents.first;
+
+ if (!r->dragging())
+ r->set_layout_v_offset(total_offset);
+
+ if (r->enabled())
+ total_offset += extents.second;
+ }
+}
+
+unsigned int TraceGroup::depth() const
+{
+ return owner_ ? owner_->depth() + 1 : 0;
+}
+
+void TraceGroup::ungroup()
+{
+ const vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+ clear_child_items();
+
+ for (shared_ptr<TraceTreeItem> r : items)
+ owner_->add_child_item(r);
+
+ owner_->remove_child_item(shared_from_this());
+}
+
+void TraceGroup::on_ungroup()
+{
+ ungroup();