]> git.lyx.org Git - features.git/blob - src/TexRow.cpp
a34acf08746392a2be7e45e1a0759a4c7d598c2c
[features.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 "DocIterator.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         if (size() == 0 || !(operator[](size() - 1) == entry))
42                 push_back(RowEntry(entry));
43         return true;
44 }
45
46
47 TexRow::TextEntry TexRow::RowEntryList::getTextEntry() const
48 {
49         if (text_entry_ < size())
50                 return operator[](text_entry_).text;
51         return TexRow::text_none;
52 }
53
54
55 TexRow::RowEntry TexRow::RowEntryList::entry() const
56 {
57         if (0 < size())
58                 return operator[](0);
59         return TexRow::row_none;
60 }
61
62
63 TexRow::TextEntry const TexRow::text_none = { -1, 0 };
64 TexRow::RowEntry const TexRow::row_none = { false, TexRow::text_none };
65
66
67 bool TexRow::isNone(TextEntry const & t)
68 {
69         return t.id < 0;
70 }
71
72
73 bool TexRow::isNone(RowEntry const & r)
74 {
75         return !r.is_math && isNone(r.text);
76 }
77
78
79 void TexRow::reset(bool enable)
80 {
81         rowlist_.clear();
82         current_row_ = RowEntryList();
83         enabled_ = enable;
84 }
85
86
87 TexRow::RowEntry TexRow::textEntry(int id, int pos)
88 {
89         RowEntry entry;
90         entry.is_math = false;
91         entry.text.pos = pos;
92         entry.text.id = id;
93         return entry;
94 }
95
96
97 TexRow::RowEntry TexRow::mathEntry(uid_type id, idx_type cell)
98 {
99         RowEntry entry;
100         entry.is_math = true;
101         entry.math.cell = cell;
102         entry.math.id = id;
103         return entry;
104 }
105
106
107 bool operator==(TexRow::RowEntry const & entry1,
108                                 TexRow::RowEntry const & entry2)
109 {
110         return entry1.is_math == entry2.is_math
111                 && (entry1.is_math
112                         ? (entry1.math.id == entry2.math.id
113                            && entry1.math.cell == entry2.math.cell)
114                         : (entry1.text.id == entry2.text.id
115                            && entry1.text.pos == entry2.text.pos));
116 }
117
118
119 bool TexRow::start(RowEntry entry)
120 {
121         if (!enabled_)
122                 return false;
123         return current_row_.addEntry(entry);
124 }
125
126
127 bool TexRow::start(int id, int pos)
128 {
129         return start(textEntry(id,pos));
130 }
131
132
133 bool TexRow::startMath(uid_type id, idx_type cell)
134 {
135         return start(mathEntry(id,cell));
136 }
137
138
139 void TexRow::newline()
140 {
141         if (!enabled_)
142                 return;
143         rowlist_.push_back(current_row_);
144         current_row_ = RowEntryList();
145 }
146
147 void TexRow::newlines(int num_lines)
148 {
149         if (!enabled_)
150                 return;
151         for (int i = 0; i < num_lines; ++i) {
152                 newline();
153         }
154 }
155
156 void TexRow::finalize()
157 {
158         if (!enabled_)
159                 return;
160         newline();
161 }
162
163
164 bool TexRow::getIdFromRow(int row, int & id, int & pos) const
165 {
166         TextEntry t = text_none;
167         bool ret = false;
168         if (row <= int(rowlist_.size()))
169                 while (row > 0 && isNone(t = rowlist_[row - 1].getTextEntry()))
170                         --row;
171         if (row > 0)
172                 ret = true;
173         id = t.id;
174         pos = t.pos;
175         return ret;
176 }
177
178
179 TexRow::RowEntry TexRow::rowEntryFromCursorSlice(CursorSlice const & slice)
180 {
181         RowEntry entry;
182         InsetMath * insetMath = slice.asInsetMath();
183         if (insetMath) {
184                 entry.is_math = 1;
185                 entry.math.id = insetMath->id();
186                 entry.math.cell = slice.idx();
187         } else if (slice.text()) {
188                 entry.is_math = 0;
189                 entry.text.id = slice.paragraph().id();
190                 entry.text.pos = slice.pos();
191         } else {
192                 // should not happen
193                 entry = row_none;
194         }
195         return entry;
196 }
197
198
199 bool TexRow::sameParOrInsetMath(RowEntry const & entry1,
200                                                                 RowEntry const & entry2)
201 {
202         return entry1.is_math == entry2.is_math
203                 && (entry1.is_math
204                         ? (entry1.math.id == entry2.math.id)
205                         : (entry1.text.id == entry2.text.id));
206 }
207
208
209 // assumes it is sameParOrInsetMath
210 int TexRow::comparePos(RowEntry const & entry1,
211                                            RowEntry const & entry2)
212 {
213         if (entry1.is_math)
214                 return entry2.math.cell - entry1.math.cell;
215         else
216                 return entry2.text.pos - entry1.text.pos;
217 }
218
219
220 // An iterator on RowList that goes top-down, left-right
221 //
222 // We assume that the end of RowList does not change, which makes things simpler
223 //
224 // Records a pair of iterators on the RowEntryList (row_it_, row_end_) and a
225 // pair of iterators on the current row (it_, it_end_).
226 //
227 // it_ always points to a valid position unless row_it_ == row_end_.
228 //
229 // We could turn this into a proper bidirectional iterator, but we don't need as
230 // much.
231 //
232 class TexRow::RowListIterator
233 {
234 public:
235         RowListIterator(RowList::const_iterator r,
236                                         RowList::const_iterator r_end)
237                 : row_it_(r), row_end_(r_end),
238                   it_(r == r_end ? RowEntryList::const_iterator() : r->begin()),
239                   it_end_(r == r_end ? RowEntryList::const_iterator() : r->end())
240         {
241                 normalize();
242         }
243
244
245         RowListIterator() :
246                 row_it_(RowList::const_iterator()),
247                 row_end_(RowList::const_iterator()),
248                 it_(RowEntryList::const_iterator()),
249                 it_end_(RowEntryList::const_iterator()) { }
250
251
252         RowEntry const & operator*()
253         {
254                 return *it_;
255         }
256
257
258         RowListIterator & operator++()
259         {
260                 ++it_;
261                 normalize();
262                 return *this;
263         }
264
265
266         bool atEnd() const
267         {
268                 return row_it_ == row_end_;
269         }
270         
271         
272         bool operator==(RowListIterator const & a) const
273         {
274                 return row_it_ == a.row_it_ && ((atEnd() && a.atEnd()) || it_ == a.it_);
275         }
276
277
278         bool operator!=(RowListIterator const & a) const { return !operator==(a); }
279
280
281         // Current row.
282         RowList::const_iterator const & row() const
283         {
284                 return row_it_;
285         }
286 private:
287         // ensures that it_ points to a valid value unless row_it_ == row_end_
288         void normalize()
289         {
290                 if (row_it_ == row_end_)
291                         return;
292                 while (it_ == it_end_) {
293                         ++row_it_;
294                         if (row_it_ != row_end_) {
295                                 it_ = row_it_->begin();
296                                 it_end_ = row_it_->end();
297                         } else
298                                 return;
299                 }
300         }
301         //
302         RowList::const_iterator row_it_;
303         //
304         RowList::const_iterator row_end_;
305         //
306         RowEntryList::const_iterator it_;
307         //
308         RowEntryList::const_iterator it_end_;
309 };
310
311
312 TexRow::RowListIterator TexRow::begin() const
313 {
314         return RowListIterator(rowlist_.begin(), rowlist_.end());
315 }
316
317
318 TexRow::RowListIterator TexRow::end() const
319 {
320         return RowListIterator(rowlist_.end(), rowlist_.end());
321 }
322
323
324 std::pair<int,int> TexRow::rowFromDocIterator(DocIterator const & dit) const
325 {
326         bool beg_found = false;
327         bool end_is_next = true;
328         int end_offset = 1;
329         size_t best_slice = 0;
330         RowEntry best_entry = row_none;
331         size_t const n = dit.depth();
332         // this loop finds a pair (best_beg_row,best_end_row) where best_beg_row is
333         // the first row of the topmost possible CursorSlice, and best_end_row is
334         // the one just before the first row matching the next CursorSlice.
335         RowListIterator const begin = this->begin();//necessary disambiguation
336         RowListIterator const end = this->end();
337         RowListIterator best_beg_entry;
338         //best last entry with same pos as the beg_entry, or first entry with pos
339         //immediately following the beg_entry
340         RowListIterator best_end_entry;
341         RowListIterator it = begin;
342         for (; it != end; ++it) {
343                 // Compute the best end row.
344                 if (beg_found
345                         && (!sameParOrInsetMath(*it, *best_end_entry)
346                                 || comparePos(*it, *best_end_entry) <= 0)
347                         && sameParOrInsetMath(*it, best_entry)) {
348                     switch (comparePos(*it, best_entry)) {
349                         case 0:
350                                 // Either it is the last one that matches pos...
351                                 best_end_entry = it;
352                                 end_is_next = false;
353                                 end_offset = 1;
354                                 break;
355                         case -1: {
356                                 // ...or it is the row preceding the first that matches pos+1 
357                                 if (!end_is_next) {
358                                         end_is_next = true;
359                                         if (it.row() != best_end_entry.row())
360                                                 end_offset = 0;
361                                         best_end_entry = it;
362                                 }
363                                 break;
364                         }
365                         }
366                 }
367                 // Compute the best begin row. It is better than the previous one if it
368                 // matches either at a deeper level, or at the same level but not
369                 // before.
370                 for (size_t i = best_slice; i < n; ++i) {
371                         TexRow::RowEntry entry_i = rowEntryFromCursorSlice(dit[i]);
372                         if (sameParOrInsetMath(*it, entry_i)) {
373                                 if (comparePos(*it, entry_i) >= 0
374                                         && (i > best_slice
375                                                 || !beg_found
376                                                 || !sameParOrInsetMath(*it, *best_beg_entry)
377                                                 || (comparePos(*it, *best_beg_entry) <= 0
378                                                         && comparePos(entry_i, *best_beg_entry) != 0)
379                                                 )
380                                         ) {
381                                         beg_found = true;
382                                         end_is_next = false;
383                                         end_offset = 1;
384                                         best_slice = i;
385                                         best_entry = entry_i;
386                                         best_beg_entry = best_end_entry = it;
387                                 }
388                                 //found CursorSlice
389                                 break;
390                         }
391                 }
392         }
393         if (!beg_found)
394                 return std::make_pair(-1,-1);
395         int const best_beg_row = distance(rowlist_.begin(),
396                                                                           best_beg_entry.row()) + 1;
397         int const best_end_row = distance(rowlist_.begin(),
398                                                                           best_end_entry.row()) + end_offset;
399         return std::make_pair(best_beg_row, best_end_row);
400 }
401
402
403 // debugging functions
404
405 ///
406 docstring TexRow::asString(RowEntry const & entry)
407 {
408         odocstringstream os;
409         if (entry.is_math)
410                 os << "(1," << entry.math.id << "," << entry.math.cell << ")";
411         else
412                 os << "(0," << entry.text.id << "," << entry.text.pos << ")";
413         return os.str();
414 }
415
416
417 ///prepends the texrow to the source given by tex, for debugging purpose
418 void TexRow::prepend(docstring_list & tex) const
419 {
420         int const prefix_length = 25;
421         if (tex.size() < rowlist_.size())
422                 tex.resize(rowlist_.size());
423         std::vector<RowEntryList>::const_iterator it = rowlist_.begin();
424         std::vector<RowEntryList>::const_iterator const beg = rowlist_.begin();
425         std::vector<RowEntryList>::const_iterator const end = rowlist_.end();
426         for (; it < end; ++it) {
427                 docstring entry;
428                 std::vector<RowEntry>::const_iterator it2 = it->begin();
429                 std::vector<RowEntry>::const_iterator const end2 = it->end();
430                 for (; it2 != end2; ++it2)
431                         entry += asString(*it2);
432                 if (entry.length() < prefix_length)
433                         entry = entry + docstring(prefix_length - entry.length(), L' ');
434                 int i = it - beg;
435                 tex[i] = entry + "  " + tex[i];
436         }
437 }
438
439
440
441 LyXErr & operator<<(LyXErr & l, TexRow & texrow)
442 {
443         if (l.enabled()) {
444                 for (int i = 0; i < texrow.rows(); i++) {
445                         int id,pos;
446                         if (texrow.getIdFromRow(i+1,id,pos) && id>0)
447                         l << i+1 << ":" << id << ":" << pos << "\n";
448                 }
449         }
450         return l;
451 }
452
453
454
455 } // namespace lyx