Make sure that undo is recorded when magic tricks are played with InsetBibitem.

Fixes bug #7111: Assertion with undo and InsetBibitem

  * add Paragraph::brokenBiblio(), with tells whether there is something to fix.
  * rename Paragraph::checkBiblio to fixBiblio; simplify it greatly by using
    InsetList methods
  * In TextMetrics::redoParagraph, call recordUndo before fixBiblio (only when
    there is something to fix obviously)

Special care is taken to update cursor only when it makes sense. Note
that there are cases where undo information is not recorded (current
cursor not in the slice where action happens)
This commit is contained in:
Jean-Marc Lasgouttes 2012-07-17 23:59:04 +02:00
parent 33b2bc45a7
commit 7b93cc5fc4
4 changed files with 68 additions and 64 deletions

View File

@ -3167,70 +3167,61 @@ char_type Paragraph::transformChar(char_type c, pos_type pos) const
} }
int Paragraph::checkBiblio(Buffer const & buffer) bool Paragraph::brokenBiblio() const
{ {
// FIXME From JS: // there is a problem if there is no bibitem at position 0 or
// This is getting more and more a mess. ...We really should clean // if there is another bibitem in the paragraph.
// up this bibitem issue for 1.6. return d->layout_->labeltype == LABEL_BIBLIO
&& (d->insetlist_.find(BIBITEM_CODE) != 0
|| d->insetlist_.find(BIBITEM_CODE, 1) > 0);
}
int Paragraph::fixBiblio(Buffer const & buffer)
{
// FIXME: What about the case where paragraph is not BIBLIO
// but there is an InsetBibitem?
// FIXME: when there was already an inset at 0, the return value is 1,
// which does not tell whether another inset has been remove; the
// cursor cannot be correctly updated.
// Add bibitem insets if necessary
if (d->layout_->labeltype != LABEL_BIBLIO) if (d->layout_->labeltype != LABEL_BIBLIO)
return 0; return 0;
bool hasbibitem = !d->insetlist_.empty() bool const track_changes = buffer.params().trackChanges;
// Insist on it being in pos 0 int bibitem_pos = d->insetlist_.find(BIBITEM_CODE);
&& d->text_[0] == META_INSET bool const hasbibitem0 = bibitem_pos == 0;
&& d->insetlist_.begin()->inset->lyxCode() == BIBITEM_CODE;
bool track_changes = buffer.params().trackChanges; if (hasbibitem0) {
bibitem_pos = d->insetlist_.find(BIBITEM_CODE, 1);
docstring oldkey; // There was an InsetBibitem at pos 0, and no other one => OK
docstring oldlabel; if (bibitem_pos == -1)
// remove a bibitem in pos != 0
// restore it later in pos 0 if necessary
// (e.g. if a user inserts contents _before_ the item)
// we're assuming there's only one of these, which there
// should be.
int erasedInsetPosition = -1;
InsetList::iterator it = d->insetlist_.begin();
InsetList::iterator end = d->insetlist_.end();
for (; it != end; ++it)
if (it->inset->lyxCode() == BIBITEM_CODE
&& it->pos > 0) {
InsetCommand * olditem = it->inset->asInsetCommand();
oldkey = olditem->getParam("key");
oldlabel = olditem->getParam("label");
erasedInsetPosition = it->pos;
eraseChar(erasedInsetPosition, track_changes);
break;
}
// There was an InsetBibitem at the beginning, and we didn't
// have to erase one.
if (hasbibitem && erasedInsetPosition < 0)
return 0; return 0;
// there is a bibitem at the 0 position, but since
// There was an InsetBibitem at the beginning and we did have to // there is a second one, we copy the second on the
// erase one. So we give its properties to the beginning inset. // first. We're assuming there are at most two of
if (hasbibitem) { // these, which there should be.
InsetCommand * inset = d->insetlist_.begin()->inset->asInsetCommand(); // FIXME: why does it make sense to do that rather
if (!oldkey.empty()) // than keep the first? (JMarc)
inset->setParam("key", oldkey); Inset * inset = d->insetlist_.release(bibitem_pos);
inset->setParam("label", oldlabel); eraseChar(bibitem_pos, track_changes);
return -erasedInsetPosition; d->insetlist_.begin()->inset = inset;
return -bibitem_pos;
} }
// There was no inset at the beginning, so we need to create one with // We need to create an inset at the beginning
// the key and label of the one we erased. Inset * inset = 0;
InsetBibitem * inset = if (bibitem_pos > 0) {
new InsetBibitem(const_cast<Buffer *>(&buffer), InsetCommandParams(BIBITEM_CODE)); // there was one somewhere in the paragraph, let's move it
// restore values of previously deleted item in this par. inset = d->insetlist_.release(bibitem_pos);
if (!oldkey.empty()) eraseChar(bibitem_pos, track_changes);
inset->setParam("key", oldkey); } else
inset->setParam("label", oldlabel); // make a fresh one
insertInset(0, inset, inset = new InsetBibitem(const_cast<Buffer *>(&buffer),
Change(track_changes ? Change::INSERTED : Change::UNCHANGED)); InsetCommandParams(BIBITEM_CODE));
insertInset(0, inset, Change(track_changes ? Change::INSERTED
: Change::UNCHANGED));
return 1; return 1;
} }

