2 * \file DocIterator.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Alfredo Braunstein
9 * Full author contact details are available in file CREDITS.
15 #include "DocIterator.h"
17 #include "support/debug.h"
18 #include "InsetList.h"
19 #include "Paragraph.h"
22 #include "mathed/MathData.h"
23 #include "mathed/InsetMath.h"
25 #include "insets/InsetTabular.h"
27 #include <boost/assert.hpp>
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)
42 DocIterator::DocIterator(Inset & inset)
43 : boundary_(false), inset_(&inset)
47 DocIterator doc_iterator_begin(Inset & inset)
49 DocIterator dit(inset);
55 DocIterator doc_iterator_end(Inset & inset)
57 return DocIterator(inset);
61 LyXErr & operator<<(LyXErr & os, DocIterator const & it)
68 Inset * DocIterator::nextInset() const
70 BOOST_ASSERT(!empty());
71 if (pos() == lastpos())
73 if (pos() > lastpos()) {
74 LYXERR0("Should not happen, but it does. ");
78 return nextAtom().nucleus();
79 return paragraph().isInset(pos()) ? paragraph().getInset(pos()) : 0;
83 Inset * DocIterator::prevInset() const
85 BOOST_ASSERT(!empty());
90 // FIXME: this should not happen but it does.
92 // http://bugzilla.lyx.org/show_bug.cgi?id=3189
95 return prevAtom().nucleus();
97 return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
101 Inset * DocIterator::realInset() const
103 BOOST_ASSERT(inTexted());
104 // if we are in a tabular, we need the cell
105 if (inset().lyxCode() == TABULAR_CODE) {
106 InsetTabular & tabular = static_cast<InsetTabular&>(inset());
107 return tabular.cell(idx()).get();
113 MathAtom & DocIterator::prevAtom() const
115 BOOST_ASSERT(!empty());
116 BOOST_ASSERT(pos() > 0);
117 return cell()[pos() - 1];
121 MathAtom & DocIterator::nextAtom() const
123 BOOST_ASSERT(!empty());
124 //lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
125 BOOST_ASSERT(pos() < lastpos());
126 return cell()[pos()];
130 Text * DocIterator::text() const
132 BOOST_ASSERT(!empty());
137 Paragraph & DocIterator::paragraph() const
141 BOOST_ASSERT(inTexted());
142 return top().paragraph();
146 Paragraph & DocIterator::innerParagraph() const
148 BOOST_ASSERT(!empty());
149 return innerTextSlice().paragraph();
153 CursorSlice const & DocIterator::innerTextSlice() const
155 BOOST_ASSERT(!empty());
156 // go up until first non-0 text is hit
157 // (innermost text is 0 in mathed)
158 for (int i = depth() - 1; i >= 0; --i)
159 if (slices_[i].text())
162 // This case is in principe not possible. We _must_
165 static CursorSlice dummy;
170 pit_type DocIterator::lastpit() const
172 return inMathed() ? 0 : text()->paragraphs().size() - 1;
176 pos_type DocIterator::lastpos() const
178 return inMathed() ? cell().size() : paragraph().size();
182 DocIterator::idx_type DocIterator::lastidx() const
184 return top().lastidx();
188 size_t DocIterator::nargs() const
190 // assume 1x1 grid for main text
191 return top().nargs();
195 size_t DocIterator::ncols() const
197 // assume 1x1 grid for main text
198 return top().ncols();
202 size_t DocIterator::nrows() const
204 // assume 1x1 grid for main text
205 return top().nrows();
209 DocIterator::row_type DocIterator::row() const
215 DocIterator::col_type DocIterator::col() const
221 MathData & DocIterator::cell() const
223 // BOOST_ASSERT(inMathed());
228 Text * DocIterator::innerText() const
230 BOOST_ASSERT(!empty());
231 // go up until first non-0 text is hit
232 // (innermost text is 0 in mathed)
233 for (int i = depth() - 1; i >= 0; --i)
234 if (slices_[i].text())
235 return slices_[i].text();
240 Inset * DocIterator::innerInsetOfType(int code) const
242 for (int i = depth() - 1; i >= 0; --i)
243 if (slices_[i].inset_->lyxCode() == code)
244 return slices_[i].inset_;
249 void DocIterator::forwardPos(bool ignorecollapsed)
251 //this dog bites his tail
253 push_back(CursorSlice(*inset_));
257 Inset * const nextinset = nextInset();
258 // jump over collapsables if they are collapsed
259 // FIXME: the check for asInsetMath() shouldn't be necessary
260 // but math insets do not return a sensible editable() state yet.
261 if (ignorecollapsed && nextinset && (!nextinset->asInsetMath()
262 && nextinset->editable() != Inset::HIGHLY_EDITABLE)) {
267 CursorSlice & tip = top();
268 //lyxerr << "XXX\n" << *this << endl;
270 // this is used twice and shows up in the profiler!
271 pos_type const lastp = lastpos();
273 // move into an inset to the right if possible
276 if (tip.pos() != lastp) {
277 // this is impossible for pos() == size()
279 n = (tip.cell().begin() + tip.pos())->nucleus();
281 if (paragraph().isInset(tip.pos()))
282 n = paragraph().getInset(tip.pos());
286 if (n && n->isActive()) {
287 //lyxerr << "... descend" << endl;
288 push_back(CursorSlice(*n));
296 // otherwise leave inset and jump over inset as a whole
298 // 'tip' is invalid now...
304 void DocIterator::forwardPar()
308 while (!empty() && (!inTexted() || pos() != 0)) {
310 pos_type const lastp = lastpos();
311 Paragraph const & par = paragraph();
312 pos_type & pos = top().pos();
313 if (par.insetList().empty())
316 while (pos < lastp && !par.isInset(pos))
324 void DocIterator::forwardChar()
327 while (!empty() && pos() == lastpos())
332 void DocIterator::forwardInset()
336 while (!empty() && !nextInset()) {
338 pos_type const lastp = lastpos();
339 Paragraph const & par = paragraph();
340 pos_type & pos = top().pos();
341 while (pos < lastp && !par.isInset(pos))
351 void DocIterator::backwardChar()
354 while (!empty() && pos() == lastpos())
359 void DocIterator::backwardPos()
361 //this dog bites his tail
363 push_back(CursorSlice(*inset_));
364 top().idx() = lastidx();
365 top().pit() = lastpit();
366 top().pos() = lastpos();
370 if (top().at_begin()) {
377 // move into an inset to the left if possible
381 n = (top().cell().begin() + top().pos())->nucleus();
383 if (paragraph().isInset(top().pos()))
384 n = paragraph().getInset(top().pos());
387 if (n && n->isActive()) {
388 push_back(CursorSlice(*n));
389 top().idx() = lastidx();
390 top().pit() = lastpit();
391 top().pos() = lastpos();
396 bool DocIterator::hasPart(DocIterator const & it) const
398 // it can't be a part if it is larger
399 if (it.depth() > depth())
402 // as inset adresses are the 'last' level
403 return &it.top().inset() == &slices_[it.depth() - 1].inset();
407 void DocIterator::updateInsets(Inset * inset)
409 // this function re-creates the cache of inset pointers.
410 //lyxerr << "converting:\n" << *this << endl;
411 DocIterator dit = *this;
412 size_t const n = slices_.size();
414 for (size_t i = 0 ; i < n; ++i) {
417 top().inset_ = inset;
421 //lyxerr << "converted:\n" << *this << endl;
425 bool DocIterator::fixIfBroken()
427 // Go through the slice stack from the bottom.
428 // Check that all coordinates (idx, pit, pos) are correct and
429 // that the inset is the one which is claimed to be there
430 Inset * inset = &slices_[0].inset();
432 size_t n = slices_.size();
433 for (; i != n; ++i) {
434 CursorSlice & cs = slices_[i];
435 if (&cs.inset() != inset) {
436 // the whole slice is wrong, chop off this as well
438 LYXERR(Debug::DEBUG, "fixIfBroken(): inset changed");
440 } else if (cs.idx() > cs.lastidx()) {
441 cs.idx() = cs.lastidx();
442 cs.pit() = cs.lastpit();
443 cs.pos() = cs.lastpos();
444 LYXERR(Debug::DEBUG, "fixIfBroken(): idx fixed");
446 } else if (cs.pit() > cs.lastpit()) {
447 cs.pit() = cs.lastpit();
448 cs.pos() = cs.lastpos();
449 LYXERR(Debug::DEBUG, "fixIfBroken(): pit fixed");
451 } else if (cs.pos() > cs.lastpos()) {
452 cs.pos() = cs.lastpos();
453 LYXERR(Debug::DEBUG, "fixIfBroken(): pos fixed");
455 } else if (i != n - 1 && cs.pos() != cs.lastpos()) {
456 // get inset which is supposed to be in the next slice
457 if (cs.inset().inMathed())
458 inset = (cs.cell().begin() + cs.pos())->nucleus();
459 else if (cs.paragraph().isInset(cs.pos()))
460 inset = cs.paragraph().getInset(cs.pos());
462 // there are slices left, so there must be another inset
468 // Did we make it through the whole slice stack? Otherwise there
469 // was a problem at slice i, and we have to chop off above
471 LYXERR(Debug::DEBUG, "fixIfBroken(): cursor chopped at " << i);
479 DocIterator::idx_type DocIterator::find(MathData const & cell) const
481 for (size_t l = 0; l != slices_.size(); ++l) {
482 if (slices_[l].asInsetMath() && &slices_[l].cell() == &cell)
489 DocIterator::idx_type DocIterator::find(InsetMath const * inset) const
491 for (size_t l = 0; l != slices_.size(); ++l) {
492 if (slices_[l].asInsetMath() == inset)
499 void DocIterator::cutOff(DocIterator::idx_type above, std::vector<CursorSlice> & cut)
501 cut = std::vector<CursorSlice>(slices_.begin() + above + 1, slices_.end());
502 slices_.resize(above + 1);
506 void DocIterator::cutOff(DocIterator::idx_type above)
508 slices_.resize(above + 1);
512 void DocIterator::append(std::vector<CursorSlice> const & x)
514 slices_.insert(slices_.end(), x.begin(), x.end());
518 void DocIterator::append(DocIterator::idx_type idx, pos_type pos)
520 slices_.push_back(CursorSlice());
526 std::ostream & operator<<(std::ostream & os, DocIterator const & dit)
528 for (size_t i = 0, n = dit.depth(); i != n; ++i)
529 os << " " << dit[i] << "\n";
534 bool operator<(DocIterator const & p, DocIterator const & q)
536 size_t depth = std::min(p.depth(), q.depth());
537 for (size_t i = 0 ; i < depth ; ++i) {
541 return p.depth() < q.depth();
545 bool operator>(DocIterator const & p, DocIterator const & q)
551 bool operator<=(DocIterator const & p, DocIterator const & q)
557 ///////////////////////////////////////////////////////
559 StableDocIterator::StableDocIterator(DocIterator const & dit)
561 data_ = dit.internalData();
562 for (size_t i = 0, n = data_.size(); i != n; ++i)
567 DocIterator StableDocIterator::asDocIterator(Inset * inset) const
569 // this function re-creates the cache of inset pointers
570 //lyxerr << "converting:\n" << *this << endl;
571 DocIterator dit = DocIterator(*inset);
572 for (size_t i = 0, n = data_.size(); i != n; ++i) {
575 LYXERR0(" Should not happen, but does e.g. after "
576 "C-n C-l C-z S-C-z\n"
577 << " or when a Buffer has been concurrently edited by two views"
578 << '\n' << "dit: " << dit << '\n'
579 << " lastpos: " << dit.lastpos());
583 dit.push_back(data_[i]);
584 dit.top().inset_ = inset;
585 if (dit.fixIfBroken())
588 inset = dit.nextInset();
590 //lyxerr << "convert:\n" << *this << " to:\n" << dit << endl;
595 std::ostream & operator<<(std::ostream & os, StableDocIterator const & dit)
597 for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
598 os << " " << dit.data_[i] << "\n";
603 bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
605 return dit1.data_ == dit2.data_;