On Tue, 2006-03-07 at 14:06 +0100, Jean-Marc Lasgouttes wrote:
> >>>>> "Martin" == Martin Vermeer <[EMAIL PROTECTED]> writes:
> 
> Martin> Could you please re-list them? It isn't clear to me which ones
> Martin> you are referring to here.
> 
> I have to leave now, but it would be something like 880, 2195, 2207,
> 2217 and maybe 2251.


Attached the change tracking patch (multi-paragraph i.e. 880) but fixes
2212 (skip over first change) as well.

Lars, others: OK to commit to trunk?

- Martin

Index: paragraph.h
===================================================================
--- paragraph.h	(revision 13276)
+++ paragraph.h	(working copy)
@@ -224,6 +224,9 @@
 
 	/// set change at pos
 	void setChange(lyx::pos_type pos, Change::Type type);
+	
+	/// set full change at pos
+	void setChangeFull(lyx::pos_type pos, Change change);
 
 	/// accept change
 	void acceptChange(lyx::pos_type start, lyx::pos_type end);
Index: BufferView_pimpl.C
===================================================================
--- BufferView_pimpl.C	(revision 13276)
+++ BufferView_pimpl.C	(working copy)
@@ -907,12 +907,7 @@
 		buffer_->undostack().clear();
 	} else {
 		cursor_.setCursor(doc_iterator_begin(buffer_->inset()));
-		bool const found = lyx::find::findNextChange(bv_);
-		if (found) {
-			// We reset the cursor to the start of the
-			// document, since the Changes Dialog is going
-			// to search for the next change anyway.
-			cursor_.setCursor(doc_iterator_begin(buffer_->inset()));
+		if (lyx::find::findNextChange(bv_)) {
 			owner_->getDialogs().show("changes");
 			return;
 		}
@@ -1213,7 +1208,8 @@
 	}
 
 	case LFUN_MERGE_CHANGES:
-		owner_->getDialogs().show("changes");
+		if (lyx::find::findNextChange(bv_))
+			owner_->getDialogs().show("changes");
 		break;
 
 	case LFUN_ACCEPT_ALL_CHANGES: {
Index: paragraph_pimpl.C
===================================================================
--- paragraph_pimpl.C	(revision 13276)
+++ paragraph_pimpl.C	(working copy)
@@ -99,7 +99,7 @@
 	lyxerr[Debug::CHANGES] << "track changes for par "
 		<< id_ << " type " << type << endl;
 	changes_.reset(new Changes(type));
-	changes_->set(type, 0, size());
+	changes_->set(type, 0, size() + 1);
 }
 
 
@@ -116,7 +116,7 @@
 		return;
 
 	changes_.reset(new Changes(Change::INSERTED));
-	changes_->set(Change::INSERTED, 0, size());
+	changes_->set(Change::INSERTED, 0, size() + 1);
 }
 
 
@@ -147,6 +147,14 @@
 }
 
 
+void Paragraph::Pimpl::setChangeFull(pos_type pos, Change change)
+{
+	if (!tracking())
+		return;
+
+	changes_->set(change, pos);
+}
+
 Change::Type Paragraph::Pimpl::lookupChange(pos_type pos) const
 {
 	if (!tracking())
@@ -204,10 +212,14 @@
 				break;
 
 			case Change::DELETED:
-				eraseIntern(i);
-				changes_->erase(i);
-				--end;
-				--i;
+				// Suppress access to nonexistent 
+				// "end-of-paragraph char":
+				if (i < size()) {
+					eraseIntern(i);
+					changes_->erase(i);
+					--end;
+					--i;
+				}
 				break;
 		}
 	}
@@ -235,15 +247,18 @@
 				break;
 
 			case Change::INSERTED:
-				eraseIntern(i);
-				changes_->erase(i);
-				--end;
-				--i;
+				if (i < size()) {
+					eraseIntern(i);
+					changes_->erase(i);
+					--end;
+					--i;
+				}
 				break;
 
 			case Change::DELETED:
 				changes_->set(Change::UNCHANGED, i);
