mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-13 17:20:55 +00:00
The big change tracking paragraph patch (bug 880)
from Martin Vermeer <martin.vermeer@hut.fi>: * text.C (backspace): Fix changebar non-update * CutAndPaste.C (pasteSelectionHelper): comments * paragraph_funcs.C (mergeParagraph): fix Juergen's cut&paste bug * changes.h: comments * paragraph.C (stripLeadingSpaces): remove unnecessary setChange * text.C (backspace): allow deletion of inserted para break Change tracking -related bug fixes (reported by Juergen) and some documentation work * rowpainter.C (paintChangeBar): fix painting of change bar with only paragraph break changed * paragraph.[Ch] (write, lookupChange, lookupChangeFull; added setChangeFull): * paragraph_pimpl.[Ch] (trackChanges, cleanChanges, acceptChange, rejectChange, erase; added setChangeFull): * CutAndPaste.C (eraseSelectionHelper): * lyxtext.h: * text.C (readParToken, readParagraph, breakParagraph, acceptChange, rejectChange, backspace, currentState; added backspacePos0): * paragraph_funcs.C (breakParagraphConservative, mergeParagraph): * lyxfind.C (findChange, findNextChange): fix bug 880: Change tracked paragraphs should still allow a paragraph split (and related things, i.e., multi-paragraph change tracking) git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/branches/BRANCH_1_4_X@13486 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
2e7158c61b
commit
3f2c546936
@ -1,3 +1,38 @@
|
||||
2006-03-18 Martin Vermeer <martin.vermeer@hut.fi>
|
||||
|
||||
* text.C (backspace): Fix changebar non-update
|
||||
|
||||
2006-03-15 Martin Vermeer <martin.vermeer@hut.fi>
|
||||
|
||||
* CutAndPaste.C (pasteSelectionHelper): comments
|
||||
* paragraph_funcs.C (mergeParagraph): fix Juergen's cut&paste bug
|
||||
* changes.h: comments
|
||||
* paragraph.C (stripLeadingSpaces): remove unnecessary setChange
|
||||
* text.C (backspace): allow deletion of inserted para break
|
||||
Change tracking -related bug fixes (reported by Juergen) and
|
||||
some documentation work
|
||||
|
||||
2006-03-13 Martin Vermeer <martin.vermeer@hut.fi>
|
||||
|
||||
* rowpainter.C (paintChangeBar): fix painting of change bar with
|
||||
only paragraph break changed
|
||||
|
||||
2006-03-11 Martin Vermeer <martin.vermeer@hut.fi>
|
||||
|
||||
* paragraph.[Ch] (write, lookupChange, lookupChangeFull;
|
||||
added setChangeFull):
|
||||
* paragraph_pimpl.[Ch] (trackChanges, cleanChanges, acceptChange,
|
||||
rejectChange, erase; added setChangeFull):
|
||||
* CutAndPaste.C (eraseSelectionHelper):
|
||||
* lyxtext.h:
|
||||
* text.C (readParToken, readParagraph, breakParagraph,
|
||||
acceptChange, rejectChange, backspace, currentState;
|
||||
added backspacePos0):
|
||||
* paragraph_funcs.C (breakParagraphConservative, mergeParagraph):
|
||||
* lyxfind.C (findChange, findNextChange): fix bug 880: Change
|
||||
tracked paragraphs should still allow a paragraph split (and related
|
||||
things, i.e., multi-paragraph change tracking)
|
||||
|
||||
2006-02-20 Jean-Marc Lasgouttes <lasgouttes@lyx.org>
|
||||
|
||||
* cursor.C (bruteFind): only iterate over the paragraphs that are
|
||||
|
@ -234,7 +234,8 @@ pasteSelectionHelper(Buffer const & buffer,
|
||||
pit = last_paste;
|
||||
pos = pars[last_paste].size();
|
||||
|
||||
// Maybe some pasting.
|
||||
// Join (conditionally) last pasted paragraph with next one, i.e.,
|
||||
// the tail of the spliced document paragraph
|
||||
if (!empty && last_paste + 1 != pit_type(pars.size())) {
|
||||
if (pars[last_paste + 1].hasSameLayout(pars[last_paste])) {
|
||||
mergeParagraph(buffer.params(), pars, last_paste);
|
||||
@ -271,64 +272,35 @@ PitPosPair eraseSelectionHelper(BufferParams const & params,
|
||||
return PitPosPair(endpit, endpos);
|
||||
}
|
||||
|
||||
bool all_erased = true;
|
||||
|
||||
// Clear fragments of the first par in selection
|
||||
pars[startpit].erase(startpos, pars[startpit].size());
|
||||
if (pars[startpit].size() != startpos)
|
||||
all_erased = false;
|
||||
|
||||
// Clear fragments of the last par in selection
|
||||
endpos -= pars[endpit].erase(0, endpos);
|
||||
if (endpos != 0)
|
||||
all_erased = false;
|
||||
|
||||
// Erase all the "middle" paragraphs.
|
||||
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());
|
||||
if (pars[pit].empty()) {
|
||||
// remove the par if it's now empty
|
||||
pars.erase(pars.begin() + pit);
|
||||
--endpit;
|
||||
} else {
|
||||
++pit;
|
||||
all_erased = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pars.erase(pars.begin() + startpit + 1, pars.begin() + endpit);
|
||||
endpit = startpit + 1;
|
||||
}
|
||||
|
||||
#if 0 // FIXME: why for cut but not copy ?
|
||||
// the cut selection should begin with standard layout
|
||||
if (realcut) {
|
||||
buf->params().clear();
|
||||
buf->bibkey = 0;
|
||||
buf->layout(textclasslist[buffer->params.textclass].defaultLayoutName());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (startpit + 1 == pit_type(pars.size()))
|
||||
return PitPosPair(endpit, endpos);
|
||||
|
||||
if (doclear) {
|
||||
pars[startpit + 1].stripLeadingSpaces();
|
||||
}
|
||||
|
||||
// Merge first and last paragraph, if possible
|
||||
if (all_erased &&
|
||||
(pars[startpit].hasSameLayout(pars[startpit + 1]) ||
|
||||
pars[startpit + 1].empty())) {
|
||||
mergeParagraph(params, pars, startpit);
|
||||
// This because endpar gets deleted here!
|
||||
endpit = startpit;
|
||||
endpos = startpos;
|
||||
// A paragraph break has to be physically removed by merging, but
|
||||
// only if either (1) change tracking is off, or (2) the para break
|
||||
// is "blue"
|
||||
for (pit_type pit = startpit; pit != endpit + 1;) {
|
||||
bool const merge = !params.tracking_changes ||
|
||||
pars[pit].lookupChange(pars[pit].size()) ==
|
||||
Change::INSERTED;
|
||||
pos_type const left = ( pit == startpit ? startpos : 0 );
|
||||
pos_type const right = ( pit == endpit ? endpos :
|
||||
pars[pit].size() + 1 );
|
||||
// Logical erase only:
|
||||
pars[pit].erase(left, right);
|
||||
// Separate handling of para break:
|
||||
if (merge && pit != endpit &&
|
||||
pars[pit].hasSameLayout(pars[pit + 1])) {
|
||||
pos_type const thissize = pars[pit].size();
|
||||
if (doclear)
|
||||
pars[pit + 1].stripLeadingSpaces();
|
||||
mergeParagraph(params, pars, pit);
|
||||
--endpit;
|
||||
if (pit == endpit)
|
||||
endpos += thissize;
|
||||
} else
|
||||
++pit;
|
||||
}
|
||||
|
||||
// Ensure legal cursor pos:
|
||||
endpit = startpit;
|
||||
endpos = startpos;
|
||||
return PitPosPair(endpit, endpos);
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,8 @@ public:
|
||||
/// return true if there is a deleted or unchanged range contained
|
||||
bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
|
||||
|
||||
/// remove the given entry
|
||||
/// remove the given entry. This implies that a character was
|
||||
/// deleted at pos, and will adjust all range bounds past it
|
||||
void erase(lyx::pos_type pos);
|
||||
|
||||
/// output latex to mark a transition between two changetypes
|
||||
@ -134,22 +135,23 @@ private:
|
||||
|
||||
typedef std::vector<ChangeRange> ChangeTable;
|
||||
|
||||
/// our table of changes
|
||||
/// our table of changes, every row a range and change descriptor
|
||||
ChangeTable table_;
|
||||
|
||||
/// change type for an empty paragraph
|
||||
Change::Type empty_type_;
|
||||
|
||||
/// handle a delete
|
||||
/// handle a delete, either logical or physical (see erase)
|
||||
void del(Change change, ChangeTable::size_type pos);
|
||||
|
||||
/// handle an add
|
||||
/// handle an add, adjusting range bounds past it
|
||||
void add(Change change, ChangeTable::size_type pos);
|
||||
|
||||
/// merge neighbouring ranges
|
||||
/// merge neighbouring ranges, assuming that they are abutting
|
||||
/// (as done by set())
|
||||
void merge();
|
||||
|
||||
/// consistency check
|
||||
/// consistency check, needed before merge()
|
||||
void check() const;
|
||||
|
||||
};
|
||||
|
@ -127,9 +127,8 @@ bool findBackwards(DocIterator & cur, MatchString const & match)
|
||||
|
||||
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 @@ bool findNextChange(BufferView * bv)
|
||||
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();
|
||||
|
@ -220,9 +220,14 @@ public:
|
||||
bool cursorTop(LCursor & cur);
|
||||
///
|
||||
bool cursorBottom(LCursor & cur);
|
||||
///
|
||||
/// Delete character at cursor. Honour change tracking
|
||||
bool Delete(LCursor & cur);
|
||||
///
|
||||
/** At cursor position 0, merge paragraph with the one before it.
|
||||
* Ignore CT (this is used in \c acceptChange, \c rejectChange for
|
||||
* physical deletion of paragraph break)
|
||||
*/
|
||||
bool backspacePos0(LCursor & cur);
|
||||
/// Delete character before cursor. Honour CT
|
||||
bool backspace(LCursor & cur);
|
||||
///
|
||||
bool selectWordWhenUnderCursor(LCursor & cur, lyx::word_location);
|
||||
|
@ -159,12 +159,15 @@ void Paragraph::write(Buffer const & buf, ostream & os,
|
||||
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 @@ void Paragraph::write(Buffer const & buf, ostream & os,
|
||||
}
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
@ -569,9 +563,8 @@ int Paragraph::stripLeadingSpaces()
|
||||
return 0;
|
||||
|
||||
int i = 0;
|
||||
while (!empty() && (isNewline(0) || isLineSeparator(0))) {
|
||||
// Set Change::Type to Change::INSERTED to quietly remove it
|
||||
setChange(0, Change::INSERTED);
|
||||
while (!empty() && (isNewline(0) || isLineSeparator(0))
|
||||
&& (lookupChange(0) != Change::DELETED)) {
|
||||
erase(0);
|
||||
++i;
|
||||
}
|
||||
@ -1639,14 +1632,14 @@ void Paragraph::cleanChanges()
|
||||
|
||||
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 +1662,12 @@ void Paragraph::setChange(lyx::pos_type pos, Change::Type type)
|
||||
}
|
||||
|
||||
|
||||
void Paragraph::setChangeFull(lyx::pos_type pos, Change change)
|
||||
{
|
||||
pimpl_->setChangeFull(pos, change);
|
||||
}
|
||||
|
||||
|
||||
void Paragraph::markErased(bool erased)
|
||||
{
|
||||
pimpl_->markErased(erased);
|
||||
|
@ -224,6 +224,9 @@ public:
|
||||
|
||||
/// 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);
|
||||
|
@ -213,6 +213,9 @@ void breakParagraphConservative(BufferParams const & bparams,
|
||||
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,28 @@ void mergeParagraph(BufferParams const & bparams,
|
||||
pos_type pos_end = next.size() - 1;
|
||||
pos_type pos_insert = par.size();
|
||||
|
||||
// What happens is the following. Later on, moveItem() will copy
|
||||
// over characters from the next paragraph to be inserted into this
|
||||
// position. Now, if the first char to be so copied is "red" (i.e.,
|
||||
// marked deleted) and the paragraph break is marked "blue",
|
||||
// insertChar will trigger (eventually, through record(), and see
|
||||
// del() and erase() in changes.C) a "hard" character deletion.
|
||||
// Which doesn't make sense of course at this pos, but the effect is
|
||||
// to shorten the change range to which this para break belongs, by
|
||||
// one. It will (should) remain "orphaned", having no CT info to it,
|
||||
// and check() in changes.C will assert. Setting the para break
|
||||
// forcibly to "black" prevents this scenario. -- MV 13.3.2006
|
||||
par.setChange(par.size(), Change::UNCHANGED);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ void Paragraph::Pimpl::trackChanges(Change::Type type)
|
||||
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 @@ void Paragraph::Pimpl::cleanChanges()
|
||||
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::setChange(pos_type pos, Change::Type type)
|
||||
}
|
||||
|
||||
|
||||
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 @@ void Paragraph::Pimpl::acceptChange(pos_type start, pos_type end)
|
||||
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 @@ void Paragraph::Pimpl::rejectChange(pos_type start, pos_type end)
|
||||
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 @@ void Paragraph::Pimpl::eraseIntern(pos_type pos)
|
||||
|
||||
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 @@ bool Paragraph::Pimpl::erase(pos_type pos)
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,6 +54,8 @@ public:
|
||||
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
|
||||
|
@ -347,7 +347,7 @@ void RowPainter::paintChangeBar()
|
||||
pos_type const start = row_.pos();
|
||||
pos_type const end = row_.endpos();
|
||||
|
||||
if (start == end || !par_.isChanged(start, end - 1))
|
||||
if (start == end || !par_.isChanged(start, end))
|
||||
return;
|
||||
|
||||
int const height = text_.isLastRow(pit_, row_)
|
||||
|
245
src/text.C
245
src/text.C
@ -315,8 +315,10 @@ void readParToken(Buffer const & buf, Paragraph & par, LyXLex & lex,
|
||||
} 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 @@ void readParagraph(Buffer const & buf, Paragraph & par, LyXLex & lex)
|
||||
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 @@ namespace {
|
||||
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 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
|
||||
|
||||
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 @@ void LyXText::acceptChange(LCursor & cur)
|
||||
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 @@ void LyXText::rejectChange(LCursor & cur)
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -1560,6 +1598,80 @@ bool LyXText::Delete(LCursor & cur)
|
||||
}
|
||||
|
||||
|
||||
bool LyXText::backspacePos0(LCursor & cur)
|
||||
{
|
||||
BOOST_ASSERT(this == cur.text());
|
||||
bool needsUpdate = 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.
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 (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);
|
||||
}
|
||||
return needsUpdate;
|
||||
}
|
||||
|
||||
|
||||
bool LyXText::backspace(LCursor & cur)
|
||||
{
|
||||
BOOST_ASSERT(this == cur.text());
|
||||
@ -1569,77 +1681,20 @@ bool LyXText::backspace(LCursor & cur)
|
||||
// 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;
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
cursorLeft(cur);
|
||||
if (cur.buffer().params().tracking_changes) {
|
||||
// Previous paragraph, mark "carriage return" as
|
||||
// deleted:
|
||||
Paragraph & par = pars_[cur.pit() - 1];
|
||||
// Take care of a just inserted para break:
|
||||
if (par.lookupChange(par.size()) != Change::INSERTED) {
|
||||
par.setChange(par.size(), Change::DELETED);
|
||||
setCursorIntern(cur, cur.pit() - 1, par.size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur.pit() != 0)
|
||||
recordUndo(cur, Undo::DELETE, cur.pit() - 1);
|
||||
needsUpdate = backspacePos0(cur);
|
||||
|
||||
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();
|
||||
|
||||
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))
|
||||
--cur.pos();
|
||||
|
||||
// the counters may have changed
|
||||
updateCounters(cur.buffer());
|
||||
setCursor(cur, cur.pit(), cur.pos(), false);
|
||||
}
|
||||
} else {
|
||||
// this is the code for a normal backspace, not pasting
|
||||
// any paragraphs
|
||||
@ -2186,9 +2241,11 @@ string LyXText::currentState(LCursor & cur)
|
||||
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);
|
||||
|
@ -47,6 +47,9 @@ What's new
|
||||
|
||||
* User Interface:
|
||||
|
||||
- Enable breaking and merging of paragraphs in change tracking mode
|
||||
(bug 880).
|
||||
|
||||
- Convert line endings for external copy/paste on OS X (bug 1955).
|
||||
|
||||
- Disable saving when document is unchanged (bug 2313)
|
||||
|
Loading…
Reference in New Issue
Block a user