import 'dart:collection'; import '../style.dart'; import 'node.dart'; /* Container of multiple nodes */ abstract class Container extends Node { final LinkedList _children = LinkedList(); LinkedList get children => _children; int get childCount => _children.length; Node get first => _children.first; Node get last => _children.last; bool get isEmpty => _children.isEmpty; bool get isNotEmpty => _children.isNotEmpty; /// abstract methods begin T get defaultChild; /// abstract methods end add(T node) { assert(node.parent == null); node.parent = this; _children.add(node); } addFirst(T node) { assert(node.parent == null); node.parent = this; _children.addFirst(node); } void remove(T node) { assert(node.parent == this); node.parent = null; _children.remove(node); } void moveChildToNewParent(Container newParent) { if (isEmpty) { return; } T last = newParent.isEmpty ? null : newParent.last; while (isNotEmpty) { T child = first; child.unlink(); newParent.add(child); } if (last != null) last.adjust(); } ChildQuery queryChild(int offset, bool inclusive) { if (offset < 0) { throw ('Offset cannot be negative'); } if (offset > length) { throw ('offset $offset > $length'); } for (Node node in children) { int len = node.length; if (offset < len || (inclusive && offset == len && (node.isLast))) { return ChildQuery(node, offset); } offset -= len; } return ChildQuery(null, 0); } @override String toPlainText() => children.map((child) => child.toPlainText()).join(); @override int get length => _children.fold(0, (cur, node) => cur + node.length); @override insert(int index, Object data, Style style) { assert(index == 0 || (index > 0 && index < length)); if (isNotEmpty) { ChildQuery child = queryChild(index, false); child.node.insert(child.offset, data, style); return; } // empty assert(index == 0); T node = defaultChild; add(node); node.insert(index, data, style); } @override retain(int index, int length, Style attributes) { assert(isNotEmpty); ChildQuery child = queryChild(index, false); child.node.retain(child.offset, length, attributes); } @override delete(int index, int length) { assert(isNotEmpty); ChildQuery child = queryChild(index, false); child.node.delete(child.offset, length); } @override String toString() => _children.join('\n'); } /// Query of a child in a Container class ChildQuery { final Node node; // null if not found final int offset; ChildQuery(this.node, this.offset); }