+TextEntry const TexRow::text_none = { -1, 0 };
+RowEntry const TexRow::row_none = { false, { TexRow::text_none } };
+
+
+bool TexRow::isNone(TextEntry const & t)
+{
+ return t.id < 0;
+}
+
+
+bool TexRow::isNone(RowEntry const & r)
+{
+ return !r.is_math && isNone(r.text);
+}
+
+
+void TexRow::reset(bool enable)
+{
+ rowlist_.clear();
+ current_row_ = RowEntryList();
+ enabled_ = enable;
+}
+
+
+RowEntry TexRow::textEntry(int id, int pos)
+{
+ RowEntry entry;
+ entry.is_math = false;
+ entry.text.pos = pos;
+ entry.text.id = id;
+ return entry;
+}
+
+
+RowEntry TexRow::mathEntry(uid_type id, idx_type cell)
+{
+ RowEntry entry;
+ entry.is_math = true;
+ entry.math.cell = cell;
+ entry.math.id = id;
+ return entry;
+}
+
+
+bool operator==(RowEntry const & entry1,
+ RowEntry const & entry2)
+{
+ return entry1.is_math == entry2.is_math
+ && (entry1.is_math
+ ? (entry1.math.id == entry2.math.id
+ && entry1.math.cell == entry2.math.cell)
+ : (entry1.text.id == entry2.text.id
+ && entry1.text.pos == entry2.text.pos));
+}
+
+
+bool TexRow::start(RowEntry entry)
+{
+ if (!enabled_)
+ return false;
+ return current_row_.addEntry(entry);
+}
+
+
+bool TexRow::start(int id, int pos)
+{
+ return start(textEntry(id,pos));
+}
+
+
+void TexRow::forceStart(int id, int pos)
+{
+ if (!enabled_)
+ return;
+ return current_row_.forceAddEntry(textEntry(id,pos));
+}
+
+
+void TexRow::startMath(uid_type id, idx_type cell)
+{
+ start(mathEntry(id,cell));
+}
+
+
+void TexRow::newline()
+{
+ if (!enabled_)
+ return;
+ rowlist_.push_back(current_row_);
+ current_row_ = RowEntryList();
+}
+
+void TexRow::newlines(int num_lines)
+{
+ if (!enabled_)
+ return;
+ for (int i = 0; i < num_lines; ++i) {
+ newline();
+ }
+}
+
+void TexRow::finalize()
+{
+ if (!enabled_)
+ return;
+ newline();
+}
+
+
+void TexRow::append(TexRow const & texrow)
+{
+ if (!enabled_ || !texrow.enabled_)
+ return;
+ RowList::const_iterator it = texrow.rowlist_.begin();
+ RowList::const_iterator const end = texrow.rowlist_.end();
+ if (it == end) {
+ current_row_.append(texrow.current_row_);
+ } else {
+ current_row_.append(*it++);
+ rowlist_.push_back(current_row_);
+ rowlist_.insert(rowlist_.end(), it, end);
+ current_row_ = texrow.current_row_;
+ }
+}
+
+
+
+bool TexRow::getIdFromRow(int row, int & id, int & pos) const
+{
+ TextEntry t = text_none;
+ if (row <= int(rowlist_.size()))
+ while (row > 0 && isNone(t = rowlist_[row - 1].getTextEntry()))
+ --row;
+ id = t.id;
+ pos = t.pos;
+ return !isNone(t);
+}
+
+
+RowEntry TexRow::rowEntryFromCursorSlice(CursorSlice const & slice)
+{
+ RowEntry entry;
+ InsetMath * insetMath = slice.asInsetMath();
+ if (insetMath) {
+ entry.is_math = 1;
+ entry.math.id = insetMath->id();
+ entry.math.cell = slice.idx();
+ } else if (slice.text()) {
+ entry.is_math = 0;
+ entry.text.id = slice.paragraph().id();
+ entry.text.pos = slice.pos();
+ } else {
+ // should not happen
+ entry = row_none;
+ }
+ return entry;
+}
+
+
+bool TexRow::sameParOrInsetMath(RowEntry const & entry1,
+ RowEntry const & entry2)
+{
+ return entry1.is_math == entry2.is_math
+ && (entry1.is_math
+ ? (entry1.math.id == entry2.math.id)
+ : (entry1.text.id == entry2.text.id));
+}
+
+
+// assumes it is sameParOrInsetMath
+int TexRow::comparePos(RowEntry const & entry1,
+ RowEntry const & entry2)
+{
+ if (entry1.is_math)
+ return entry2.math.cell - entry1.math.cell;
+ else
+ return entry2.text.pos - entry1.text.pos;
+}
+
+
+// An iterator on RowList that goes top-down, left-right
+//
+// We assume that the end of RowList does not change, which makes things simpler
+//
+// Records a pair of iterators on the RowEntryList (row_it_, row_end_) and a
+// pair of iterators on the current row (it_, it_end_).
+//
+// it_ always points to a valid position unless row_it_ == row_end_.
+//
+// We could turn this into a proper bidirectional iterator, but we don't need as
+// much.
+//
+class TexRow::RowListIterator
+{