]> git.lyx.org Git - lyx.git/blob - src/dociterator.C
* Painter.h:
[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 #if 0
418         DocIterator cmp(*this);
419 #endif
420
421         while (!empty() && (!inTexted() || pos() != 0)) {
422                 if (inTexted()) {
423                         pos_type const lastp = lastpos();
424                         Paragraph const & par = paragraph();
425                         pos_type & pos = top().pos();
426                         while (pos < lastp && !par.isInset(pos))
427                                 ++pos;
428                 }
429                 forwardPos();
430         }
431
432 #if 0
433         while (!cmp.empty() && (!cmp.inTexted() || cmp.pos() != 0))
434                 cmp.forwardPos();
435         BOOST_ASSERT(cmp == *this);
436 #endif
437 }
438
439
440 void DocIterator::forwardChar()
441 {
442         forwardPos();
443         while (!empty() && pos() == lastpos())
444                 forwardPos();
445 }
446
447
448 void DocIterator::forwardInset()
449 {
450         forwardPos();
451         while (!empty() && (pos() == lastpos() || nextInset() == 0))
452                 forwardPos();
453 }
454
455
456 void DocIterator::backwardChar()
457 {
458         backwardPos();
459         while (!empty() && pos() == lastpos())
460                 backwardPos();
461 }
462
463
464 void DocIterator::backwardPos()
465 {
466         //this dog bites his tail
467         if (empty()) {
468                 push_back(CursorSlice(*inset_));
469                 top().idx() = lastidx();
470                 top().pit() = lastpit();
471                 top().pos() = lastpos();
472                 return;
473         }
474
475         CursorSlice & tip = top();
476
477         if (tip.pos() != 0) {
478                 --tip.pos();
479         } else if (tip.pit() != 0) {
480                 --tip.pit();
481                 tip.pos() = lastpos();
482                 return;
483         } else if (tip.idx() != 0) {
484                 --tip.idx();
485                 tip.pit() = lastpit();
486                 tip.pos() = lastpos();
487                 return;
488         } else {
489                 pop_back();
490                 return;
491         }
492
493         // move into an inset to the left if possible
494         InsetBase * n = 0;
495
496         if (inMathed()) {
497                 n = (tip.cell().begin() + tip.pos())->nucleus();
498         } else {
499                 if (paragraph().isInset(tip.pos()))
500                         n = paragraph().getInset(tip.pos());
501         }
502
503         if (n && n->isActive()) {
504                 push_back(CursorSlice(*n));
505                 top().idx() = lastidx();
506                 top().pit() = lastpit();
507                 top().pos() = lastpos();
508         }
509 }
510
511
512 bool DocIterator::hasPart(DocIterator const & it) const
513 {
514         // it can't be a part if it is larger
515         if (it.depth() > depth())
516                 return false;
517
518         // as inset adresses are the 'last' level
519         return &it.top().inset() == &slices_[it.depth() - 1].inset();
520 }
521
522
523 void DocIterator::updateInsets(InsetBase * inset)
524 {
525         // this function re-creates the cache of inset pointers.
526         // code taken in part from StableDocIterator::asDocIterator.
527         //lyxerr << "converting:\n" << *this << endl;
528         DocIterator dit = DocIterator(*inset);
529         size_t const n = slices_.size();
530         for (size_t i = 0 ; i < n; ++i) {
531                 BOOST_ASSERT(inset);
532                 dit.push_back(slices_[i]);
533                 dit.top().inset_ = inset;
534                 if (i + 1 != n)
535                         inset = dit.nextInset();
536         }
537         //lyxerr << "converted:\n" << *this << endl;
538         operator=(dit);
539 }
540
541
542 std::ostream & operator<<(std::ostream & os, DocIterator const & dit)
543 {
544         for (size_t i = 0, n = dit.depth(); i != n; ++i)
545                 os << " " << dit[i] << "\n";
546         return os;
547 }
548
549
550
551 ///////////////////////////////////////////////////////
552
553 StableDocIterator::StableDocIterator(DocIterator const & dit)
554 {
555         data_ = dit.internalData();
556         for (size_t i = 0, n = data_.size(); i != n; ++i)
557                 data_[i].inset_ = 0;
558 }
559
560
561 DocIterator StableDocIterator::asDocIterator(InsetBase * inset) const
562 {
563         // this function re-creates the cache of inset pointers
564         //lyxerr << "converting:\n" << *this << endl;
565         DocIterator dit = DocIterator(*inset);
566         for (size_t i = 0, n = data_.size(); i != n; ++i) {
567                 if (inset == 0) {
568                         // FIXME
569                         lyxerr << BOOST_CURRENT_FUNCTION
570                                << " Should not happen, but does e.g. after C-n C-l C-z S-C-z"
571                                 << '\n' << "dit: " << dit << '\n'
572                                 << " lastpos: " << dit.lastpos() << endl;
573                         //break;
574                         BOOST_ASSERT(false);
575                 }
576                 dit.push_back(data_[i]);
577                 dit.top().inset_ = inset;
578                 if (i + 1 != n)
579                         inset = dit.nextInset();
580         }
581         //lyxerr << "convert:\n" << *this << " to:\n" << dit << endl;
582         return dit;
583 }
584
585
586 std::ostream & operator<<(std::ostream & os, StableDocIterator const & dit)
587 {
588         for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
589                 os << " " << dit.data_[i] << "\n";
590         return os;
591 }
592
593
594 bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
595 {
596         return dit1.data_ == dit2.data_;
597 }
598
599
600 } // namespace lyx