]> git.lyx.org Git - lyx.git/blob - src/TexRow.cpp
prepare Qt 5.6 builds
[lyx.git] / src / TexRow.cpp
1 /**
2  * \file TexRow.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Matthias Ettrich
7  * \author Lars Gullik Bjønnes
8  * \author John Levon
9  * \author Guillaume Munch
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "Cursor.h"
17 #include "Paragraph.h"
18 #include "TexRow.h"
19
20 #include "mathed/InsetMath.h"
21
22 #include "support/debug.h"
23 #include "support/docstring_list.h"
24
25 #include <algorithm>
26 #include <sstream>
27
28
29 namespace lyx {
30
31
32
33 bool TexRow::RowEntryList::addEntry(RowEntry const & entry)
34 {
35         if (!entry.is_math) {
36                 if (text_entry_ < size())
37                         return false;
38                 else
39                         text_entry_ = size();
40         }
41         forceAddEntry(entry);
42         return true;
43 }
44
45
46 void TexRow::RowEntryList::forceAddEntry(RowEntry const & entry)
47 {
48         if (size() == 0 || !(operator[](size() - 1) == entry))
49                 push_back(RowEntry(entry));
50 }
51
52
53 TexRow::TextEntry TexRow::RowEntryList::getTextEntry() const
54 {
55         if (text_entry_ < size())
56                 return operator[](text_entry_).text;
57         return TexRow::text_none;
58 }
59
60
61 TexRow::RowEntry TexRow::RowEntryList::entry() const
62 {
63         if (0 < size())
64                 return operator[](0);
65         return TexRow::row_none;
66 }
67
68
69 void TexRow::RowEntryList::append(RowEntryList const & row)
70 {
71         if (text_entry_ >= size())
72                 text_entry_ = row.text_entry_ + size();
73         insert(end(), row.begin(), row.end());
74 }
75
76
77 TexRow::TextEntry const TexRow::text_none = { -1, 0 };
78 TexRow::RowEntry const TexRow::row_none = { false, { TexRow::text_none } };
79
80
81 bool TexRow::isNone(TextEntry const & t)
82 {
83         return t.id < 0;
84 }
85
86
87 bool TexRow::isNone(RowEntry const & r)
88 {
89         return !r.is_math && isNone(r.text);
90 }
91
92
93 void TexRow::reset(bool enable)
94 {
95         rowlist_.clear();
96         current_row_ = RowEntryList();
97         enabled_ = enable;
98 }
99
100
101 TexRow::RowEntry TexRow::textEntry(int id, int pos)
102 {
103         RowEntry entry;
104         entry.is_math = false;
105         entry.text.pos = pos;
106         entry.text.id = id;
107         return entry;
108 }
109
110
111 TexRow::RowEntry TexRow::mathEntry(uid_type id, idx_type cell)
112 {
113         RowEntry entry;
114         entry.is_math = true;
115         entry.math.cell = cell;
116         entry.math.id = id;
117         return entry;
118 }
119
120
121 bool operator==(TexRow::RowEntry const & entry1,
122                                 TexRow::RowEntry const & entry2)
123 {
124         return entry1.is_math == entry2.is_math
125                 && (entry1.is_math
126                         ? (entry1.math.id == entry2.math.id
127                            && entry1.math.cell == entry2.math.cell)
128                         : (entry1.text.id == entry2.text.id
129                            && entry1.text.pos == entry2.text.pos));
130 }
131
132
133 bool TexRow::start(RowEntry entry)
134 {
135         if (!enabled_)
136                 return false;
137         return current_row_.addEntry(entry);
138 }
139
140
141 bool TexRow::start(int id, int pos)
142 {
143         return start(textEntry(id,pos));
144 }
145
146
147 void TexRow::forceStart(int id, int pos)
148 {
149         if (!enabled_)
150                 return;
151         return current_row_.forceAddEntry(textEntry(id,pos));
152 }
153
154
155 void TexRow::startMath(uid_type id, idx_type cell)
156 {
157         start(mathEntry(id,cell));
158 }
159
160
161 void TexRow::newline()
162 {
163         if (!enabled_)
164                 return;
165         rowlist_.push_back(current_row_);
166         current_row_ = RowEntryList();
167 }
168
169 void TexRow::newlines(int num_lines)
170 {
171         if (!enabled_)
172                 return;
173         for (int i = 0; i < num_lines; ++i) {
174                 newline();
175         }
176 }
177
178 void TexRow::finalize()
179 {
180         if (!enabled_)
181                 return;
182         newline();
183 }
184
185
186 void TexRow::append(TexRow const & texrow)
187 {
188         if (!enabled_ || !texrow.enabled_)
189                 return;
190         RowList::const_iterator it = texrow.rowlist_.begin();
191         RowList::const_iterator const end = texrow.rowlist_.end();
192         if (it == end) {
193                 current_row_.append(texrow.current_row_);
194         } else {
195                 current_row_.append(*it++);
196                 rowlist_.push_back(current_row_);
197                 rowlist_.insert(rowlist_.end(), it, end);
198                 current_row_ = texrow.current_row_;
199         }
200 }
201
202
203
204 bool TexRow::getIdFromRow(int row, int & id, int & pos) const
205 {
206         TextEntry t = text_none;
207         if (row <= int(rowlist_.size()))
208                 while (row > 0 && isNone(t = rowlist_[row - 1].getTextEntry()))
209                         --row;
210         id = t.id;
211         pos = t.pos;
212         return !isNone(t);
213 }
214
215
216 TexRow::RowEntry TexRow::rowEntryFromCursorSlice(CursorSlice const & slice)
217 {
218         RowEntry entry;
219         InsetMath * insetMath = slice.asInsetMath();
220         if (insetMath) {
221                 entry.is_math = 1;
222                 entry.math.id = insetMath->id();
223                 entry.math.cell = slice.idx();
224         } else if (slice.text()) {
225                 entry.is_math = 0;
226                 entry.text.id = slice.paragraph().id();
227                 entry.text.pos = slice.pos();
228         } else {
229                 // should not happen
230                 entry = row_none;
231         }
232         return entry;
233 }
234
235
236 bool TexRow::sameParOrInsetMath(RowEntry const & entry1,
237                                                                 RowEntry const & entry2)
238 {
239         return entry1.is_math == entry2.is_math
240                 && (entry1.is_math
241                         ? (entry1.math.id == entry2.math.id)
242                         : (entry1.text.id == entry2.text.id));
243 }
244
245
246 // assumes it is sameParOrInsetMath
247 int TexRow::comparePos(RowEntry const & entry1,
248                                            RowEntry const & entry2)
249 {
250         if (entry1.is_math)
251                 return entry2.math.cell - entry1.math.cell;
252         else
253                 return entry2.text.pos - entry1.text.pos;
254 }
255
256
257 // An iterator on RowList that goes top-down, left-right
258 //
259 // We assume that the end of RowList does not change, which makes things simpler
260 //
261 // Records a pair of iterators on the RowEntryList (row_it_, row_end_) and a
262 // pair of iterators on the current row (it_, it_end_).
263 //
264 // it_ always points to a valid position unless row_it_ == row_end_.
265 //
266 // We could turn this into a proper bidirectional iterator, but we don't need as
267 // much.
268 //
269 class TexRow::RowListIterator
270 {
271 public:
272         RowListIterator(RowList::const_iterator r,
273                                         RowList::const_iterator r_end)
274                 : row_it_(r), row_end_(r_end),
275                   it_(r == r_end ? RowEntryList::const_iterator() : r->begin()),
276                   it_end_(r == r_end ? RowEntryList::const_iterator() : r->end())
277         {
278                 normalize();
279         }
280
281
282         RowListIterator() :
283                 row_it_(RowList::const_iterator()),
284                 row_end_(RowList::const_iterator()),
285                 it_(RowEntryList::const_iterator()),
286                 it_end_(RowEntryList::const_iterator()) { }
287
288
289         RowEntry const & operator*()
290         {
291                 return *it_;
292         }
293
294
295         RowListIterator & operator++()
296         {
297                 ++it_;
298                 normalize();
299                 return *this;
300         }
301
302
303         bool atEnd() const
304         {
305                 return row_it_ == row_end_;
306         }
307
308
309         bool operator==(RowListIterator const & a) const
310         {
311                 return row_it_ == a.row_it_ && ((atEnd() && a.atEnd()) || it_ == a.it_);
312         }
313
314
315         bool operator!=(RowListIterator const & a) const { return !operator==(a); }
316
317
318         // Current row.
319         RowList::const_iterator const & row() const
320         {
321                 return row_it_;
322         }
323 private:
324         // ensures that it_ points to a valid value unless row_it_ == row_end_
325         void normalize()
326         {
327                 if (row_it_ == row_end_)
328                         return;
329                 while (it_ == it_end_) {
330                         ++row_it_;
331                         if (row_it_ != row_end_) {
332                                 it_ = row_it_->begin();
333                                 it_end_ = row_it_->end();
334                         } else
335                                 return;
336                 }
337         }
338         //
339         RowList::const_iterator row_it_;
340         //
341         RowList::const_iterator row_end_;
342         //
343         RowEntryList::const_iterator it_;
344         //
345         RowEntryList::const_iterator it_end_;
346 };
347
348
349 TexRow::RowListIterator TexRow::begin() const
350 {
351         return RowListIterator(rowlist_.begin(), rowlist_.end());
352 }
353
354
355 TexRow::RowListIterator TexRow::end() const
356 {
357         return RowListIterator(rowlist_.end(), rowlist_.end());
358 }
359
360
361 std::pair<int,int> TexRow::rowFromDocIterator(DocIterator const & dit) const
362 {
363         bool beg_found = false;
364         bool end_is_next = true;
365         int end_offset = 1;
366         size_t best_slice = 0;
367         RowEntry best_entry = row_none;
368         size_t const n = dit.depth();
369         // this loop finds a pair (best_beg_row,best_end_row) where best_beg_row is
370         // the first row of the topmost possible CursorSlice, and best_end_row is
371         // the one just before the first row matching the next CursorSlice.
372         RowListIterator const begin = this->begin();//necessary disambiguation
373         RowListIterator const end = this->end();
374         RowListIterator best_beg_entry;
375         //best last entry with same pos as the beg_entry, or first entry with pos
376         //immediately following the beg_entry
377         RowListIterator best_end_entry;
378         RowListIterator it = begin;
379         for (; it != end; ++it) {
380                 // Compute the best end row.
381                 if (beg_found
382                         && (!sameParOrInsetMath(*it, *best_end_entry)
383                                 || comparePos(*it, *best_end_entry) <= 0)
384                         && sameParOrInsetMath(*it, best_entry)) {
385                     switch (comparePos(*it, best_entry)) {
386                         case 0:
387                                 // Either it is the last one that matches pos...
388                                 best_end_entry = it;
389                                 end_is_next = false;
390                                 end_offset = 1;
391                                 break;
392                         case -1: {
393                                 // ...or it is the row preceding the first that matches pos+1
394                                 if (!end_is_next) {
395                                         end_is_next = true;
396                                         if (it.row() != best_end_entry.row())
397                                                 end_offset = 0;
398                                         best_end_entry = it;
399                                 }
400                                 break;
401                         }
402                         }
403                 }
404                 // Compute the best begin row. It is better than the previous one if it
405                 // matches either at a deeper level, or at the same level but not
406                 // before.
407                 for (size_t i = best_slice; i < n; ++i) {
408                         TexRow::RowEntry entry_i = rowEntryFromCursorSlice(dit[i]);
409                         if (sameParOrInsetMath(*it, entry_i)) {
410                                 if (comparePos(*it, entry_i) >= 0
411                                         && (i > best_slice
412                                                 || !beg_found
413                                                 || !sameParOrInsetMath(*it, *best_beg_entry)
414                                                 || (comparePos(*it, *best_beg_entry) <= 0
415                                                         && comparePos(entry_i, *best_beg_entry) != 0)
416                                                 )
417                                         ) {
418                                         beg_found = true;
419                                         end_is_next = false;
420                                         end_offset = 1;
421                                         best_slice = i;
422                                         best_entry = entry_i;
423                                         best_beg_entry = best_end_entry = it;
424                                 }
425                                 //found CursorSlice
426                                 break;
427                         }
428                 }
429         }
430         if (!beg_found)
431                 return std::make_pair(-1,-1);
432         int const best_beg_row = distance(rowlist_.begin(),
433                                                                           best_beg_entry.row()) + 1;
434         int const best_end_row = distance(rowlist_.begin(),
435                                                                           best_end_entry.row()) + end_offset;
436         return std::make_pair(best_beg_row, best_end_row);
437 }
438
439
440 std::pair<int,int> TexRow::rowFromCursor(Cursor const & cur) const
441 {
442         DocIterator beg = cur.selectionBegin();
443         std::pair<int,int> beg_rows = rowFromDocIterator(beg);
444         if (cur.selection()) {
445                 DocIterator end = cur.selectionEnd();
446                 if (!cur.selIsMultiCell()
447                         // backwardPos asserts without the following test, IMO it's not my
448                         // duty to check this.
449                         && (end.top().pit() != 0
450                                 || end.top().idx() != 0
451                                 || end.top().pos() != 0))
452                         end.top().backwardPos();
453                 std::pair<int,int> end_rows = rowFromDocIterator(end);
454                 return std::make_pair(std::min(beg_rows.first, end_rows.first),
455                                                           std::max(beg_rows.second, end_rows.second));
456         } else
457                 return std::make_pair(beg_rows.first, beg_rows.second);
458 }
459
460
461 // debugging functions
462
463 ///
464 docstring TexRow::asString(RowEntry const & entry)
465 {
466         odocstringstream os;
467         if (entry.is_math)
468                 os << "(1," << entry.math.id << "," << entry.math.cell << ")";
469         else
470                 os << "(0," << entry.text.id << "," << entry.text.pos << ")";
471         return os.str();
472 }
473
474
475 ///prepends the texrow to the source given by tex, for debugging purpose
476 void TexRow::prepend(docstring_list & tex) const
477 {
478         size_type const prefix_length = 25;
479         if (tex.size() < rowlist_.size())
480                 tex.resize(rowlist_.size());
481         std::vector<RowEntryList>::const_iterator it = rowlist_.begin();
482         std::vector<RowEntryList>::const_iterator const beg = rowlist_.begin();
483         std::vector<RowEntryList>::const_iterator const end = rowlist_.end();
484         for (; it < end; ++it) {
485                 docstring entry;
486                 std::vector<RowEntry>::const_iterator it2 = it->begin();
487                 std::vector<RowEntry>::const_iterator const end2 = it->end();
488                 for (; it2 != end2; ++it2)
489                         entry += asString(*it2);
490                 if (entry.length() < prefix_length)
491                         entry = entry + docstring(prefix_length - entry.length(), L' ');
492                 ptrdiff_t i = it - beg;
493                 tex[i] = entry + "  " + tex[i];
494         }
495 }
496
497
498
499 LyXErr & operator<<(LyXErr & l, TexRow & texrow)
500 {
501         if (l.enabled()) {
502                 for (int i = 0; i < texrow.rows(); i++) {
503                         int id,pos;
504                         if (texrow.getIdFromRow(i+1,id,pos) && id>0)
505                                 l << i+1 << ":" << id << ":" << pos << "\n";
506                 }
507         }
508         return l;
509 }
510
511
512
513 } // namespace lyx