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