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