+ default:
+ return false;
+ }
+}
+
+
+//static
+int TexRow::comparePos(RowEntry entry1, RowEntry entry2)
+{
+ // assume it is sameParOrInsetMath
+ switch (entry1.type /* equal to entry2.type */) {
+ case TexRow::text_entry:
+ return entry2.text.pos - entry1.text.pos;
+ case TexRow::math_entry:
+ return entry2.math.cell - entry1.math.cell;
+ case TexRow::begin_document:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+
+// 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
+{
+public:
+ RowListIterator(RowList::const_iterator r,
+ RowList::const_iterator r_end)
+ : row_it_(r), row_end_(r_end),
+ it_(r == r_end ? RowEntryList::const_iterator() : r->begin()),
+ it_end_(r == r_end ? RowEntryList::const_iterator() : r->end())
+ {
+ normalize();
+ }
+
+
+ RowListIterator() :
+ row_it_(RowList::const_iterator()),
+ row_end_(RowList::const_iterator()),
+ it_(RowEntryList::const_iterator()),
+ it_end_(RowEntryList::const_iterator()) { }
+
+
+ RowEntry const & operator*()
+ {
+ return *it_;
+ }
+
+
+ RowListIterator & operator++()
+ {
+ ++it_;
+ normalize();
+ return *this;
+ }
+
+
+ bool atEnd() const
+ {
+ return row_it_ == row_end_;
+ }
+
+
+ bool operator==(RowListIterator const & a) const
+ {
+ return row_it_ == a.row_it_ && ((atEnd() && a.atEnd()) || it_ == a.it_);
+ }
+
+
+ bool operator!=(RowListIterator const & a) const { return !operator==(a); }
+
+
+ // Current row.
+ RowList::const_iterator const & row() const
+ {
+ return row_it_;
+ }
+private:
+ // ensures that it_ points to a valid value unless row_it_ == row_end_
+ void normalize()
+ {
+ if (row_it_ == row_end_)
+ return;
+ while (it_ == it_end_) {
+ ++row_it_;
+ if (row_it_ != row_end_) {
+ it_ = row_it_->begin();
+ it_end_ = row_it_->end();
+ } else
+ return;
+ }
+ }
+ //
+ RowList::const_iterator row_it_;
+ //
+ RowList::const_iterator row_end_;
+ //
+ RowEntryList::const_iterator it_;
+ //
+ RowEntryList::const_iterator it_end_;
+};
+
+
+TexRow::RowListIterator TexRow::begin() const
+{
+ return RowListIterator(rowlist_.begin(), rowlist_.end());
+}
+
+
+TexRow::RowListIterator TexRow::end() const
+{
+ return RowListIterator(rowlist_.end(), rowlist_.end());
+}
+
+
+pair<int,int> TexRow::rowFromDocIterator(DocIterator const & dit) const
+{
+ // Do not change anything in this algorithm if unsure.
+ bool beg_found = false;
+ bool end_is_next = true;
+ int end_offset = 1;
+ size_t best_slice = 0;
+ RowEntry best_entry = row_none;
+ size_t const n = dit.depth();
+ // this loop finds a pair (best_beg_row,best_end_row) where best_beg_row is
+ // the first row of the topmost possible CursorSlice, and best_end_row is
+ // the one just before the first row matching the next CursorSlice.
+ RowListIterator const begin = this->begin();//necessary disambiguation
+ RowListIterator const end = this->end();
+ RowListIterator best_beg_entry;
+ //best last entry with same pos as the beg_entry, or first entry with pos
+ //immediately following the beg_entry
+ RowListIterator best_end_entry;
+ RowListIterator it = begin;
+ for (; it != end; ++it) {
+ // Compute the best end row.
+ if (beg_found
+ && (!sameParOrInsetMath(*it, *best_end_entry)
+ || comparePos(*it, *best_end_entry) <= 0)
+ && sameParOrInsetMath(*it, best_entry)) {
+ switch (comparePos(*it, best_entry)) {
+ case 0:
+ // Either it is the last one that matches pos...
+ best_end_entry = it;
+ end_is_next = false;
+ end_offset = 1;
+ break;
+ case -1: {
+ // ...or it is the row preceding the first that matches pos+1
+ if (!end_is_next) {
+ end_is_next = true;
+ if (it.row() != best_end_entry.row())
+ end_offset = 0;
+ best_end_entry = it;
+ }
+ break;
+ }
+ }
+ }
+ // Compute the best begin row. It is better than the previous one if it
+ // matches either at a deeper level, or at the same level but not
+ // before.
+ for (size_t i = best_slice; i < n; ++i) {
+ RowEntry entry_i = rowEntryFromCursorSlice(dit[i]);
+ if (sameParOrInsetMath(*it, entry_i)) {
+ if (comparePos(*it, entry_i) >= 0
+ && (i > best_slice
+ || !beg_found
+ || !sameParOrInsetMath(*it, *best_beg_entry)
+ || (comparePos(*it, *best_beg_entry) <= 0
+ && comparePos(entry_i, *best_beg_entry) != 0)
+ )
+ ) {
+ beg_found = true;
+ end_is_next = false;
+ end_offset = 1;
+ best_slice = i;
+ best_entry = entry_i;
+ best_beg_entry = best_end_entry = it;
+ }
+ //found CursorSlice
+ break;
+ }
+ }
+ }
+ if (!beg_found)
+ return make_pair(-1,-1);
+ int const best_beg_row = distance(rowlist_.begin(),
+ best_beg_entry.row()) + 1;
+ int const best_end_row = distance(rowlist_.begin(),
+ best_end_entry.row()) + end_offset;
+ return make_pair(best_beg_row, best_end_row);
+}
+
+
+pair<int,int> TexRow::rowFromCursor(Cursor const & cur) const
+{
+ DocIterator beg = cur.selectionBegin();
+ pair<int,int> beg_rows = rowFromDocIterator(beg);
+ if (cur.selection()) {
+ DocIterator end = cur.selectionEnd();
+ if (!cur.selIsMultiCell() && !end.top().at_cell_begin())
+ end.top().backwardPos();
+ pair<int,int> end_rows = rowFromDocIterator(end);
+ return make_pair(min(beg_rows.first, end_rows.first),
+ max(beg_rows.second, end_rows.second));
+ } else
+ return make_pair(beg_rows.first, beg_rows.second);
+}
+
+
+size_t TexRow::rows() const
+{
+ return rowlist_.size();
+}
+
+
+void TexRow::setRows(size_t r)
+{
+ rowlist_.resize(r, RowEntryList());
+}
+
+
+// debugging functions
+
+///
+docstring TexRow::asString(RowEntry entry)
+{
+ odocstringstream os;
+ switch (entry.type) {
+ case TexRow::text_entry:
+ os << "(par " << entry.text.id << "," << entry.text.pos << ")";
+ break;
+ case TexRow::math_entry:
+ os << "(" << entry.math.id << "," << entry.math.cell << ")";
+ break;
+ case TexRow::begin_document:
+ os << "(begin_document)";
+ break;
+ default:
+ break;