X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferView2.C;h=fa9456ea50788a105b496d7242557021762e5155;hb=98c966c64594611e469313314abd1e59524adb4a;hp=8ad5d776a2eeca43070b9626d799200051bc6657;hpb=09b61de3036345b16cf31546fc12d307dbef2bbe;p=lyx.git diff --git a/src/BufferView2.C b/src/BufferView2.C index 8ad5d776a2..fa9456ea50 100644 --- a/src/BufferView2.C +++ b/src/BufferView2.C @@ -1,8 +1,8 @@ /* This file is part of - * ====================================================== - * + * ====================================================== + * * LyX, The Document Processor - * + * * Copyright 1995 Matthias Ettrich * Copyright 1995-2001 The LyX Team. * @@ -34,9 +34,9 @@ #include "support/filetools.h" #include "support/lyxfunctional.h" //equal_1st_in_pair #include "support/types.h" +#include "support/lyxalgo.h" // lyx_count #include -#include extern BufferList bufferlist; @@ -47,7 +47,6 @@ using std::endl; using std::ifstream; using std::vector; using std::find; -using std::count; using std::count_if; @@ -74,7 +73,7 @@ bool BufferView::insertLyXFile(string const & filen) MakeDisplayPath(fname, 50)); return false; } - + beforeChange(text); ifstream ifs(fname.c_str()); @@ -84,9 +83,9 @@ bool BufferView::insertLyXFile(string const & filen) MakeDisplayPath(fname, 50)); return false; } - + int const c = ifs.peek(); - + LyXLex lex(0, 0); lex.setStream(ifs); @@ -96,7 +95,7 @@ bool BufferView::insertLyXFile(string const & filen) lyxerr[Debug::INFO] << "Will insert file with header" << endl; res = buffer()->readFile(lex, text->cursor.par()); } else { - lyxerr[Debug::INFO] << "Will insert file without header" + lyxerr[Debug::INFO] << "Will insert file without header" << endl; res = buffer()->readLyXformat2(lex, text->cursor.par()); } @@ -108,38 +107,43 @@ bool BufferView::insertLyXFile(string const & filen) bool BufferView::removeAutoInsets() { - LyXCursor tmpcursor = text->cursor; - Paragraph * cur_par = tmpcursor.par(); - Paragraph * cur_par_prev = cur_par ? cur_par->previous() : 0; - Paragraph * cur_par_next = cur_par ? cur_par->next() : 0; - pos_type cur_pos = tmpcursor.pos(); + // keep track of which pos and par the cursor was on + Paragraph * cursor_par = text->cursor.par(); + Paragraph * cursor_par_prev = cursor_par ? cursor_par->previous() : 0; + Paragraph * cursor_par_next = cursor_par ? cursor_par->next() : 0; + pos_type cursor_pos = text->cursor.pos(); bool found = false; // Trap the deletion of the paragraph the cursor is in. - // It should be almost impossible for the new cursor par to be - // deleted later on in this function. - // This is the way to segfault this now. Although you may have to do this - // multiple times: Have an InsetERT with an unknown command in it. - // View->DVI, move cursor between Error box and InsetERT and hit , - // , again, View->DVI, BANG! - // - while ((cur_par_prev || cur_par_next) + // Iterate until we find a paragraph that won't be immediately deleted. + // In reality this should mean we only execute the body of the while + // loop once at most. However for safety we iterate rather than just + // make this an if () conditional. + while ((cursor_par_prev || cursor_par_next) && text->setCursor(this, - cur_par_prev ? cur_par_prev : cur_par_next, + cursor_par_prev ? cursor_par_prev : cursor_par_next, 0)) { - // we just removed cur_par so have to fix the "cursor" - if (cur_par_prev) { - cur_par = cur_par_prev; - cur_pos = cur_par->size(); + // We just removed cursor_par so have to fix the "cursor" + if (cursor_par_prev) { + // '.' = cursor_par + // a -> a. + // . + cursor_par = cursor_par_prev; + cursor_pos = cursor_par->size(); } else { - cur_par = cur_par_next; - cur_pos = 0; + // . -> .a + // a + cursor_par = cursor_par_next; + cursor_pos = 0; } - cur_par_prev = cur_par->previous(); - cur_par_next = cur_par->next(); + cursor_par_prev = cursor_par->previous(); + cursor_par_next = cursor_par->next(); } + // Iterate through the paragraphs removing autoDelete insets as we go. + // If the paragraph ends up empty after all the autoDelete insets are + // removed that paragraph will be removed by the next setCursor() call. ParIterator it = buffer()->par_iterator_begin(); ParIterator end = buffer()->par_iterator_end(); for (; it != end; ++it) { @@ -148,30 +152,35 @@ bool BufferView::removeAutoInsets() bool removed = false; if (text->setCursor(this, par, 0) - && cur_par == par_prev) { + && cursor_par == par_prev) { // The previous setCursor line was deleted and that - // was the cur_par line. This can only happen if an - // error box was the sole item on cur_par. - if (cur_par_prev) { - // '|' = par, '.' = cur_par, 'E' = error box + // was the cursor_par line. This can only happen if an + // error box was the sole item on cursor_par. + // It is possible for cursor_par_prev to be stray if + // the line it pointed to only had a error box on it + // so we have to set it to a known correct value. + // This is often the same value it already had. + cursor_par_prev = par->previous(); + if (cursor_par_prev) { + // '|' = par, '.' = cursor_par, 'E' = error box // First step below may occur before while{} - // a |a a a .a + // a |a a a a. // E -> .E -> |.E -> . -> |b // . b b |b // b - cur_par = cur_par_prev; - cur_pos = cur_par_prev->size(); - cur_par_prev = cur_par->previous(); - // cur_par_next remains the same - } else if (cur_par_next) { + cursor_par = cursor_par_prev; + cursor_pos = cursor_par_prev->size(); + cursor_par_prev = cursor_par->previous(); + // cursor_par_next remains the same + } else if (cursor_par_next) { // First step below may occur before while{} // . // E -> |.E -> |. -> . -> .|a // a a a |a - cur_par = cur_par_next; - cur_pos = 0; - // cur_par_prev remains unset - cur_par_next = cur_par->next(); + cursor_par = cursor_par_next; + cursor_pos = 0; + // cursor_par_prev remains unset + cursor_par_next = cursor_par->next(); } else { // I can't find a way to trigger this // so it should be unreachable code @@ -186,11 +195,18 @@ bool BufferView::removeAutoInsets() if (pit->autoDelete()) { removed = true; pos_type const pos = pit.getPos(); - + par->erase(pos); - if (cur_par == par) { - if (cur_pos > pos) - --cur_pos; + // We just invalidated par's inset iterators so + // we get the next valid iterator position + pit = par->InsetIterator(pos); + // and ensure we have a valid end iterator. + pend = par->inset_iterator_end(); + + if (cursor_par == par) { + // update the saved cursor position + if (cursor_pos > pos) + --cursor_pos; } } else { ++pit; @@ -202,7 +218,23 @@ bool BufferView::removeAutoInsets() } } - text->setCursorIntern(this, cur_par, cur_pos); + // It is possible that the last line is empty if it was cursor_par + // and/or only had an error inset on it. So we set the cursor to the + // start of the doc to force its removal and ensure a valid saved cursor + if (text->setCursor(this, text->ownerParagraph(), 0) + && 0 == cursor_par_next) { + cursor_par = cursor_par_prev; + cursor_pos = cursor_par->size(); + } else if (cursor_pos > cursor_par->size()) { + // Some C-Enter lines were removed by the setCursor call which + // then invalidated cursor_pos. It could still be "wrong" because + // the cursor may appear to have jumped but since we collapsed + // some C-Enter lines this should be a reasonable compromise. + cursor_pos = cursor_par->size(); + } + + // restore the original cursor in its corrected location. + text->setCursorIntern(this, cursor_par, cursor_pos); return found; } @@ -222,13 +254,13 @@ void BufferView::insertErrors(TeXErrors & terr) int const errorrow = cit->error_in_line; // Insert error string for row number - int tmpid = -1; + int tmpid = -1; int tmppos = -1; if (buffer()->texrow.getIdFromRow(errorrow, tmpid, tmppos)) { buffer()->texrow.increasePos(tmpid, tmppos); } - + Paragraph * texrowpar = 0; if (tmpid == -1) { @@ -253,7 +285,7 @@ void BufferView::insertErrors(TeXErrors & terr) void BufferView::setCursorFromRow(int row) { - int tmpid = -1; + int tmpid = -1; int tmppos = -1; buffer()->texrow.getIdFromRow(row, tmpid, tmppos); @@ -324,7 +356,7 @@ void BufferView::menuRedo() return; } #endif - + if (available()) { owner()->message(_("Redo")); hideCursor(); @@ -361,7 +393,7 @@ void BufferView::pasteEnvironment() void BufferView::copy() { if (available()) { - text->copySelection(this); + getLyXText()->copySelection(this); owner()->message(_("Copy")); } } @@ -381,7 +413,8 @@ void BufferView::cut(bool realcut) void BufferView::paste() { - if (!available()) return; + if (!available()) + return; owner()->message(_("Paste")); @@ -390,19 +423,22 @@ void BufferView::paste() toggleSelection(); text->clearSelection(); update(text, BufferView::SELECT|BufferView::FITCUR); - + // paste text->pasteSelection(this); update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE); - - // clear the selection +// why fake a selection only I think it should be a real one and not only +// a painted one (Jug 20020318). +#if 0 + // clear the selection toggleSelection(); text->clearSelection(); update(text, BufferView::SELECT|BufferView::FITCUR); +#endif } -/* these functions are for the spellchecker */ +/* these functions are for the spellchecker */ string const BufferView::nextWord(float & value) { if (!available()) { @@ -413,11 +449,11 @@ string const BufferView::nextWord(float & value) return text->selectNextWordToSpellcheck(this, value); } - + void BufferView::selectLastWord() { if (!available()) return; - + LyXCursor cur = text->selection.cursor; hideCursor(); beforeChange(text); @@ -431,7 +467,7 @@ void BufferView::selectLastWord() void BufferView::endOfSpellCheck() { if (!available()) return; - + hideCursor(); beforeChange(text); text->selectSelectedWord(this); @@ -447,15 +483,15 @@ void BufferView::replaceWord(string const & replacestring) LyXText * tt = getLyXText(); hideCursor(); update(tt, BufferView::SELECT|BufferView::FITCUR); - - /* clear the selection (if there is any) */ + + /* clear the selection (if there is any) */ toggleSelection(false); update(tt, BufferView::SELECT|BufferView::FITCUR); - - /* clear the selection (if there is any) */ + + /* clear the selection (if there is any) */ toggleSelection(false); tt->replaceSelectionWithString(this, replacestring); - + tt->setSelectionOverString(this, replacestring); // Go back so that replacement string is also spellchecked @@ -501,7 +537,7 @@ bool BufferView::lockInset(UpdatableInset * inset) } if ((*it)->getInsetFromID(id)) { text->setCursorIntern(this, par, it.getPos()); - theLockingInset(static_cast(*it)); + (*it)->edit(this); return theLockingInset()->lockInsetInInset(this, inset); } } @@ -524,7 +560,7 @@ void BufferView::showLockedInsetCursor(int x, int y, int asc, int desc) (cursor.par()->getInset(cursor.pos() - 1) == locking_inset)) text->setCursor(this, cursor, - cursor.par(), cursor.pos() - 1); + cursor.par(), cursor.pos() - 1); LyXScreen::Cursor_Shape shape = LyXScreen::BAR_SHAPE; LyXText * txt = getLyXText(); if (locking_inset->isTextInset() && @@ -551,13 +587,16 @@ void BufferView::hideLockedInsetCursor() } -void BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc) +bool BufferView::fitLockedInsetCursor(int x, int y, int asc, int desc) { if (theLockingInset() && available()) { y += text->cursor.y() + theLockingInset()->insetInInsetY(); - if (pimpl_->screen_->fitManualCursor(text, this, x, y, asc, desc)) + if (pimpl_->screen_->fitManualCursor(text, this, x, y, asc, desc)) { updateScrollbar(); + return true; + } } + return false; } @@ -569,12 +608,12 @@ int BufferView::unlockInset(UpdatableInset * inset) inset->insetUnlock(this); theLockingInset(0); // make sure we update the combo ! - owner()->setLayout(getLyXText()->cursor.par()->getLayout()); + owner()->setLayout(getLyXText()->cursor.par()->layout()); finishUndo(); return 0; } else if (inset && theLockingInset() && theLockingInset()->unlockInsetInInset(this, inset)) { - // owner inset has updated the layout combo + // owner inset has updated the layout combo finishUndo(); return 0; } @@ -589,8 +628,8 @@ void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind) if (kind == Undo::EDIT) // in this case insets would not be stored! kind = Undo::FINISH; setUndo(this, kind, - text->cursor.par(), - text->cursor.par()->next()); + text->cursor.par(), + text->cursor.par()->next()); } @@ -626,9 +665,9 @@ bool BufferView::ChangeInsets(Inset::Code code, } if (changed_inset) { need_update = true; -#ifdef WITH_WARNINGS -#warning FIXME -#endif + + // FIXME + // The test it.size()==1 was needed to prevent crashes. // How to set the cursor corretly when it.size()>1 ?? if (it.size() == 1) { @@ -648,22 +687,22 @@ bool BufferView::ChangeRefsIfUnique(string const & from, string const & to) { // Check if the label 'from' appears more than once vector labels = buffer()->getLabelList(); - // count is broken on some systems, so use the HP version - int res; - count(labels.begin(), labels.end(), from, res); - if (res > 1) + + if (lyx::count(labels.begin(), labels.end(), from) > 1) return false; return ChangeInsets(Inset::REF_CODE, from, to); } -bool BufferView::ChangeCitationsIfUnique(string const & from, string const & to) +bool BufferView::ChangeCitationsIfUnique(string const & from, + string const & to) { + typedef pair StringPair; - vector > keys = buffer()->getBibkeyList(); - if (count_if(keys.begin(), keys.end(), - lyx::equal_1st_in_pair(from)) + vector keys = buffer()->getBibkeyList(); + if (count_if(keys.begin(), keys.end(), + lyx::equal_1st_in_pair(from)) > 1) return false;