lyx_mirror/src/dociterator.C
Abdelrazak Younes 4d5ae916ad Fix bug 3293 by Bernhard Roider:
This changes the semantics of isOK() and operator(), comments from Bernhard below:

With the old version of lyxlex it was _impossible_ to check whether reading an integer, float, ... succeeded or not. The current solution to check for is.bad() in some cases and in other cases use is.good() does not give the desired information. Moreover the result of is.bad() depends on the stl implementation and behaves different for linux and windows.

the bug was introduced by the patch that fixed the bug that crashed lyx when "inset-insert ert" was executed from the command buffer.
The lexer has the method isOK() which reflects the status of the stream is.
The operators void* and ! are not really well defined (they depend on the value of is.bad()). What is missing is a test if the last reading operation was successful and thus the returned value is valid.
That's what i implemented in this patch.

The new rule for using the lexer:

if you want to know if the lexer still has data to read (either from the stream or from the pushed token) then use "lex.isOK()".
If you want to test if the last reading operation was successful then use eg. "if (lex) {...}" or unsuccessful then use eg. "if (!lex) {...}"

an example:

int readParam(LyxLex &lex) {

    int param = 1; // default value
    if (lex.isOK()) { // the lexer has data to read
        int p;    // temporary variable
        lex >> p;
        if (lex) param = p; // only use the input if the reading operation was successful
    }
    return param;
}




git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@17569 a592a061-630c-0410-9148-cb99ea01b6c8
2007-03-26 13:43:49 +00:00

591 lines
12 KiB
C