-				if (owner_->isInset(i))
+				// No real char at position size():
+				if (i < size() && owner_->isInset(i))
 					owner_->getInset(i)->markErased(false);
 				break;
 		}
@@ -351,7 +366,7 @@
 
 bool Paragraph::Pimpl::erase(pos_type pos)
 {
-	BOOST_ASSERT(pos < size());
+	BOOST_ASSERT(pos <= size());
 
 	if (tracking()) {
 		Change::Type changetype(changes_->lookup(pos));
@@ -359,14 +374,19 @@
 
 		// only allow the actual removal if it was /new/ text
 		if (changetype != Change::INSERTED) {
-			if (owner_->isInset(pos))
+			if (pos < size() && owner_->isInset(pos))
 				owner_->getInset(pos)->markErased(true);
 			return false;
 		}
 	}
 
-	eraseIntern(pos);
-	return true;
+	// Don't physically access nonexistent end-of-paragraph char
+	if (pos < size()) {
+		eraseIntern(pos);
+		return true;
+	}
+
+	return false;
 }
 
 
Index: paragraph_pimpl.h
===================================================================
--- paragraph_pimpl.h	(revision 13276)
+++ paragraph_pimpl.h	(working copy)
@@ -54,6 +54,8 @@
 	bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
 	/// set change at pos
 	void setChange(lyx::pos_type pos, Change::Type type);
+	/// set full change at pos
+	void setChangeFull(lyx::pos_type pos, Change change);
 	/// mark as erased
 	void markErased(bool);
 	/// accept change
Index: CutAndPaste.C
===================================================================
--- CutAndPaste.C	(revision 13276)
+++ CutAndPaste.C	(working copy)
@@ -274,7 +274,7 @@
 	bool all_erased = true;
 
         // Clear fragments of the first par in selection
-	pars[startpit].erase(startpos, pars[startpit].size());
+	pars[startpit].erase(startpos, pars[startpit].size() + 1);
 	if (pars[startpit].size() != startpos)
 		all_erased = false;
 
