]> git.lyx.org Git - lyx.git/blob - src/dociterator.C
hopefully fix tex2lyx linking.
[lyx.git] / src / dociterator.C
1 /**
2  * \file dociterator.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11
12 #include <config.h>
13
14 #include "dociterator.h"
15
16 #include "debug.h"
17 #include "lyxtext.h"
18 #include "lyxrow.h"
19 #include "paragraph.h"
20
21 #include "mathed/MathData.h"
22 #include "mathed/InsetMath.h"
23
24 #include "insets/insettabular.h"
25
26 #include <boost/assert.hpp>
27 #include <boost/current_function.hpp>
28
29
30 namespace lyx {
31
32 using std::endl;
33
34
35 // We could be able to get rid of this if only every BufferView were
36 // associated to a buffer on construction.
37 DocIterator::DocIterator()
38         : boundary_(false), inset_(0)
39 {}
40
41
42 DocIterator::DocIterator(InsetBase & inset)
43         : boundary_(false), inset_(&inset)
44 {}
45
46
47 DocIterator doc_iterator_begin(InsetBase & inset)
48 {
49         DocIterator dit(inset);
50         dit.forwardPos();
51         return dit;
52 }
53
54
55 DocIterator doc_iterator_end(InsetBase & inset)
56 {
57         return DocIterator(inset);
58 }
59
60
61 InsetBase * DocIterator::nextInset()
62 {
63         BOOST_ASSERT(!empty());
64         if (pos() == lastpos())
65                 return 0;
66         if (pos() > lastpos()) {
67                 lyxerr << "Should not happen, but it does. " << endl;
68                 return 0;
69         }
70         if (inMathed())
71                 return nextAtom().nucleus();
72         return paragraph().isInset(pos()) ? paragraph().getInset(pos()) : 0;
73 }
74
75
76 InsetBase * DocIterator::prevInset()
77 {
78         BOOST_ASSERT(!empty());
79         if (pos() == 0)
80                 return 0;
81         if (inMathed())
82                 return prevAtom().nucleus();
83         return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
84 }
85
86
87 InsetBase const * DocIterator::prevInset() const
88 {
89         BOOST_ASSERT(!empty());
90         if (pos() == 0)
91                 return 0;
92         if (inMathed())
93                 return prevAtom().nucleus();
94         return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
95 }
96
97
98 InsetBase * DocIterator::realInset() const
99 {
100         BOOST_ASSERT(inTexted());
101         // if we are in a tabular, we need the cell
102         if (inset().lyxCode() == InsetBase::TABULAR_CODE) {
103                 InsetTabular & tabular = static_cast<InsetTabular&>(inset());
104                 return tabular.cell(idx()).get();
105         }
106         return &inset();
107 }
108
109
110 MathAtom const & DocIterator::prevAtom() const
111 {
112         BOOST_ASSERT(!empty());
113         BOOST_ASSERT(pos() > 0);
114         return cell()[pos() - 1];
115 }
116
117
118 MathAtom & DocIterator::prevAtom()
119 {
120         BOOST_ASSERT(!empty());
121         BOOST_ASSERT(pos() > 0);
122         return cell()[pos() - 1];
123 }
124
125
126 MathAtom const & DocIterator::nextAtom() const
127 {
128         BOOST_ASSERT(!empty());
129         //lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
130         BOOST_ASSERT(pos() < lastpos());
131         return cell()[pos()];
132 }
133
134
135 MathAtom & DocIterator::nextAtom()
136 {
137         BOOST_ASSERT(!empty());
138         //lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
139         BOOST_ASSERT(pos() < lastpos());
140         return cell()[pos()];
141 }
142
143
144 LyXText * DocIterator::text()
145 {
146         BOOST_ASSERT(!empty());
147         return top().text();
148 }
149
150 LyXText const * DocIterator::text() const
151 {
152         BOOST_ASSERT(!empty());
153         return top().text();
154 }
155
156
157 Paragraph & DocIterator::paragraph()
158 {
159         if (!inTexted())
160                 lyxerr << *this << endl;
161         BOOST_ASSERT(inTexted());
162         return top().paragraph();
163 }
164
165
166 Paragraph const & DocIterator::paragraph() const
167 {
168         BOOST_ASSERT(inTexted());
169         return top().paragraph();
170 }
171
172
173 Row & DocIterator::textRow()
174 {
175         BOOST_ASSERT(!paragraph().rows().empty());
176         return paragraph().getRow(pos(), boundary_);
177 }
178
179
180 Row const & DocIterator::textRow() const
181 {
182         BOOST_ASSERT(!paragraph().rows().empty());
183         return paragraph().getRow(pos(), boundary_);
184 }
185
186
187 pit_type DocIterator::lastpit() const
188 {
189         return inMathed() ? 0 : text()->paragraphs().size() - 1;
190 }
191
192
193 pos_type DocIterator::lastpos() const
194 {
195         return inMathed() ? cell().size() : paragraph().size();
196 }
197
198
199 DocIterator::idx_type DocIterator::lastidx() const
200 {
201         return top().lastidx();
202 }
203
204
205 size_t DocIterator::nargs() const
206 {
207         // assume 1x1 grid for main text
208         return top().nargs();
209 }
210
211
212 size_t DocIterator::ncols() const
213 {
214         // assume 1x1 grid for main text
215         return top().ncols();
216 }
217
218
219 size_t DocIterator::nrows() const
220 {
221         // assume 1x1 grid for main text
222         return top().nrows();
223 }
224
225
226 DocIterator::row_type DocIterator::row() const
227 {
228         return top().row();
229 }
230
231
232 DocIterator::col_type DocIterator::col() const
233 {
234         return top().col();
235 }
236
237
238 MathArray const & DocIterator::cell() const
239 {
240         BOOST_ASSERT(inMathed());
241         return top().cell();
242 }
243
244
245 MathArray & DocIterator::cell()
246 {
247         BOOST_ASSERT(inMathed());
248         return top().cell();
249 }
250
251
252 bool DocIterator::inMathed() const
253 {
254         return !empty() && inset().inMathed();
255 }
256
257
258 bool DocIterator::inTexted() const
259 {
260         return !empty() && !inset().inMathed();
261 }
262
263
264 LyXText * DocIterator::innerText()
265 {
266         BOOST_ASSERT(!empty());
267         // Go up until first non-0 text is hit
268         // (innermost text is 0 in mathed)
269         for (int i = depth() - 1; i >= 0; --i)
270                 if (slices_[i].text())
271                         return slices_[i].text();
272         return 0;
273 }
274
275 LyXText const * DocIterator::innerText() const
276 {
277         BOOST_ASSERT(!empty());
278         // go up until first non-0 text is hit
279         // (innermost text is 0 in mathed)
280         for (int i = depth() - 1; i >= 0; --i)
281                 if (slices_[i].text())
282                         return slices_[i].text();
283         return 0;
284 }
285
286
287 InsetBase * DocIterator::innerInsetOfType(int code) const
288 {
289         for (int i = depth() - 1; i >= 0; --i)
290                 if (slices_[i].inset_->lyxCode() == code)
291                         return slices_[i].inset_;
292         return 0;
293 }
294
295
296 void DocIterator::forwardPos(bool ignorecollapsed)
297 {
298         //this dog bites his tail
299         if (empty()) {
300                 push_back(CursorSlice(*inset_));
301                 return;
302         }
303
304         // jump over collapsables if they are collapsed
305         // FIXME: the check for asInsetMath() shouldn't be necessary
306         // but math insets do not return a sensible editable() state yet.
307         if (ignorecollapsed && nextInset() && (!nextInset()->asInsetMath()
308             && nextInset()->editable() != InsetBase::HIGHLY_EDITABLE)) {
309                 ++top().pos();
310                 return;
311         }
312
313         CursorSlice & tip = top();
314         //lyxerr << "XXX\n" << *this << endl;
315
316         // this is used twice and shows up in the profiler!
317         pos_type const lastp = lastpos();
318
319         // move into an inset to the right if possible
320         InsetBase * n = 0;
321
322         if (tip.pos() != lastp) {
323                 // this is impossible for pos() == size()
324                 if (inMathed()) {
325                         n = (tip.cell().begin() + tip.pos())->nucleus();
326                 } else {
327                         if (paragraph().isInset(tip.pos()))
328                                 n = paragraph().getInset(tip.pos());
329                 }
330         }
331
332         if (n && n->isActive()) {
333                 //lyxerr << "... descend" << endl;
334                 push_back(CursorSlice(*n));
335                 return;
336         }
337
338         // otherwise move on one position if possible
339         if (tip.pos() < lastp) {
340                 //lyxerr << "... next pos" << endl;
341                 ++tip.pos();
342                 return;
343         }
344         //lyxerr << "... no next pos" << endl;
345
346         // otherwise move on one paragraph if possible
347         if (tip.pit() < lastpit()) {
348                 //lyxerr << "... next par" << endl;
349                 ++tip.pit();
350                 tip.pos() = 0;
351                 return;
352         }
353         //lyxerr << "... no next pit" << endl;
354
355         // otherwise try to move on one cell if possible
356         if (tip.idx() < lastidx()) {
357                 //lyxerr << "... next idx" << endl;
358                 ++tip.idx();
359                 tip.pit() = 0;
360                 tip.pos() = 0;
361                 return;
362         }
363         //lyxerr << "... no next idx" << endl;
364
365         // otherwise leave inset and jump over inset as a whole
366         pop_back();
367         // 'top' is invalid now...
368         if (!empty())
369                 ++top().pos();
370 }
371
372
373 void DocIterator::forwardPosNoDescend()
374 {
375         CursorSlice & tip = top();
376         pos_type const lastp = lastpos();
377
378         //  move on one position if possible
379         if (tip.pos() < lastp) {
380                 //lyxerr << "... next pos" << endl;
381                 ++tip.pos();
382                 return;
383         }
384         //lyxerr << "... no next pos" << endl;
385
386         // otherwise move on one paragraph if possible
387         if (tip.pit() < lastpit()) {
388                 //lyxerr << "... next par" << endl;
389                 ++tip.pit();
390                 tip.pos() = 0;
391                 return;
392         }
393         //lyxerr << "... no next pit" << endl;
394
395         // otherwise try to move on one cell if possible
396         if (tip.idx() < lastidx()) {
397                 //lyxerr << "... next idx" << endl;
398                 ++tip.idx();
399                 tip.pit() = 0;
400                 tip.pos() = 0;
401                 return;
402         }
403         //lyxerr << "... no next idx" << endl;
404
405         // otherwise leave inset and jump over inset as a whole
406         pop_back();
407         // 'top' is invalid now...
408         if (!empty())
409                 ++top().pos();
410 }
411
412
413 void DocIterator::forwardPar()
414 {
415         forwardPos();
416
417         while (!empty() && (!inTexted() || pos() != 0)) {
418                 if (inTexted()) {
419                         pos_type const lastp = lastpos();
420                         Paragraph const & par = paragraph();
421                         pos_type & pos = top().pos();
422                         while (pos < lastp && !par.isInset(pos))
423                                 ++pos;
424                 }
425                 forwardPos();
426         }
427 }
428
429
430 void DocIterator::forwardChar()
431 {
432         forwardPos();
433         while (!empty() && pos() == lastpos())
434                 forwardPos();
435 }
436
437
438 void DocIterator::forwardInset()
439 {
440         forwardPos();
441
442         while (!empty() && !nextInset()) {
443                 if (inTexted()) {
444                         pos_type const lastp = lastpos();
445                         Paragraph const & par = paragraph();
446                         pos_type & pos = top().pos();
447                         while (pos < lastp && !par.isInset(pos))
448                                 ++pos;
449                         if (pos < lastp)
450                                 break;
451                 }
452                 forwardPos();
453         }
454 }
455
456
457 void DocIterator::backwardChar()
458 {
459         backwardPos();
460         while (!empty() && pos() == lastpos())
461                 backwardPos();
462 }
463
464
465 void DocIterator::backwardPos()
466 {
467         //this dog bites his tail
468         if (empty()) {
469                 push_back(CursorSlice(*inset_));
470                 top().idx() = lastidx();
471                 top().pit() = lastpit();
472                 top().pos() = lastpos();
473                 return;
474         }
475
476         CursorSlice & tip = top();
477
478         if (tip.pos() != 0) {
479                 --tip.pos();
480         } else if (tip.pit() != 0) {
481                 --tip.pit();
482                 tip.pos() = lastpos();
483                 return;
484         } else if (tip.idx() != 0) {
485                 --tip.idx();
486                 tip.pit() = lastpit();
487                 tip.pos() = lastpos();
488                 return;
489         } else {
490                 pop_back();
491                 return;
492         }
493
494         // move into an inset to the left if possible
495         InsetBase * n = 0;
496
497         if (inMathed()) {
498                 n = (tip.cell().begin() + tip.pos())->nucleus();
499         } else {
500                 if (paragraph().isInset(tip.pos()))
501                         n = paragraph().getInset(tip.pos());
502         }
503
504         if (n && n->isActive()) {
505                 push_back(CursorSlice(*n));
506                 top().idx() = lastidx();
507                 top().pit() = lastpit();
508                 top().pos() = lastpos();
509         }
510 }
511
512
513 bool DocIterator::hasPart(DocIterator const & it) const
514 {
515         // it can't be a part if it is larger
516         if (it.depth() > depth())
517                 return false;
518
519         // as inset adresses are the 'last' level
520         return &it.top().inset() == &slices_[it.depth() - 1].inset();
521 }
522
523
524 void DocIterator::updateInsets(InsetBase * inset)
525 {
526         // this function re-creates the cache of inset pointers.
527         // code taken in part from StableDocIterator::asDocIterator.
528         //lyxerr << "converting:\n" << *this << endl;
529         DocIterator dit = DocIterator(*inset);
530         size_t const n = slices_.size();
531         for (size_t i = 0 ; i < n; ++i) {
532                 BOOST_ASSERT(inset);
533                 dit.push_back(slices_[i]);
534                 dit.top().inset_ = inset;
535                 if (i + 1 != n)
536                         inset = dit.nextInset();
537         }
538         //lyxerr << "converted:\n" << *this << endl;
539         operator=(dit);
540 }
541
542
543 std::ostream & operator<<(std::ostream & os, DocIterator const & dit)
544 {
545         for (size_t i = 0, n = dit.depth(); i != n; ++i)
546                 os << " " << dit[i] << "\n";
547         return os;
548 }
549
550
551
552 ///////////////////////////////////////////////////////
553
554 StableDocIterator::StableDocIterator(DocIterator const & dit)
555 {
556         data_ = dit.internalData();
557         for (size_t i = 0, n = data_.size(); i != n; ++i)
558                 data_[i].inset_ = 0;
559 }
560
561
562 DocIterator StableDocIterator::asDocIterator(InsetBase * inset) const
563 {
564         // this function re-creates the cache of inset pointers
565         //lyxerr << "converting:\n" << *this << endl;
566         DocIterator dit = DocIterator(*inset);
567         for (size_t i = 0, n = data_.size(); i != n; ++i) {
568                 if (inset == 0) {
569                         // FIXME
570                         lyxerr << BOOST_CURRENT_FUNCTION
571                                << " Should not happen, but does e.g. after C-n C-l C-z S-C-z"
572                                 << '\n' << "dit: " << dit << '\n'
573                                 << " lastpos: " << dit.lastpos() << endl;
574                         //break;
575                         BOOST_ASSERT(false);
576                 }
577                 dit.push_back(data_[i]);
578                 dit.top().inset_ = inset;
579                 if (i + 1 != n)
580                         inset = dit.nextInset();
581         }
582         //lyxerr << "convert:\n" << *this << " to:\n" << dit << endl;
583         return dit;
584 }
585
586
587 std::ostream & operator<<(std::ostream & os, StableDocIterator const & dit)
588 {
589         for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
590                 os << " " << dit.data_[i] << "\n";
591         return os;
592 }
593
594
595 bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
596 {
597         return dit1.data_ == dit2.data_;
598 }
599
600
601 } // namespace lyx