3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Matthias Ettrich
7 * \author Lars Gullik Bjønnes
9 * \author Guillaume Munch
11 * Full author contact details are available in file CREDITS.
18 #include "Paragraph.h"
21 #include "mathed/InsetMath.h"
23 #include "support/debug.h"
24 #include "support/docstring_list.h"
25 #include "support/lassert.h"
36 bool TexRow::RowEntryList::addEntry(RowEntry entry)
39 if (!isNone(text_entry_))
42 text_entry_ = entry.text;
49 void TexRow::RowEntryList::forceAddEntry(RowEntry entry)
51 if (v_.empty() || !(v_.back() == entry))
56 TextEntry TexRow::RowEntryList::getTextEntry() const
58 if (!isNone(text_entry_))
60 return TexRow::text_none;
64 void TexRow::RowEntryList::append(RowEntryList row)
66 if (isNone(text_entry_))
67 text_entry_ = row.text_entry_;
68 move(row.begin(), row.end(), back_inserter(v_));
78 TextEntry const TexRow::text_none = { -1, 0 };
79 RowEntry const TexRow::row_none = { false, { TexRow::text_none } };
83 bool TexRow::isNone(TextEntry t)
90 bool TexRow::isNone(RowEntry r)
92 return !r.is_math && isNone(r.text);
103 TexRow::RowEntryList & TexRow::currentRow()
105 return rowlist_.back();
110 RowEntry TexRow::textEntry(int id, pos_type pos)
113 entry.is_math = false;
114 entry.text.pos = pos;
121 RowEntry TexRow::mathEntry(uid_type id, idx_type cell)
124 entry.is_math = true;
125 entry.math.cell = cell;
131 bool operator==(RowEntry entry1, RowEntry entry2)
133 return entry1.is_math == entry2.is_math
135 ? (entry1.math.id == entry2.math.id
136 && entry1.math.cell == entry2.math.cell)
137 : (entry1.text.id == entry2.text.id
138 && entry1.text.pos == entry2.text.pos));
142 bool TexRow::start(RowEntry entry)
144 return currentRow().addEntry(entry);
148 bool TexRow::start(int id, pos_type pos)
150 return start(textEntry(id,pos));
154 void TexRow::forceStart(int id, pos_type pos)
156 return currentRow().forceAddEntry(textEntry(id,pos));
160 void TexRow::startMath(uid_type id, idx_type cell)
162 start(mathEntry(id,cell));
166 void TexRow::newline()
168 rowlist_.push_back(RowEntryList());
172 void TexRow::newlines(size_t num_lines)
179 void TexRow::append(TexRow other)
181 RowList::iterator it = other.rowlist_.begin();
182 RowList::iterator const end = other.rowlist_.end();
183 LASSERT(it != end, return);
184 currentRow().append(move(*it++));
185 move(it, end, back_inserter(rowlist_));
189 bool TexRow::getIdFromRow(int row, int & id, int & pos) const
191 LYXERR(Debug::LATEX, "getIdFromRow: row " << row << " requested");
192 TextEntry t = text_none;
193 if (row <= int(rowlist_.size()))
194 while (row > 0 && isNone(t = rowlist_[row - 1].getTextEntry()))
202 pair<TextEntry, TextEntry> TexRow::getEntriesFromRow(int const row) const
204 LYXERR(Debug::LATEX, "getEntriesFromRow: row " << row << " requested");
205 // check bounds for row - 1, our target index
207 return {text_none, text_none};
208 size_t const i = static_cast<size_t>(row - 1);
209 if (i >= rowlist_.size())
210 return {text_none, text_none};
211 // find the start entry
213 while (j > 0 && isNone(rowlist_[j].getTextEntry()))
215 TextEntry start = rowlist_[j].getTextEntry();
216 // find the end entry
218 while (j < rowlist_.size() && isNone(rowlist_[j].getTextEntry()))
221 (j < rowlist_.size()) ? rowlist_[j].getTextEntry() : text_none;
222 // The following occurs for a displayed math inset for instance (for good
223 // reasons involving subtleties of the algorithm in getRowFromDocIterator).
224 // We want this inset selected.
225 if (start.id == end.id && start.pos == end.pos)
231 pair<DocIterator, DocIterator> TexRow::getDocIteratorFromRow(
233 Buffer const & buf) const
235 TextEntry start, end;
236 tie(start,end) = getEntriesFromRow(row);
238 "getDocIteratorFromRow: for row " << row << ", TexRow has found "
239 "start (id=" << start.id << ",pos=" << start.pos << "), "
240 "end (id=" << end.id << ",pos=" << end.pos << ")");
242 DocIterator dit_start = buf.getParFromID(start.id);
244 dit_start.pos() = min(start.pos, dit_start.lastpos());
246 DocIterator dit_end = buf.getParFromID(end.id);
248 dit_end.pos() = min(end.pos, dit_end.lastpos());
249 // So far dit_end belongs to the next row. Step backwards.
250 if (!dit_end.top().at_cell_begin()) {
251 CursorSlice end_top = dit_end.top();
252 end_top.backwardPos();
253 if (dit_start && end_top != dit_start.top())
254 dit_end.top() = end_top;
256 dit_end.boundary(true);
258 return {dit_start, dit_end};
263 RowEntry TexRow::rowEntryFromCursorSlice(CursorSlice const & slice)
266 InsetMath * insetMath = slice.asInsetMath();
269 entry.math.id = insetMath->id();
270 entry.math.cell = slice.idx();
271 } else if (slice.text()) {
273 entry.text.id = slice.paragraph().id();
274 entry.text.pos = slice.pos();
276 LASSERT(false, return row_none);
282 bool TexRow::sameParOrInsetMath(RowEntry entry1, RowEntry entry2)
284 return entry1.is_math == entry2.is_math
286 ? (entry1.math.id == entry2.math.id)
287 : (entry1.text.id == entry2.text.id));
292 int TexRow::comparePos(RowEntry entry1, RowEntry entry2)
294 // assume it is sameParOrInsetMath
296 return entry2.math.cell - entry1.math.cell;
298 return entry2.text.pos - entry1.text.pos;
302 // An iterator on RowList that goes top-down, left-right
304 // We assume that the end of RowList does not change, which makes things simpler
306 // Records a pair of iterators on the RowEntryList (row_it_, row_end_) and a
307 // pair of iterators on the current row (it_, it_end_).
309 // it_ always points to a valid position unless row_it_ == row_end_.
311 // We could turn this into a proper bidirectional iterator, but we don't need as
314 class TexRow::RowListIterator
317 RowListIterator(RowList::const_iterator r,
318 RowList::const_iterator r_end)
319 : row_it_(r), row_end_(r_end),
320 it_(r == r_end ? RowEntryList::const_iterator() : r->begin()),
321 it_end_(r == r_end ? RowEntryList::const_iterator() : r->end())
328 row_it_(RowList::const_iterator()),
329 row_end_(RowList::const_iterator()),
330 it_(RowEntryList::const_iterator()),
331 it_end_(RowEntryList::const_iterator()) { }
334 RowEntry const & operator*()
340 RowListIterator & operator++()
350 return row_it_ == row_end_;
354 bool operator==(RowListIterator const & a) const
356 return row_it_ == a.row_it_ && ((atEnd() && a.atEnd()) || it_ == a.it_);
360 bool operator!=(RowListIterator const & a) const { return !operator==(a); }
364 RowList::const_iterator const & row() const
369 // ensures that it_ points to a valid value unless row_it_ == row_end_
372 if (row_it_ == row_end_)
374 while (it_ == it_end_) {
376 if (row_it_ != row_end_) {
377 it_ = row_it_->begin();
378 it_end_ = row_it_->end();
384 RowList::const_iterator row_it_;
386 RowList::const_iterator row_end_;
388 RowEntryList::const_iterator it_;
390 RowEntryList::const_iterator it_end_;
394 TexRow::RowListIterator TexRow::begin() const
396 return RowListIterator(rowlist_.begin(), rowlist_.end());
400 TexRow::RowListIterator TexRow::end() const
402 return RowListIterator(rowlist_.end(), rowlist_.end());
406 pair<int,int> TexRow::rowFromDocIterator(DocIterator const & dit) const
408 bool beg_found = false;
409 bool end_is_next = true;
411 size_t best_slice = 0;
412 RowEntry best_entry = row_none;
413 size_t const n = dit.depth();
414 // this loop finds a pair (best_beg_row,best_end_row) where best_beg_row is
415 // the first row of the topmost possible CursorSlice, and best_end_row is
416 // the one just before the first row matching the next CursorSlice.
417 RowListIterator const begin = this->begin();//necessary disambiguation
418 RowListIterator const end = this->end();
419 RowListIterator best_beg_entry;
420 //best last entry with same pos as the beg_entry, or first entry with pos
421 //immediately following the beg_entry
422 RowListIterator best_end_entry;
423 RowListIterator it = begin;
424 for (; it != end; ++it) {
425 // Compute the best end row.
427 && (!sameParOrInsetMath(*it, *best_end_entry)
428 || comparePos(*it, *best_end_entry) <= 0)
429 && sameParOrInsetMath(*it, best_entry)) {
430 switch (comparePos(*it, best_entry)) {
432 // Either it is the last one that matches pos...
438 // ...or it is the row preceding the first that matches pos+1
441 if (it.row() != best_end_entry.row())
449 // Compute the best begin row. It is better than the previous one if it
450 // matches either at a deeper level, or at the same level but not
452 for (size_t i = best_slice; i < n; ++i) {
453 RowEntry entry_i = rowEntryFromCursorSlice(dit[i]);
454 if (sameParOrInsetMath(*it, entry_i)) {
455 if (comparePos(*it, entry_i) >= 0
458 || !sameParOrInsetMath(*it, *best_beg_entry)
459 || (comparePos(*it, *best_beg_entry) <= 0
460 && comparePos(entry_i, *best_beg_entry) != 0)
467 best_entry = entry_i;
468 best_beg_entry = best_end_entry = it;
476 return make_pair(-1,-1);
477 int const best_beg_row = distance(rowlist_.begin(),
478 best_beg_entry.row()) + 1;
479 int const best_end_row = distance(rowlist_.begin(),
480 best_end_entry.row()) + end_offset;
481 return make_pair(best_beg_row, best_end_row);
485 pair<int,int> TexRow::rowFromCursor(Cursor const & cur) const
487 DocIterator beg = cur.selectionBegin();
488 pair<int,int> beg_rows = rowFromDocIterator(beg);
489 if (cur.selection()) {
490 DocIterator end = cur.selectionEnd();
491 if (!cur.selIsMultiCell() && !end.top().at_cell_begin())
492 end.top().backwardPos();
493 pair<int,int> end_rows = rowFromDocIterator(end);
494 return make_pair(min(beg_rows.first, end_rows.first),
495 max(beg_rows.second, end_rows.second));
497 return make_pair(beg_rows.first, beg_rows.second);
501 int TexRow::rows() const
503 return rowlist_.size();
507 // debugging functions
510 docstring TexRow::asString(RowEntry entry)
514 os << "(1," << entry.math.id << "," << entry.math.cell << ")";
516 os << "(0," << entry.text.id << "," << entry.text.pos << ")";
521 ///prepends the texrow to the source given by tex, for debugging purpose
522 void TexRow::prepend(docstring_list & tex) const
524 size_type const prefix_length = 25;
525 if (tex.size() < rowlist_.size())
526 tex.resize(rowlist_.size());
527 auto it = rowlist_.cbegin();
528 auto const beg = rowlist_.cbegin();
529 auto const end = rowlist_.cend();
530 for (; it < end; ++it) {
532 for (RowEntry const & e : *it)
533 entry += asString(e);
534 if (entry.length() < prefix_length)
535 entry = entry + docstring(prefix_length - entry.length(), ' ');
536 ptrdiff_t i = it - beg;
537 tex[i] = entry + " " + tex[i];
543 LyXErr & operator<<(LyXErr & l, TexRow const & texrow)
546 for (int i = 0; i < texrow.rows(); i++) {
548 if (texrow.getIdFromRow(i+1,id,pos) && id>0)
549 l << i+1 << ":" << id << ":" << pos << "\n";