@@ -287,8 +287,9 @@
 	if (params.tracking_changes) {
 		// Look through the deleted pars if any, erasing as needed
 		for (pit_type pit = startpit + 1; pit != endpit;) {
-			// "erase" the contents of the par
-			pars[pit].erase(0, pars[pit].size());
+			// "erase" the contents of the par, up to and
+			// including (symbolically) end-of-par position
+			pars[pit].erase(0, pars[pit].size() + 1);
 			if (pars[pit].empty()) {
 				// remove the par if it's now empty
 				pars.erase(pars.begin() + pit);
Index: lyxtext.h
===================================================================
--- lyxtext.h	(revision 13276)
+++ lyxtext.h	(working copy)
@@ -223,6 +223,8 @@
 	///
 	bool Delete(LCursor & cur);
 	///
+	bool backspacePos0(LCursor & cur);
+	///
 	bool backspace(LCursor & cur);
 	///
 	bool selectWordWhenUnderCursor(LCursor & cur, lyx::word_location);
Index: frontends/gtk/GChanges.C
===================================================================
--- frontends/gtk/GChanges.C	(revision 13276)
+++ frontends/gtk/GChanges.C	(working copy)
@@ -63,7 +63,10 @@
 
 void GChanges::update()
 {
-	onNext();
+	if (controller().changed())
+		promptChange();
+	else
+		promptDismiss();
 }
 
 
Index: frontends/qt2/QChanges.C
===================================================================
--- frontends/qt2/QChanges.C	(revision 13276)
+++ frontends/qt2/QChanges.C	(working copy)
@@ -50,14 +50,6 @@
 
 void QChanges::update_contents()
 {
-	next();
-}
-
-
-void QChanges::next()
-{
-	controller().find();
-
 	string text;
 	string author(controller().getChangeAuthor());
 	string date(controller().getChangeDate());
@@ -71,6 +63,13 @@
 }
 
 
+void QChanges::next()
+{
+	controller().find();
+	update_contents();
+}
+
+
 void QChanges::accept()
 {
 	controller().accept();
Index: frontends/xforms/FormChanges.C
===================================================================
--- frontends/xforms/FormChanges.C	(revision 13276)
+++ frontends/xforms/FormChanges.C	(working copy)
@@ -48,35 +48,32 @@
 
 void FormChanges::update()
 {
-	input(dialog_->button_next, 0);
+	if (!dialog_.get()) return;
+	bool exist = controller().changed();
+	setEnabled(dialog_->button_accept, exist);
+	setEnabled(dialog_->button_reject, exist);
+	setEnabled(dialog_->button_next, exist);
+
+	string const author = exist ? controller().getChangeAuthor() : "";
+	fl_set_object_label(dialog_->text_author, author.c_str());
+
+	string const date = exist ? controller().getChangeDate() : "";
+	fl_set_object_label(dialog_->text_date, date.c_str());
+
+	// Yes, this is needed.
+	fl_redraw_form(form());
 }
 
 
 ButtonPolicy::SMInput FormChanges::input(FL_OBJECT * obj, long)
 {
-	if (obj == dialog_->button_accept) {
+	if (obj == dialog_->button_accept)
 		controller().accept();
-
-	} else if (obj == dialog_->button_reject) {
+	else if (obj == dialog_->button_reject)
 		controller().reject();
-
-	} else if (obj == dialog_->button_next) {
-
-		bool const exist = controller().find();
-		setEnabled(dialog_->button_accept, exist);
-		setEnabled(dialog_->button_reject, exist);
-		setEnabled(dialog_->button_next, exist);
-
-		string const author = exist ? controller().getChangeAuthor() : "";
-		fl_set_object_label(dialog_->text_author, author.c_str());
-
-		string const date = exist ? controller().getChangeDate() : "";
-		fl_set_object_label(dialog_->text_date, date.c_str());
-
-		// Yes, this is needed.
-		fl_redraw_form(form());
-	}
-
+	else if (obj == dialog_->button_next)
+		controller().find();
+	update();
 	return ButtonPolicy::SMI_VALID;
 }
 
Index: frontends/controllers/ControlChanges.C
===================================================================
--- frontends/controllers/ControlChanges.C	(revision 13276)
+++ frontends/controllers/ControlChanges.C	(working copy)
@@ -39,6 +39,13 @@
 }
 
 
+bool ControlChanges::changed()
+{
+	Change c(kernel().bufferview()->getCurrentChange());
+	return c.type != Change::UNCHANGED;
+}
+
+
 string const ControlChanges::getChangeDate()
 {
 	Change c(kernel().bufferview()->getCurrentChange());
Index: frontends/controllers/ControlChanges.h
===================================================================
--- frontends/controllers/ControlChanges.h	(revision 13276)
+++ frontends/controllers/ControlChanges.h	(working copy)
@@ -38,6 +38,9 @@
 	/// find the next merge chunk and highlight it
 	bool find();
 
+	/// Are there changes to be merged at current location?
+	bool changed();
+	
 	/// return date of change
 	std::string const getChangeDate();
 
Index: paragraph_funcs.C
===================================================================
--- paragraph_funcs.C	(revision 13276)
+++ paragraph_funcs.C	(working copy)
@@ -213,6 +213,9 @@
 			if (moveItem(par, tmp, bparams, i, j - pos, change))
 				++j;
 		}
+		// Move over end-of-par change attr
+		tmp.setChange(tmp.size(), par.lookupChange(par.size()));
+
 		// If tracking changes, set all the text that is to be
 		// erased to Type::INSERTED.
 		for (pos_type k = pos_end; k >= pos; --k) {
@@ -233,12 +236,15 @@
 	pos_type pos_end = next.size() - 1;
 	pos_type pos_insert = par.size();
 
+	Change::Type cr = next.lookupChange(next.size());
 	// ok, now copy the paragraph
 	for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
 		Change::Type change = next.lookupChange(i);
 		if (moveItem(next, par, bparams, i, pos_insert + j, change))
 			++j;
 	}
+	// Move the change status of "carriage return" over
+	par.setChange(par.size(), cr);
 
 	pars.erase(pars.begin() + par_offset + 1);
 }
Index: lyxfind.C
===================================================================
--- lyxfind.C	(revision 13276)
+++ lyxfind.C	(working copy)
@@ -127,9 +127,8 @@
 
 bool findChange(DocIterator & cur)
 {
-	for (; cur; cur.forwardChar())
-		if (cur.inTexted() && cur.pos() != cur.paragraph().size() &&
-		    cur.paragraph().lookupChange(cur.pos())
+	for (; cur; cur.forwardPos())
+		if (cur.inTexted() && cur.paragraph().lookupChange(cur.pos())
 		    != Change::UNCHANGED)
 			return true;
 	return false;
@@ -344,25 +343,21 @@
 	if (!findChange(cur))
 		return false;
 
-	Paragraph const & par = cur.paragraph();
-	const pos_type pos = cur.pos();
+	bv->cursor().setCursor(cur);
+	bv->cursor().resetAnchor();
+	
+	Change orig_change = cur.paragraph().lookupChangeFull(cur.pos());
 
-	Change orig_change = par.lookupChangeFull(pos);
-	const pos_type parsize = par.size();
-	pos_type end = pos;
-
-	for (; end != parsize; ++end) {
-		Change change = par.lookupChangeFull(end);
+	DocIterator et = doc_iterator_end(cur.inset());
+	for (; cur != et; cur.forwardPosNoDescend()) {
+		Change change = cur.paragraph().lookupChangeFull(cur.pos());
 		if (change != orig_change) {
-			// slight UI optimisation: for replacements, we get
-			// text like : _old_new. Consider that as one change.
-			if (!(orig_change.type == Change::DELETED &&
-				change.type == Change::INSERTED))
-				break;
+			break;
 		}
 	}
-	pos_type length = end - pos;
-	bv->putSelectionAt(cur, length, false);
+	// Now put cursor to end of selection:
+	bv->cursor().setCursor(cur);
+	bv->cursor().setSelection();
 	// if we used a lfun like in find/replace, dispatch would do
 	// that for us
 	bv->update();
Index: paragraph.C
===================================================================
--- paragraph.C	(revision 13276)
+++ paragraph.C	(working copy)
@@ -159,12 +159,15 @@
 	lyx::time_type const curtime(lyx::current_time());
 
 	int column = 0;
-	for (pos_type i = 0; i < size(); ++i) {
+	for (pos_type i = 0; i <= size(); ++i) {
 
 		Change change = pimpl_->lookupChangeFull(i);
 		Changes::lyxMarkChange(os, column, curtime, running_change, change);
 		running_change = change;
 
+		if (i == size())
+			break;
+
 		// Write font changes
 		LyXFont font2 = getFontSettings(bparams, i);
 		if (font2 != font1) {
@@ -223,15 +226,6 @@
 		}
 	}
 
-	// to make reading work properly
-	if (!size()) {
-		running_change = pimpl_->lookupChange(0);
-		Changes::lyxMarkChange(os, column, curtime,
-			Change(Change::UNCHANGED), running_change);
-	}
-	Changes::lyxMarkChange(os, column, curtime,
-		running_change, Change(Change::UNCHANGED));
-
 	os << "\n\\end_layout\n";
 }
 
@@ -1639,14 +1633,14 @@
 
 Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
 {
-	BOOST_ASSERT(empty() || pos < size());
+	BOOST_ASSERT(pos <= size());
 	return pimpl_->lookupChange(pos);
 }
 
 
 Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
 {
-	BOOST_ASSERT(empty() || pos < size());
+	BOOST_ASSERT(pos <= size());
 	return pimpl_->lookupChangeFull(pos);
 }
 
@@ -1669,6 +1663,12 @@
 }
 
 
+void Paragraph::setChangeFull(lyx::pos_type pos, Change change)
+{
+	pimpl_->setChangeFull(pos, change);
+}
+
+
 void Paragraph::markErased(bool erased)
 {
 	pimpl_->markErased(erased);
Index: text.C
===================================================================
--- text.C	(revision 13276)
+++ text.C	(working copy)
@@ -315,8 +315,10 @@
 	} else if (token == "\\change_unchanged") {
 		// Hack ! Needed for empty paragraphs :/
 		// FIXME: is it still ??
+		/*
 		if (!par.size())
 			par.cleanChanges();
+		*/
 		change = Change(Change::UNCHANGED);
 	} else if (token == "\\change_inserted") {
 		lex.eatLine();
@@ -375,6 +377,9 @@
 			break;
 		}
 	}
+	// Final change goes to paragraph break:
+	par.setChangeFull(par.size(), change);
+	
 	// Initialize begin_of_body_ on load; redoParagraph maintains
 	par.setBeginOfBody();
 }
@@ -1026,14 +1031,10 @@
 void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
 {
 	BOOST_ASSERT(this == cur.text());
-	// allow only if at start or end, or all previous is new text
+
 	Paragraph & cpar = cur.paragraph();
 	pit_type cpit = cur.pit();
 
-	if (cur.pos() != 0 && cur.pos() != cur.lastpos()
-	    && cpar.isChangeEdited(0, cur.pos()))
-		return;
-
 	LyXTextClass const & tclass = cur.buffer().params().getLyXTextClass();
 	LyXLayout_ptr const & layout = cpar.layout();
 
@@ -1088,6 +1089,12 @@
 
 	updateCounters(cur.buffer());
 
+	// Mark "carriage return" as inserted if change tracking:
+	if (cur.buffer().params().tracking_changes) {
+		cur.paragraph().setChange(cur.paragraph().size(), 
+			Change::INSERTED);
+	}
+
 	// This check is necessary. Otherwise the new empty paragraph will
 	// be deleted automatically. And it is more friendly for the user!
 	if (cur.pos() != 0 || isempty)
@@ -1392,18 +1399,34 @@
 	if (!cur.selection() && cur.lastpos() != 0)
 		return;
 
-	CursorSlice const & startc = cur.selBegin();
-	CursorSlice const & endc = cur.selEnd();
-	if (startc.pit() == endc.pit()) {
-		recordUndoSelection(cur, Undo::INSERT);
-		pars_[startc.pit()].acceptChange(startc.pos(), endc.pos());
-		finishUndo();
-		cur.clearSelection();
-		setCursorIntern(cur, startc.pit(), 0);
+	recordUndoSelection(cur, Undo::INSERT);
+	
+	DocIterator it = cur.selectionBegin();
+	DocIterator et = cur.selectionEnd();
+	pit_type pit = it.pit();
+	Change::Type const type = pars_[pit].lookupChange(it.pos());
+	for (; pit <= et.pit(); ++pit) {
+		pos_type left  = ( pit == it.pit() ? it.pos() : 0 );
+		pos_type right = 
+		    ( pit == et.pit() ? et.pos() : pars_[pit].size() + 1 );
+		pars_[pit].acceptChange(left, right);
 	}
-#ifdef WITH_WARNINGS
-#warning handle multi par selection
-#endif
+	if (type == Change::DELETED) {
+		ParagraphList & plist = paragraphs();
+		if (it.pit() + 1 < et.pit())
+			pars_.erase(plist.begin() + it.pit() + 1,
+				    plist.begin() + et.pit());
+		
+		// Paragraph merge if appropriate:
+		if (pars_[it.pit()].lookupChange(pars_[it.pit()].size()) 
+			== Change::DELETED) {
+			setCursorIntern(cur, it.pit() + 1, 0);
+			backspacePos0(cur);
+		}
+	}
+	finishUndo();
+	cur.clearSelection();
+	setCursorIntern(cur, it.pit(), 0);
 }
 
 
@@ -1413,18 +1436,33 @@
 	if (!cur.selection() && cur.lastpos() != 0)
 		return;
 
-	CursorSlice const & startc = cur.selBegin();
-	CursorSlice const & endc = cur.selEnd();
-	if (startc.pit() == endc.pit()) {
-		recordUndoSelection(cur, Undo::INSERT);
-		pars_[startc.pit()].rejectChange(startc.pos(), endc.pos());
-		finishUndo();
-		cur.clearSelection();
-		setCursorIntern(cur, startc.pit(), 0);
+	recordUndoSelection(cur, Undo::INSERT);
+
+	DocIterator it = cur.selectionBegin();
+	DocIterator et = cur.selectionEnd();
+	pit_type pit = it.pit();
+	Change::Type const type = pars_[pit].lookupChange(it.pos());
+	for (; pit <= et.pit(); ++pit) {
+		pos_type left  = ( pit == it.pit() ? it.pos() : 0 );
+		pos_type right = 
+		    ( pit == et.pit() ? et.pos() : pars_[pit].size() + 1 );
+		pars_[pit].rejectChange(left, right);
 	}
-#ifdef WITH_WARNINGS
-#warning handle multi par selection
-#endif
+	if (type == Change::INSERTED) {
+		ParagraphList & plist = paragraphs();
+		if (it.pit() + 1 < et.pit())
+			pars_.erase(plist.begin() + it.pit() + 1,
+				    plist.begin() + et.pit());
+		// Paragraph merge if appropriate:
+		if (pars_[it.pit()].lookupChange(pars_[it.pit()].size()) 
+			== Change::INSERTED) {
+			setCursorIntern(cur, it.pit() + 1, 0);
+			backspacePos0(cur);
+		}
+	}
+	finishUndo();
+	cur.clearSelection();
+	setCursorIntern(cur, it.pit(), 0);
 }
 
 
@@ -1556,86 +1594,100 @@
 }
 
 
-bool LyXText::backspace(LCursor & cur)
+bool LyXText::backspacePos0(LCursor & cur)
 {
 	BOOST_ASSERT(this == cur.text());
 	bool needsUpdate = false;
-	if (cur.pos() == 0) {
-		// The cursor is at the beginning of a paragraph, so
-		// the the backspace will collapse two paragraphs into
-		// one.
 
-		// but it's not allowed unless it's new
-		Paragraph & par = cur.paragraph();
-		if (par.isChangeEdited(0, par.size()))
-			return false;
+	Paragraph & par = cur.paragraph();
+	// is it an empty paragraph?
+	pos_type lastpos = cur.lastpos();
+	if (lastpos == 0 || (lastpos == 1 && 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.
 
-		// we may paste some paragraphs
-
-		// is it an empty paragraph?
-		pos_type lastpos = cur.lastpos();
-		if (lastpos == 0 || (lastpos == 1 && 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 (cur.pit() != 0) {
-                                // For KeepEmpty layouts we need to get
-                                // rid of the keepEmpty setting first.
-                                // And the only way to do this is to
-                                // reset the layout to something
-                                // else: f.ex. the default layout.
-                                if (par.allowEmpty()) {
-                                        Buffer & buf = cur.buffer();
-                                        BufferParams const & bparams = buf.params();
-                                        par.layout(bparams.getLyXTextClass().defaultLayout());
-                                }
+		if (cur.pit() != 0) {
+			// For KeepEmpty layouts we need to get
+			// rid of the keepEmpty setting first.
+			// And the only way to do this is to
+			// reset the layout to something
+			// else: f.ex. the default layout.
+			if (par.allowEmpty()) {
+				Buffer & buf = cur.buffer();
+				BufferParams const & bparams = buf.params();
+				par.layout(bparams.getLyXTextClass().defaultLayout());
+			}
                                 
-				cursorLeft(cur);
-				return true;
-			}
+			cursorLeft(cur);
+			return true;
 		}
+	}
 
-		if (cur.pit() != 0)
-			recordUndo(cur, Undo::DELETE, cur.pit() - 1);
+	if (cur.pit() != 0)
+		recordUndo(cur, Undo::DELETE, cur.pit() - 1);
 
-		pit_type tmppit = cur.pit();
-		// 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)
-		if (cur.pit() != 0) {
-			// steps into the above paragraph.
-			setCursorIntern(cur, cur.pit() - 1,
-					pars_[cur.pit() - 1].size(),
-					false);
-		}
+	pit_type tmppit = cur.pit();
+	// 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)
+	if (cur.pit() != 0) {
+		// steps into the above paragraph.
+		setCursorIntern(cur, cur.pit() - 1,
+				pars_[cur.pit() - 1].size(),
+				false);
+	}
 
-		// Pasting 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.
-		// Correction: Pasting is always allowed with standard-layout
-		// Correction (Jug 20050717): Remove check about alignment!
-		Buffer & buf = cur.buffer();
-		BufferParams const & bufparams = buf.params();
-		LyXTextClass const & tclass = bufparams.getLyXTextClass();
-		pit_type const cpit = cur.pit();
+	// Pasting 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.
+	// Correction: Pasting is always allowed with standard-layout
+	// Correction (Jug 20050717): Remove check about alignment!
+	Buffer & buf = cur.buffer();
+	BufferParams const & bufparams = buf.params();
+	LyXTextClass const & tclass = bufparams.getLyXTextClass();
+	pit_type const cpit = cur.pit();
 
-		if (cpit != tmppit
-		    && (pars_[cpit].layout() == pars_[tmppit].layout()
-		        || pars_[tmppit].layout() == tclass.defaultLayout()))
-		{
-			mergeParagraph(bufparams, pars_, cpit);
-			needsUpdate = true;
+	if (cpit != tmppit
+	    && (pars_[cpit].layout() == pars_[tmppit].layout()
+	        || pars_[tmppit].layout() == tclass.defaultLayout()))
+	{
+		mergeParagraph(bufparams, pars_, cpit);
+		needsUpdate = true;
 
-			if (cur.pos() != 0 && pars_[cpit].isSeparator(cur.pos() - 1))
+		if (cur.pos() != 0 && pars_[cpit].isSeparator(cur.pos() - 1))
 				--cur.pos();
 
-			// the counters may have changed
-			updateCounters(cur.buffer());
-			setCursor(cur, cur.pit(), cur.pos(), false);
+		// the counters may have changed
+		updateCounters(cur.buffer());
+		setCursor(cur, cur.pit(), cur.pos(), false);
+	}
+	return needsUpdate;
+}
+
+
+bool LyXText::backspace(LCursor & cur)
+{
+	BOOST_ASSERT(this == cur.text());
+	bool needsUpdate = false;
+	if (cur.pos() == 0) {
+		// The cursor is at the beginning of a paragraph, so
+		// the the backspace will collapse two paragraphs into
+		// one.
+
+		if (cur.buffer().params().tracking_changes) {
+			// Previous paragraph, mark "carriage return" as
+			// deleted:
+			Paragraph & par = pars_[cur.pit() - 1];
+			par.setChange(par.size(), Change::DELETED);
+			setCursorIntern(cur, cur.pit() - 1, par.size());
+			return false;
 		}
+
+		needsUpdate = backspacePos0(cur);
+
 	} else {
 		// this is the code for a normal backspace, not pasting
 		// any paragraphs
@@ -2182,9 +2238,11 @@
 	std::ostringstream os;
 
 	bool const show_change = buf.params().tracking_changes
-		&& cur.pos() != cur.lastpos()
 		&& par.lookupChange(cur.pos()) != Change::UNCHANGED;
 
+	if (buf.params().tracking_changes)
+		os << "[C] ";
+
 	if (show_change) {
 		Change change = par.lookupChangeFull(cur.pos());
 		Author const & a = buf.params().authors().get(change.author);

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to