On Wed, Mar 12, 2003 at 02:38:12PM +0100, Christian Ridderstr?m wrote: > I'll play with it tonight... even though I'm not sure which patch this > is... :)
You're testing the patch that simply does not remove empty pars, except on save of the .lyx file. Try this one instead. So far, I have found one bug, and hopefully fixed it - please try merging pars with different/same layouts together via backspace and delete. Everyone, there is one nasty hack here, but I don't know a better solution. Comments welcome (and also if you know of places where we have depm-dependent code we don't need any more). > PS. John, you only got one mail this time, right? DS right :) john Index: BufferView.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/BufferView.C,v retrieving revision 1.125 diff -u -p -r1.125 BufferView.C --- BufferView.C 12 Mar 2003 05:46:32 -0000 1.125 +++ BufferView.C 12 Mar 2003 15:04:11 -0000 @@ -334,135 +334,50 @@ bool BufferView::insertLyXFile(string co bool BufferView::removeAutoInsets() { - // 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. - // 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, - cursor_par_prev ? cursor_par_prev : cursor_par_next, - 0)) { - // 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 { - // . -> .a - // a - cursor_par = cursor_par_next; - cursor_pos = 0; - } - 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) { Paragraph * par = *it; - Paragraph * par_prev = par ? par->previous() : 0; - bool removed = false; - - if (text->setCursor(this, par, 0) - && cursor_par == par_prev) { - // The previous setCursor line was deleted and that - // 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. - // E -> .E -> |.E -> . -> |b - // . b b |b - // b - 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 - 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 - // unless the buffer is corrupted. - lyxerr << "BufferView::removeAutoInsets() is bad\n"; - } - } InsetList::iterator pit = par->insetlist.begin(); InsetList::iterator pend = par->insetlist.end(); + bool removed = false; + while (pit != pend) { - if (pit.getInset()->autoDelete()) { - removed = true; - pos_type const pos = pit.getPos(); - - par->erase(pos); - // We just invalidated par's inset iterators so - // we get the next valid iterator position - pit = par->insetlist.insetIterator(pos); - // and ensure we have a valid end iterator. - pend = par->insetlist.end(); - - if (cursor_par == par) { - // update the saved cursor position - if (cursor_pos > pos) - --cursor_pos; - } - } else { + if (!pit.getInset()->autoDelete()) { ++pit; + continue; } + + pos_type const pos = pit.getPos(); + + par->erase(pos); + removed = true; + found = true; + + // We just invalidated par's inset iterators so + // we get the next valid iterator position + // FIXME: erase should return the next iterator like STL + pit = par->insetlist.insetIterator(pos); + + // and ensure we have a valid end iterator. + pend = par->insetlist.end(); + + ++pit; } + if (removed) { - found = true; + // don't leave cursor in an impossible position + if (par == text->cursor.par()) + text->cursor.pos(0); + text->redoParagraph(this); } } - - // 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; } Index: lyxtext.h =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/lyxtext.h,v retrieving revision 1.139 diff -u -p -r1.139 lyxtext.h --- lyxtext.h 10 Mar 2003 00:05:05 -0000 1.139 +++ lyxtext.h 12 Mar 2003 15:04:12 -0000 @@ -306,7 +306,7 @@ public: /// void selectSelectedWord(BufferView *); /// returns true if par was empty and was removed - bool setCursor(BufferView *, Paragraph * par, + void setCursor(BufferView *, Paragraph * par, lyx::pos_type pos, bool setfont = true, bool boundary = false) const; @@ -536,14 +536,13 @@ private: void setHeightOfRow(BufferView *, Row * row_ptr) const; // fix the cursor `cur' after a characters has been deleted at `where' - // position. Called by deleteEmptyParagraphMechanism + // position. Called by deleteExtraSpaces void fixCursorAfterDelete(BufferView * bv, LyXCursor & cur, LyXCursor const & where) const; - /// delete double space (false) or empty paragraphs (true) around old_cursor - bool deleteEmptyParagraphMechanism(BufferView *, - LyXCursor const & old_cursor) const; + /// delete double spaces + void deleteExtraSpaces(BufferView *, LyXCursor const & old_cursor) const; public: /** Updates all counters starting BEHIND the row. Changed paragraphs Index: paragraph.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/paragraph.C,v retrieving revision 1.249 diff -u -p -r1.249 paragraph.C --- paragraph.C 12 Mar 2003 06:53:49 -0000 1.249 +++ paragraph.C 12 Mar 2003 15:04:14 -0000 @@ -24,6 +24,7 @@ #include "ParameterStruct.h" #include "gettext.h" #include "changes.h" +#include "paragraph_funcs.h" #include "insets/insetbibitem.h" #include "insets/insetoptarg.h" @@ -142,6 +143,25 @@ Paragraph::~Paragraph() } +bool Paragraph::ignore() const +{ + if (!empty()) + return false; + + if (layout()->free_spacing || isFreeSpacing()) + return false; + + if (!pimpl_->inset_owner) + return true; + + // This is quite a hack. Output an empty paragraph + // if it's the only par inside an inset. Nice + // solutions welcome. + InsetText const * it = static_cast<InsetText const *>(pimpl_->inset_owner); + return !(it->paragraphs.size() == 1); +} + + void Paragraph::write(Buffer const * buf, ostream & os, BufferParams const & bparams, depth_type & dth) const @@ -160,6 +180,9 @@ void Paragraph::write(Buffer const * buf } } } + + if (ignore()) + return; // First write the layout os << "\n\\layout " << layout()->name() << '\n'; Index: paragraph.h =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/paragraph.h,v retrieving revision 1.62 diff -u -p -r1.62 paragraph.h --- paragraph.h 11 Mar 2003 15:01:27 -0000 1.62 +++ paragraph.h 12 Mar 2003 15:04:14 -0000 @@ -77,7 +77,10 @@ public: string const asString(Buffer const *, lyx::pos_type beg, lyx::pos_type end, bool label) const; - /// + /// should we write this paragraph to the .lyx file ? + bool ignore() const; + + /// write the paragraph to the lyx file void write(Buffer const *, std::ostream &, BufferParams const &, depth_type & depth) const; /// Index: text.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/text.C,v retrieving revision 1.307 diff -u -p -r1.307 text.C --- text.C 11 Mar 2003 15:01:27 -0000 1.307 +++ text.C 12 Mar 2003 15:04:19 -0000 @@ -178,30 +178,6 @@ unsigned char LyXText::transformChar(uns } } -// This is the comments that some of the warnings below refers to. -// There are some issues in this file and I don't think they are -// really related to the FIX_DOUBLE_SPACE patch. I'd rather think that -// this is a problem that has been here almost from day one and that a -// larger userbase with differenct access patters triggers the bad -// behaviour. (segfaults.) What I think happen is: In several places -// we store the paragraph in the current cursor and then moves the -// cursor. This movement of the cursor will delete paragraph at the -// old position if it is now empty. This will make the temporary -// pointer to the old cursor paragraph invalid and dangerous to use. -// And is some cases this will trigger a segfault. I have marked some -// of the cases where this happens with a warning, but I am sure there -// are others in this file and in text2.C. There is also a note in -// Delete() that you should read. In Delete I store the paragraph->id -// instead of a pointer to the paragraph. I am pretty sure this faulty -// use of temporary pointers to paragraphs that might have gotten -// invalidated (through a cursor movement) before they are used, are -// the cause of the strange crashes we get reported often. -// -// It is very tiresom to change this code, especially when it is as -// hard to read as it is. Help to fix all the cases where this is done -// would be greately appreciated. -// -// Lgb int LyXText::singleWidth(BufferView * bview, Paragraph * par, pos_type pos) const @@ -1886,13 +1862,6 @@ void LyXText::prepareToPrint(BufferView } -// important for the screen - - -// the cursor set functions have a special mechanism. When they -// realize, that you left an empty paragraph, they will delete it. -// They also delete the corresponding row - void LyXText::cursorRightOneWord(BufferView * bview) const { // treat floats, HFills and Insets as words @@ -2380,36 +2349,18 @@ void LyXText::transposeChars(BufferView void LyXText::Delete(BufferView * bview) { - // this is a very easy implementation - LyXCursor old_cursor = cursor; - int const old_cur_par_id = old_cursor.par()->id(); - int const old_cur_par_prev_id = old_cursor.par()->previous() ? - old_cursor.par()->previous()->id() : -1; // just move to the right cursorRight(bview); - // CHECK Look at the comment here. - // This check is not very good... - // The cursorRightIntern calls DeleteEmptyParagrapgMechanism - // and that can very well delete the par or par->previous in - // old_cursor. Will a solution where we compare paragraph id's - //work better? - if ((cursor.par()->previous() ? cursor.par()->previous()->id() : -1) - == old_cur_par_prev_id - && cursor.par()->id() != old_cur_par_id) { - // delete-empty-paragraph-mechanism has done it - return; - } - // if you had success make a backspace if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) { LyXCursor tmpcursor = cursor; // to make sure undo gets the right cursor position cursor = old_cursor; setUndo(bview, Undo::DELETE, - cursor.par(), cursor.par()->next()); + cursor.par(), cursor.par()->next()); cursor = tmpcursor; backspace(bview); } @@ -2424,49 +2375,23 @@ void LyXText::backspace(BufferView * bvi cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1); - if (cursor.pos() == 0) { - // The cursor is at the beginning of a paragraph, - // so the the backspace will collapse two paragraphs into one. + bool at_start = cursor.pos() == 0; + + // A special case: allow the merge if we're just after + // a non-manual label (e.g. chapter number) + Paragraph * parprev = cursor.par()->previous(); + if (parprev && parprev->empty() + && cursor.pos() == cursor.par()->beginningOfBody() + && cursor.par()->layout()->labeltype != MARGIN_MANUAL) + at_start = true; + + if (at_start) { + // the backspace will collapse two paragraphs into one. // but it's not allowed unless it's new if (cursor.par()->isChangeEdited(0, cursor.par()->size())) return; - // we may paste some paragraphs - - // is it an empty paragraph? - - if ((lastpos == 0 - || (lastpos == 1 && cursor.par()->isSeparator(0)))) { - // This is an empty paragraph and we delete it just by moving the cursor one step - // left and let the DeleteEmptyParagraphMechanism handle the actual deletion - // of the paragraph. - - if (cursor.par()->previous()) { - Paragraph * tmppar = cursor.par()->previous(); - if (cursor.par()->layout() == tmppar->layout() - && cursor.par()->getAlign() == tmppar->getAlign()) { - // Inherit bottom DTD from the paragraph below. - // (the one we are deleting) - tmppar->params().lineBottom(cursor.par()->params().lineBottom()); - tmppar->params().spaceBottom(cursor.par()->params().spaceBottom()); - tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom()); - } - - cursorLeft(bview); - - // the layout things can change the height of a row ! - int const tmpheight = cursor.row()->height(); - setHeightOfRow(bview, cursor.row()); - if (cursor.row()->height() != tmpheight) { - refresh_y = cursor.y() - cursor.row()->baseline(); - refresh_row = cursor.row(); - status(bview, LyXText::NEED_MORE_REFRESH); - } - return; - } - } - if (cursor.par()->previous()) { setUndo(bview, Undo::DELETE, cursor.par()->previous(), cursor.par()->next()); @@ -2475,36 +2400,38 @@ void LyXText::backspace(BufferView * bvi Paragraph * tmppar = cursor.par(); Row * tmprow = cursor.row(); - // We used to do cursorLeftIntern() here, but it is - // not a good idea since it triggers the auto-delete - // mechanism. So we do a cursorLeftIntern()-lite, - // without the dreaded mechanism. (JMarc) + // step to the end of the previous paragraph if (cursor.par()->previous()) { - // steps into the above paragraph. setCursorIntern(bview, cursor.par()->previous(), cursor.par()->previous()->size(), false); } - // Pasting is not allowed, if the paragraphs have different + // Merging is not allowed if the paragraphs have different // layout. I think it is a real bug of all other // word processors to allow it. It confuses the user. - // Even so with a footnote paragraph and a non-footnote - // paragraph. I will not allow pasting in this case, - // because the user would be confused if the footnote behaves - // different wether it is open or closed. - - // Correction: Pasting is always allowed with standard-layout - LyXTextClass const & tclass = - bview->buffer()->params.getLyXTextClass(); - - if (cursor.par() != tmppar - && (cursor.par()->layout() == tmppar->layout() - || tmppar->layout() == tclass.defaultLayout()) - && cursor.par()->getAlign() == tmppar->getAlign()) { + // Merging is always allowed with the standard layout though. + + LyXTextClass const & tclass = bview->buffer()->params.getLyXTextClass(); + + bool should_merge = cursor.par()->empty(); + + should_merge |= (cursor.par()->layout() == tmppar->layout() + || tmppar->layout() == tclass.defaultLayout()); + + // This is to get natural layout merging when deleting empty + // paragraphs. + LyXLayout_ptr const layout = + (cursor.par()->empty() ? tmppar->layout() : cursor.par()->layout()); + + if (cursor.par()->getAlign() != tmppar->getAlign()) + should_merge = false; + + if (should_merge) { removeParagraph(tmprow); removeRow(tmprow); mergeParagraph(bview->buffer()->params, bview->buffer()->paragraphs, cursor.par()); + cursor.par()->layout(layout); if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1)) ; //cursor.par()->insertChar(cursor.pos(), ' '); @@ -2519,14 +2446,6 @@ void LyXText::backspace(BufferView * bvi refresh_row = cursor.row(); refresh_y = cursor.y() - cursor.row()->baseline(); - // remove the lost paragraph - // This one is not safe, since the paragraph that the tmprow and the - // following rows belong to has been deleted by the PasteParagraph - // above. The question is... could this be moved in front of the - // PasteParagraph? - //RemoveParagraph(tmprow); - //RemoveRow(tmprow); - // This rebuilds the rows. appendParagraph(bview, cursor.row()); updateCounters(bview); @@ -2539,10 +2458,7 @@ void LyXText::backspace(BufferView * bvi // any paragraphs setUndo(bview, Undo::DELETE, cursor.par(), cursor.par()->next()); - // We used to do cursorLeftIntern() here, but it is - // not a good idea since it triggers the auto-delete - // mechanism. So we do a cursorLeftIntern()-lite, - // without the dreaded mechanism. (JMarc) + setCursorIntern(bview, cursor.par(), cursor.pos()- 1, false, cursor.boundary()); Index: text2.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/text2.C,v retrieving revision 1.286 diff -u -p -r1.286 text2.C --- text2.C 11 Mar 2003 16:38:37 -0000 1.286 +++ text2.C 12 Mar 2003 15:04:21 -0000 @@ -850,10 +850,6 @@ void LyXText::fullRebreak(BufferView * b // important for the screen -// the cursor set functions have a special mechanism. When they -// realize, that you left an empty paragraph, they will delete it. -// They also delete the corresponding row - // need the selection cursor: void LyXText::setSelection(BufferView * bview) { @@ -1737,13 +1733,13 @@ bool LyXText::updateInset(BufferView * b } -bool LyXText::setCursor(BufferView * bview, Paragraph * par, +void LyXText::setCursor(BufferView * bview, Paragraph * par, pos_type pos, bool setfont, bool boundary) const { LyXCursor old_cursor = cursor; setCursorIntern(bview, par, pos, setfont, boundary); - return deleteEmptyParagraphMechanism(bview, old_cursor); + deleteExtraSpaces(bview, old_cursor); } @@ -2061,7 +2057,7 @@ void LyXText::setCursorFromCoordinates(B setCursorFromCoordinates(bview, cursor, x, y); setCurrentFont(bview); - deleteEmptyParagraphMechanism(bview, old_cursor); + deleteExtraSpaces(bview, old_cursor); } @@ -2217,7 +2213,7 @@ void LyXText::cursorDownParagraph(Buffer } // fix the cursor `cur' after a characters has been deleted at `where' -// position. Called by deleteEmptyParagraphMechanism +// position. Called by deleteExtraSpaces void LyXText::fixCursorAfterDelete(BufferView * bview, LyXCursor & cur, LyXCursor const & where) const @@ -2242,17 +2238,17 @@ void LyXText::fixCursorAfterDelete(Buffe } -bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview, - LyXCursor const & old_cursor) const +void LyXText::deleteExtraSpaces(BufferView * bview, + LyXCursor const & old_cursor) const { // Would be wrong to delete anything if we have a selection. if (selection.set()) - return false; + return; // We allow all kinds of "mumbo-jumbo" when freespacing. if (old_cursor.par()->layout()->free_spacing || old_cursor.par()->isFreeSpacing()) { - return false; + return; } /* Ok I'll put some comments here about what is missing. @@ -2308,120 +2304,8 @@ bool LyXText::deleteEmptyParagraphMechan fixCursorAfterDelete(bview, toggle_cursor, old_cursor); fixCursorAfterDelete(bview, toggle_end_cursor, old_cursor); - return false; - } - } - - // don't delete anything if this is the ONLY paragraph! - if (!old_cursor.par()->next() && !old_cursor.par()->previous()) - return false; - - // Do not delete empty paragraphs with keepempty set. - if (old_cursor.par()->layout()->keepempty) - return false; - - // only do our magic if we changed paragraph - if (old_cursor.par() == cursor.par()) - return false; - - // record if we have deleted a paragraph - // we can't possibly have deleted a paragraph before this point - bool deleted = false; - - if ((old_cursor.par()->empty() - || (old_cursor.par()->size() == 1 - && old_cursor.par()->isLineSeparator(0)))) { - // ok, we will delete anything - LyXCursor tmpcursor; - - // make sure that you do not delete any environments - status(bview, LyXText::NEED_MORE_REFRESH); - deleted = true; - - if (old_cursor.row()->previous()) { - refresh_row = old_cursor.row()->previous(); - refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height(); - tmpcursor = cursor; - cursor = old_cursor; // that undo can restore the right cursor position - Paragraph * endpar = old_cursor.par()->next(); - if (endpar && endpar->getDepth()) { - while (endpar && endpar->getDepth()) { - endpar = endpar->next(); - } - } - setUndo(bview, Undo::DELETE, old_cursor.par(), endpar); - cursor = tmpcursor; - - // delete old row - removeRow(old_cursor.row()); - if (ownerParagraph() == old_cursor.par()) { - ownerParagraph(ownerParagraph()->next()); - } - // delete old par - delete old_cursor.par(); - - /* Breakagain the next par. Needed because of - * the parindent that can occur or dissappear. - * The next row can change its height, if - * there is another layout before */ - if (refresh_row->next()) { - breakAgain(bview, refresh_row->next()); - updateCounters(bview); - } - setHeightOfRow(bview, refresh_row); - } else { - refresh_row = old_cursor.row()->next(); - refresh_y = old_cursor.y() - old_cursor.row()->baseline(); - - tmpcursor = cursor; - cursor = old_cursor; // that undo can restore the right cursor position - Paragraph * endpar = old_cursor.par()->next(); - if (endpar && endpar->getDepth()) { - while (endpar && endpar->getDepth()) { - endpar = endpar->next(); - } - } - setUndo(bview, Undo::DELETE, old_cursor.par(), endpar); - cursor = tmpcursor; - - // delete old row - removeRow(old_cursor.row()); - // delete old par - if (ownerParagraph() == old_cursor.par()) { - ownerParagraph(ownerParagraph()->next()); - } - - delete old_cursor.par(); - - /* Breakagain the next par. Needed because of - the parindent that can occur or dissappear. - The next row can change its height, if - there is another layout before */ - if (refresh_row) { - breakAgain(bview, refresh_row); - updateCounters(bview); - } - } - - // correct cursor y - setCursorIntern(bview, cursor.par(), cursor.pos()); - - if (selection.cursor.par() == old_cursor.par() - && selection.cursor.pos() == old_cursor.pos()) { - // correct selection - selection.cursor = cursor; - } - } - if (!deleted) { - if (old_cursor.par()->stripLeadingSpaces()) { - redoParagraphs(bview, old_cursor, - old_cursor.par()->next()); - // correct cursor y - setCursorIntern(bview, cursor.par(), cursor.pos()); - selection.cursor = cursor; } } - return deleted; } Index: insets/insettext.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/insets/insettext.C,v retrieving revision 1.354 diff -u -p -r1.354 insettext.C --- insets/insettext.C 12 Mar 2003 11:52:23 -0000 1.354 +++ insets/insettext.C 12 Mar 2003 15:04:28 -0000 @@ -816,6 +816,7 @@ void InsetText::insetUnlock(BufferView * } else bv->owner()->setLayout(bv->text->cursor.par()->layout()->name()); // hack for deleteEmptyParMech +#warning FIXME, DEPM is dead - now what ? if (!paragraphs.begin()->empty()) { lt->setCursor(bv, &*(paragraphs.begin()), 0); } else if (paragraphs.begin()->next()) {