View File

@ -440,6 +440,8 @@ public:
/// ///
ParagraphParameters const & params() const; ParagraphParameters const & params() const;
/// Check whether a call to fixBiblio is needed.
bool brokenBiblio() const;
/// Check if we are in a Biblio environment and insert or /// Check if we are in a Biblio environment and insert or
/// delete InsetBibitems as necessary. /// delete InsetBibitems as necessary.
/// \retval int 1, if we had to add an inset, in which case /// \retval int 1, if we had to add an inset, in which case
@ -447,7 +449,7 @@ public:
/// an inset, in which case pos is the position from which the inset /// an inset, in which case pos is the position from which the inset
/// was deleted, and the cursor will need to be moved back one if it /// was deleted, and the cursor will need to be moved back one if it
/// was previously past that position. Return 0 otherwise. /// was previously past that position. Return 0 otherwise.
int checkBiblio(Buffer const & buffer); int fixBiblio(Buffer const & buffer);
/// For each author, set 'used' to true if there is a change /// For each author, set 'used' to true if there is a change
/// by this author in the paragraph. /// by this author in the paragraph.

View File

@ -375,19 +375,28 @@ bool TextMetrics::redoParagraph(pit_type const pit)
main_text_ = (text_ == &buffer.text()); main_text_ = (text_ == &buffer.text());
bool changed = false; bool changed = false;
// Check whether there are InsetBibItems that need fixing
// FIXME: This check ought to be done somewhere else. It is the reason // FIXME: This check ought to be done somewhere else. It is the reason
// why text_ is not const. But then, where else to do it? // why text_ is not const. But then, where else to do it?
// Well, how can you end up with either (a) a biblio environment that // Well, how can you end up with either (a) a biblio environment that
// has no InsetBibitem or (b) a biblio environment with more than one // has no InsetBibitem or (b) a biblio environment with more than one
// InsetBibitem? I think the answer is: when paragraphs are merged; // InsetBibitem? I think the answer is: when paragraphs are merged;
// when layout is set; when material is pasted. // when layout is set; when material is pasted.
int const moveCursor = par.checkBiblio(buffer); if (par.brokenBiblio()) {
if (moveCursor > 0) Cursor & cur = const_cast<Cursor &>(bv_->cursor());
const_cast<Cursor &>(bv_->cursor()).posForward(); // In some cases, we do not know how to record undo
else if (moveCursor < 0) { if (&cur.inset() == &text_->inset())
Cursor & cursor = const_cast<Cursor &>(bv_->cursor()); cur.recordUndo(ATOMIC_UNDO, pit, pit);
if (cursor.pos() >= -moveCursor)
cursor.posBackward(); int const moveCursor = par.fixBiblio(buffer);
// Is it necessary to update the cursor?
if (&cur.inset() == &text_->inset() && cur.pit() == pit) {
if (moveCursor > 0)
cur.posForward();
else if (moveCursor < 0 && cur.pos() >= -moveCursor)
cur.posBackward();
}
} }
// Optimisation: this is used in the next two loops // Optimisation: this is used in the next two loops

View File

@ -95,6 +95,8 @@ What's new
- Fix various crashes when single document is edited in more windows - Fix various crashes when single document is edited in more windows
(bug 8203). (bug 8203).
- Fix crash when using undo in a paragraph with layout Bibliography (bug 7111).
- Replace current selection when pasting (bug 8027). - Replace current selection when pasting (bug 8027).
- Make sure that undo restores environment depth correctly (bug 8159). - Make sure that undo restores environment depth correctly (bug 8159).