/**
* \file dociterator.C
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author André Pönitz
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "dociterator.h"
#include "debug.h"
#include "lyxtext.h"
#include "paragraph.h"
#include "mathed/MathData.h"
#include "mathed/InsetMath.h"
#include "insets/insettabular.h"
#include <boost/assert.hpp>
#include <boost/current_function.hpp>
namespace lyx {
using std::endl;
// We could be able to get rid of this if only every BufferView were
// associated to a buffer on construction.
DocIterator::DocIterator()
: boundary_(false), inset_(0)
{}
DocIterator::DocIterator(InsetBase & inset)
: boundary_(false), inset_(&inset)
{}
DocIterator doc_iterator_begin(InsetBase & inset)
{
DocIterator dit(inset);
dit.forwardPos();
return dit;
}
DocIterator doc_iterator_end(InsetBase & inset)
{
return DocIterator(inset);
}
InsetBase * DocIterator::nextInset()
{
BOOST_ASSERT(!empty());
if (pos() == lastpos())
return 0;
if (pos() > lastpos()) {
lyxerr << "Should not happen, but it does. " << endl;
return 0;
}
if (inMathed())
return nextAtom().nucleus();
return paragraph().isInset(pos()) ? paragraph().getInset(pos()) : 0;
}
InsetBase * DocIterator::prevInset()
{
BOOST_ASSERT(!empty());
if (pos() == 0)
return 0;
if (inMathed())
if (cell().empty())
// FIXME: this should not happen but it does.
// See bug 3189
// http://bugzilla.lyx.org/show_bug.cgi?id=3189
return 0;
else
return prevAtom().nucleus();
return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
}
InsetBase const * DocIterator::prevInset() const
{
BOOST_ASSERT(!empty());
if (pos() == 0)
return 0;
if (inMathed())
if (cell().empty())
// FIXME: this should not happen but it does.
// See bug 3189
// http://bugzilla.lyx.org/show_bug.cgi?id=3189
return 0;
else
return prevAtom().nucleus();
return paragraph().isInset(pos() - 1) ? paragraph().getInset(pos() - 1) : 0;
}
InsetBase * DocIterator::realInset() const
{
BOOST_ASSERT(inTexted());
// if we are in a tabular, we need the cell
if (inset().lyxCode() == InsetBase::TABULAR_CODE) {
InsetTabular & tabular = static_cast<InsetTabular&>(inset());
return tabular.cell(idx()).get();
}
return &inset();
}
MathAtom const & DocIterator::prevAtom() const
{
BOOST_ASSERT(!empty());
BOOST_ASSERT(pos() > 0);
return cell()[pos() - 1];
}
MathAtom & DocIterator::prevAtom()
{
BOOST_ASSERT(!empty());
BOOST_ASSERT(pos() > 0);
return cell()[pos() - 1];
}
MathAtom const & DocIterator::nextAtom() const
{
BOOST_ASSERT(!empty());
//lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
BOOST_ASSERT(pos() < lastpos());
return cell()[pos()];
}
MathAtom & DocIterator::nextAtom()
{
BOOST_ASSERT(!empty());
//lyxerr << "lastpos: " << lastpos() << " next atom:\n" << *this << endl;
BOOST_ASSERT(pos() < lastpos());
return cell()[pos()];
}
LyXText * DocIterator::text()
{
BOOST_ASSERT(!empty());
return top().text();
}
LyXText const * DocIterator::text() const
{
BOOST_ASSERT(!empty());
return top().text();
}
Paragraph & DocIterator::paragraph()
{
if (!inTexted())
lyxerr << *this << endl;
BOOST_ASSERT(inTexted());
return top().paragraph();
}
Paragraph const & DocIterator::paragraph() const
{
BOOST_ASSERT(inTexted());
return top().paragraph();
}
pit_type DocIterator::lastpit() const
{
return inMathed() ? 0 : text()->paragraphs().size() - 1;
}
pos_type DocIterator::lastpos() const
{
return inMathed() ? cell().size() : paragraph().size();
}
DocIterator::idx_type DocIterator::lastidx() const
{
return top().lastidx();
}
size_t DocIterator::nargs() const
{
// assume 1x1 grid for main text
return top().nargs();
}
size_t DocIterator::ncols() const
{
// assume 1x1 grid for main text
return top().ncols();
}
size_t DocIterator::nrows() const
{
// assume 1x1 grid for main text
return top().nrows();
}
DocIterator::row_type DocIterator::row() const
{
return top().row();
}
DocIterator::col_type DocIterator::col() const
{
return top().col();
}
MathArray const & DocIterator::cell() const
{
// BOOST_ASSERT(inMathed());
return top().cell();
}
MathArray & DocIterator::cell()
{
// BOOST_ASSERT(inMathed());
return top().cell();
}
LyXText * DocIterator::innerText()
{
BOOST_ASSERT(!empty());
// Go up until first non-0 text is hit
// (innermost text is 0 in mathed)
for (int i = depth() - 1; i >= 0; --i)
if (slices_[i].text())
return slices_[i].text();
return 0;
}
LyXText const * DocIterator::innerText() const
{
BOOST_ASSERT(!empty());
// go up until first non-0 text is hit
// (innermost text is 0 in mathed)
for (int i = depth() - 1; i >= 0; --i)
if (slices_[i].text())
return slices_[i].text();
return 0;
}
InsetBase * DocIterator::innerInsetOfType(int code) const
{
for (int i = depth() - 1; i >= 0; --i)
if (slices_[i].inset_->lyxCode() == code)
return slices_[i].inset_;
return 0;
}
void DocIterator::forwardPos(bool ignorecollapsed)
{
//this dog bites his tail
if (empty()) {
push_back(CursorSlice(*inset_));
return;
}
InsetBase * const nextinset = nextInset();
// jump over collapsables if they are collapsed
// FIXME: the check for asInsetMath() shouldn't be necessary
// but math insets do not return a sensible editable() state yet.
if (ignorecollapsed && nextinset && (!nextinset->asInsetMath()
&& nextinset->editable() != InsetBase::HIGHLY_EDITABLE)) {
++top().pos();
return;
}
CursorSlice & tip = top();
//lyxerr << "XXX\n" << *this << endl;
// this is used twice and shows up in the profiler!
pos_type const lastp = lastpos();
// move into an inset to the right if possible
InsetBase * n = 0;
if (tip.pos() != lastp) {
// this is impossible for pos() == size()
if (inMathed()) {
n = (tip.cell().begin() + tip.pos())->nucleus();
} else {
if (paragraph().isInset(tip.pos()))
n = paragraph().getInset(tip.pos());
}
}
if (n && n->isActive()) {
//lyxerr << "... descend" << endl;
push_back(CursorSlice(*n));
return;
}
// otherwise move on one position if possible
if (tip.pos() < lastp) {
//lyxerr << "... next pos" << endl;
++tip.pos();
return;
}
//lyxerr << "... no next pos" << endl;
// otherwise move on one paragraph if possible
if (tip.pit() < lastpit()) {
//lyxerr << "... next par" << endl;
++tip.pit();
tip.pos() = 0;
return;
}
//lyxerr << "... no next pit" << endl;
// otherwise try to move on one cell if possible
if (tip.idx() < lastidx()) {
//lyxerr << "... next idx" << endl;
++tip.idx();
tip.pit() = 0;
tip.pos() = 0;
return;
}
//lyxerr << "... no next idx" << endl;
// otherwise leave inset and jump over inset as a whole
pop_back();
// 'top' is invalid now...
if (!empty())
++top().pos();
}
void DocIterator::forwardPosNoDescend()
{
CursorSlice & tip = top();
pos_type const lastp = lastpos();
// move on one position if possible
if (tip.pos() < lastp) {
//lyxerr << "... next pos" << endl;
++tip.pos();
return;
}
//lyxerr << "... no next pos" << endl;
// otherwise move on one paragraph if possible
if (tip.pit() < lastpit()) {
//lyxerr << "... next par" << endl;
++tip.pit();
tip.pos() = 0;
return;
}
//lyxerr << "... no next pit" << endl;
// otherwise try to move on one cell if possible
if (tip.idx() < lastidx()) {
//lyxerr << "... next idx" << endl;
++tip.idx();
tip.pit() = 0;
tip.pos() = 0;
return;
}
//lyxerr << "... no next idx" << endl;
// otherwise leave inset and jump over inset as a whole
pop_back();
// 'top' is invalid now...
if (!empty())
++top().pos();
}
void DocIterator::forwardPar()
{
forwardPos();
while (!empty() && (!inTexted() || pos() != 0)) {
if (inTexted()) {
pos_type const lastp = lastpos();
Paragraph const & par = paragraph();
pos_type & pos = top().pos();
if (par.insetlist.empty())
pos = lastp;
else
while (pos < lastp && !par.isInset(pos))
++pos;
}
forwardPos();
}
}
void DocIterator::forwardChar()
{
forwardPos();
while (!empty() && pos() == lastpos())
forwardPos();
}
void DocIterator::forwardInset()
{
forwardPos();
while (!empty() && !nextInset()) {
if (inTexted()) {
pos_type const lastp = lastpos();
Paragraph const & par = paragraph();
pos_type & pos = top().pos();
while (pos < lastp && !par.isInset(pos))
++pos;
if (pos < lastp)
break;
}
forwardPos();
}
}
void DocIterator::backwardChar()
{
backwardPos();
while (!empty() && pos() == lastpos())
backwardPos();
}
void DocIterator::backwardPos()
{
//this dog bites his tail
if (empty()) {
push_back(CursorSlice(*inset_));
top().idx() = lastidx();
top().pit() = lastpit();
top().pos() = lastpos();
return;
}
CursorSlice & tip = top();
if (tip.pos() != 0) {
--tip.pos();
} else if (tip.pit() != 0) {
--tip.pit();
tip.pos() = lastpos();
return;
} else if (tip.idx() != 0) {
--tip.idx();
tip.pit() = lastpit();
tip.pos() = lastpos();
return;
} else {
pop_back();
return;
}
// move into an inset to the left if possible
InsetBase * n = 0;
if (inMathed()) {
n = (tip.cell().begin() + tip.pos())->nucleus();
} else {
if (paragraph().isInset(tip.pos()))
n = paragraph().getInset(tip.pos());
}
if (n && n->isActive()) {
push_back(CursorSlice(*n));
top().idx() = lastidx();
top().pit() = lastpit();
top().pos() = lastpos();
}
}
bool DocIterator::hasPart(DocIterator const & it) const
{
// it can't be a part if it is larger
if (it.depth() > depth())
return false;
// as inset adresses are the 'last' level
return &it.top().inset() == &slices_[it.depth() - 1].inset();
}
void DocIterator::updateInsets(InsetBase * inset)
{
// this function re-creates the cache of inset pointers.
// code taken in part from StableDocIterator::asDocIterator.
//lyxerr << "converting:\n" << *this << endl;
DocIterator dit = DocIterator(*inset);
size_t const n = slices_.size();
for (size_t i = 0 ; i < n; ++i) {
BOOST_ASSERT(inset);
dit.push_back(slices_[i]);
dit.top().inset_ = inset;
if (i + 1 != n)
inset = dit.nextInset();
}
//lyxerr << "converted:\n" << *this << endl;
operator=(dit);
}
std::ostream & operator<<(std::ostream & os, DocIterator const & dit)
{
for (size_t i = 0, n = dit.depth(); i != n; ++i)
os << " " << dit[i] << "\n";
return os;
}
///////////////////////////////////////////////////////
StableDocIterator::StableDocIterator(DocIterator const & dit)
{
data_ = dit.internalData();
for (size_t i = 0, n = data_.size(); i != n; ++i)
data_[i].inset_ = 0;
}
DocIterator StableDocIterator::asDocIterator(InsetBase * inset) const
{
// this function re-creates the cache of inset pointers
//lyxerr << "converting:\n" << *this << endl;
DocIterator dit = DocIterator(*inset);
for (size_t i = 0, n = data_.size(); i != n; ++i) {
if (inset == 0) {
// FIXME
lyxerr << BOOST_CURRENT_FUNCTION
<< " Should not happen, but does e.g. after C-n C-l C-z S-C-z"
<< '\n' << "dit: " << dit << '\n'
<< " lastpos: " << dit.lastpos() << endl;
//break;
BOOST_ASSERT(false);
}
dit.push_back(data_[i]);
dit.top().inset_ = inset;
if (i + 1 != n)
inset = dit.nextInset();
}
//lyxerr << "convert:\n" << *this << " to:\n" << dit << endl;
return dit;
}
std::ostream & operator<<(std::ostream & os, StableDocIterator const & dit)
{
for (size_t i = 0, n = dit.data_.size(); i != n; ++i)
os << " " << dit.data_[i] << "\n";
return os;
}
bool operator==(StableDocIterator const & dit1, StableDocIterator const & dit2)
{
return dit1.data_ == dit2.data_;
}
} // namespace lyx