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>
|
2006-02-20 Jean-Marc Lasgouttes <lasgouttes@lyx.org>
|
||||||
|
|
||||||
* cursor.C (bruteFind): only iterate over the paragraphs that are
|
* cursor.C (bruteFind): only iterate over the paragraphs that are
|
||||||
|
@ -234,7 +234,8 @@ pasteSelectionHelper(Buffer const & buffer,
|
|||||||
pit = last_paste;
|
pit = last_paste;
|
||||||
pos = pars[last_paste].size();
|
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 (!empty && last_paste + 1 != pit_type(pars.size())) {
|
||||||
if (pars[last_paste + 1].hasSameLayout(pars[last_paste])) {
|
if (pars[last_paste + 1].hasSameLayout(pars[last_paste])) {
|
||||||
mergeParagraph(buffer.params(), pars, last_paste);
|
mergeParagraph(buffer.params(), pars, last_paste);
|
||||||
@ -271,64 +272,35 @@ PitPosPair eraseSelectionHelper(BufferParams const & params,
|
|||||||
return PitPosPair(endpit, endpos);
|
return PitPosPair(endpit, endpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool all_erased = true;
|
// A paragraph break has to be physically removed by merging, but
|
||||||
|
// only if either (1) change tracking is off, or (2) the para break
|
||||||
// Clear fragments of the first par in selection
|
// is "blue"
|
||||||
pars[startpit].erase(startpos, pars[startpit].size());
|
for (pit_type pit = startpit; pit != endpit + 1;) {
|
||||||
if (pars[startpit].size() != startpos)
|
bool const merge = !params.tracking_changes ||
|
||||||
all_erased = false;
|
pars[pit].lookupChange(pars[pit].size()) ==
|
||||||
|
Change::INSERTED;
|
||||||
// Clear fragments of the last par in selection
|
pos_type const left = ( pit == startpit ? startpos : 0 );
|
||||||
endpos -= pars[endpit].erase(0, endpos);
|
pos_type const right = ( pit == endpit ? endpos :
|
||||||
if (endpos != 0)
|
pars[pit].size() + 1 );
|
||||||
all_erased = false;
|
// Logical erase only:
|
||||||
|
pars[pit].erase(left, right);
|
||||||
// Erase all the "middle" paragraphs.
|
// Separate handling of para break:
|
||||||
if (params.tracking_changes) {
|
if (merge && pit != endpit &&
|
||||||
// Look through the deleted pars if any, erasing as needed
|
pars[pit].hasSameLayout(pars[pit + 1])) {
|
||||||
for (pit_type pit = startpit + 1; pit != endpit;) {
|
pos_type const thissize = pars[pit].size();
|
||||||
// "erase" the contents of the par
|
if (doclear)
|
||||||
pars[pit].erase(0, pars[pit].size());
|
pars[pit + 1].stripLeadingSpaces();
|
||||||
if (pars[pit].empty()) {
|
mergeParagraph(params, pars, pit);
|
||||||
// remove the par if it's now empty
|
--endpit;
|
||||||
pars.erase(pars.begin() + pit);
|
if (pit == endpit)
|
||||||
--endpit;
|
endpos += thissize;
|
||||||
} else {
|
} else
|
||||||
++pit;
|
++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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure legal cursor pos:
|
||||||
|
endpit = startpit;
|
||||||
|
endpos = startpos;
|
||||||
return PitPosPair(endpit, endpos);
|
return PitPosPair(endpit, endpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ public:
|
|||||||
/// return true if there is a deleted or unchanged range contained
|
/// return true if there is a deleted or unchanged range contained
|
||||||
bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
|
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);
|
void erase(lyx::pos_type pos);
|
||||||
|
|
||||||
/// output latex to mark a transition between two changetypes
|
/// output latex to mark a transition between two changetypes
|
||||||
@ -134,22 +135,23 @@ private:
|
|||||||
|
|
||||||
typedef std::vector<ChangeRange> ChangeTable;
|
typedef std::vector<ChangeRange> ChangeTable;
|
||||||
|
|
||||||
/// our table of changes
|
/// our table of changes, every row a range and change descriptor
|
||||||
ChangeTable table_;
|
ChangeTable table_;
|
||||||
|
|
||||||
/// change type for an empty paragraph
|
/// change type for an empty paragraph
|
||||||
Change::Type empty_type_;
|
Change::Type empty_type_;
|
||||||
|
|
||||||
/// handle a delete
|
/// handle a delete, either logical or physical (see erase)
|
||||||
void del(Change change, ChangeTable::size_type pos);
|
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);
|
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();
|
void merge();
|
||||||
|
|
||||||
/// consistency check
|
/// consistency check, needed before merge()
|
||||||
void check() const;
|
void check() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -127,9 +127,8 @@ bool findBackwards(DocIterator & cur, MatchString const & match)
|
|||||||
|
|
||||||
bool findChange(DocIterator & cur)
|
bool findChange(DocIterator & cur)
|
||||||
{
|
{
|
||||||
for (; cur; cur.forwardChar())
|
for (; cur; cur.forwardPos())
|
||||||
if (cur.inTexted() && cur.pos() != cur.paragraph().size() &&
|
if (cur.inTexted() && cur.paragraph().lookupChange(cur.pos())
|
||||||
cur.paragraph().lookupChange(cur.pos())
|
|
||||||
!= Change::UNCHANGED)
|
!= Change::UNCHANGED)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
@ -344,25 +343,21 @@ bool findNextChange(BufferView * bv)
|
|||||||
if (!findChange(cur))
|
if (!findChange(cur))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Paragraph const & par = cur.paragraph();
|
bv->cursor().setCursor(cur);
|
||||||
const pos_type pos = cur.pos();
|
bv->cursor().resetAnchor();
|
||||||
|
|
||||||
|
Change orig_change = cur.paragraph().lookupChangeFull(cur.pos());
|
||||||
|
|
||||||
Change orig_change = par.lookupChangeFull(pos);
|
DocIterator et = doc_iterator_end(cur.inset());
|
||||||
const pos_type parsize = par.size();
|
for (; cur != et; cur.forwardPosNoDescend()) {
|
||||||
pos_type end = pos;
|
Change change = cur.paragraph().lookupChangeFull(cur.pos());
|
||||||
|
|
||||||
for (; end != parsize; ++end) {
|
|
||||||
Change change = par.lookupChangeFull(end);
|
|
||||||
if (change != orig_change) {
|
if (change != orig_change) {
|
||||||
// slight UI optimisation: for replacements, we get
|
break;
|
||||||
// text like : _old_new. Consider that as one change.
|
|
||||||
if (!(orig_change.type == Change::DELETED &&
|
|
||||||
change.type == Change::INSERTED))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos_type length = end - pos;
|
// Now put cursor to end of selection:
|
||||||
bv->putSelectionAt(cur, length, false);
|
bv->cursor().setCursor(cur);
|
||||||
|
bv->cursor().setSelection();
|
||||||
// if we used a lfun like in find/replace, dispatch would do
|
// if we used a lfun like in find/replace, dispatch would do
|
||||||
// that for us
|
// that for us
|
||||||
bv->update();
|
bv->update();
|
||||||
|
@ -220,9 +220,14 @@ public:
|
|||||||
bool cursorTop(LCursor & cur);
|
bool cursorTop(LCursor & cur);
|
||||||
///
|
///
|
||||||
bool cursorBottom(LCursor & cur);
|
bool cursorBottom(LCursor & cur);
|
||||||
///
|
/// Delete character at cursor. Honour change tracking
|
||||||
bool Delete(LCursor & cur);
|
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 backspace(LCursor & cur);
|
||||||
///
|
///
|
||||||
bool selectWordWhenUnderCursor(LCursor & cur, lyx::word_location);
|
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());
|
lyx::time_type const curtime(lyx::current_time());
|
||||||
|
|
||||||
int column = 0;
|
int column = 0;
|
||||||
for (pos_type i = 0; i < size(); ++i) {
|
for (pos_type i = 0; i <= size(); ++i) {
|
||||||
|
|
||||||
Change change = pimpl_->lookupChangeFull(i);
|
Change change = pimpl_->lookupChangeFull(i);
|
||||||
Changes::lyxMarkChange(os, column, curtime, running_change, change);
|
Changes::lyxMarkChange(os, column, curtime, running_change, change);
|
||||||
running_change = change;
|
running_change = change;
|
||||||
|
|
||||||
|
if (i == size())
|
||||||
|
break;
|
||||||
|
|
||||||
// Write font changes
|
// Write font changes
|
||||||
LyXFont font2 = getFontSettings(bparams, i);
|
LyXFont font2 = getFontSettings(bparams, i);
|
||||||
if (font2 != font1) {
|
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";
|
os << "\n\\end_layout\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,9 +563,8 @@ int Paragraph::stripLeadingSpaces()
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (!empty() && (isNewline(0) || isLineSeparator(0))) {
|
while (!empty() && (isNewline(0) || isLineSeparator(0))
|
||||||
// Set Change::Type to Change::INSERTED to quietly remove it
|
&& (lookupChange(0) != Change::DELETED)) {
|
||||||
setChange(0, Change::INSERTED);
|
|
||||||
erase(0);
|
erase(0);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@ -1639,14 +1632,14 @@ void Paragraph::cleanChanges()
|
|||||||
|
|
||||||
Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
|
Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(empty() || pos < size());
|
BOOST_ASSERT(pos <= size());
|
||||||
return pimpl_->lookupChange(pos);
|
return pimpl_->lookupChange(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
|
Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(empty() || pos < size());
|
BOOST_ASSERT(pos <= size());
|
||||||
return pimpl_->lookupChangeFull(pos);
|
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)
|
void Paragraph::markErased(bool erased)
|
||||||
{
|
{
|
||||||
pimpl_->markErased(erased);
|
pimpl_->markErased(erased);
|
||||||
|
@ -224,6 +224,9 @@ public:
|
|||||||
|
|
||||||
/// set change at pos
|
/// set change at pos
|
||||||
void setChange(lyx::pos_type pos, Change::Type type);
|
void setChange(lyx::pos_type pos, Change::Type type);
|
||||||
|
|
||||||
|
/// set full change at pos
|
||||||
|
void setChangeFull(lyx::pos_type pos, Change change);
|
||||||
|
|
||||||
/// accept change
|
/// accept change
|
||||||
void acceptChange(lyx::pos_type start, lyx::pos_type end);
|
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))
|
if (moveItem(par, tmp, bparams, i, j - pos, change))
|
||||||
++j;
|
++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
|
// If tracking changes, set all the text that is to be
|
||||||
// erased to Type::INSERTED.
|
// erased to Type::INSERTED.
|
||||||
for (pos_type k = pos_end; k >= pos; --k) {
|
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_end = next.size() - 1;
|
||||||
pos_type pos_insert = par.size();
|
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
|
// ok, now copy the paragraph
|
||||||
for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
|
for (pos_type i = 0, j = 0; i <= pos_end; ++i) {
|
||||||
Change::Type change = next.lookupChange(i);
|
Change::Type change = next.lookupChange(i);
|
||||||
if (moveItem(next, par, bparams, i, pos_insert + j, change))
|
if (moveItem(next, par, bparams, i, pos_insert + j, change))
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
|
// Move the change status of "carriage return" over
|
||||||
|
par.setChange(par.size(), cr);
|
||||||
|
|
||||||
pars.erase(pars.begin() + par_offset + 1);
|
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 "
|
lyxerr[Debug::CHANGES] << "track changes for par "
|
||||||
<< id_ << " type " << type << endl;
|
<< id_ << " type " << type << endl;
|
||||||
changes_.reset(new Changes(type));
|
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;
|
return;
|
||||||
|
|
||||||
changes_.reset(new Changes(Change::INSERTED));
|
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
|
Change::Type Paragraph::Pimpl::lookupChange(pos_type pos) const
|
||||||
{
|
{
|
||||||
if (!tracking())
|
if (!tracking())
|
||||||
@ -204,10 +212,14 @@ void Paragraph::Pimpl::acceptChange(pos_type start, pos_type end)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Change::DELETED:
|
case Change::DELETED:
|
||||||
eraseIntern(i);
|
// Suppress access to nonexistent
|
||||||
changes_->erase(i);
|
// "end-of-paragraph char":
|
||||||
--end;
|
if (i < size()) {
|
||||||
--i;
|
eraseIntern(i);
|
||||||
|
changes_->erase(i);
|
||||||
|
--end;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,15 +247,18 @@ void Paragraph::Pimpl::rejectChange(pos_type start, pos_type end)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Change::INSERTED:
|
case Change::INSERTED:
|
||||||
eraseIntern(i);
|
if (i < size()) {
|
||||||
changes_->erase(i);
|
eraseIntern(i);
|
||||||
--end;
|
changes_->erase(i);
|
||||||
--i;
|
--end;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Change::DELETED:
|
case Change::DELETED:
|
||||||
changes_->set(Change::UNCHANGED, i);
|
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);
|
owner_->getInset(i)->markErased(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -351,7 +366,7 @@ void Paragraph::Pimpl::eraseIntern(pos_type pos)
|
|||||||
|
|
||||||
bool Paragraph::Pimpl::erase(pos_type pos)
|
bool Paragraph::Pimpl::erase(pos_type pos)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(pos < size());
|
BOOST_ASSERT(pos <= size());
|
||||||
|
|
||||||
if (tracking()) {
|
if (tracking()) {
|
||||||
Change::Type changetype(changes_->lookup(pos));
|
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
|
// only allow the actual removal if it was /new/ text
|
||||||
if (changetype != Change::INSERTED) {
|
if (changetype != Change::INSERTED) {
|
||||||
if (owner_->isInset(pos))
|
if (pos < size() && owner_->isInset(pos))
|
||||||
owner_->getInset(pos)->markErased(true);
|
owner_->getInset(pos)->markErased(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eraseIntern(pos);
|
// Don't physically access nonexistent end-of-paragraph char
|
||||||
return true;
|
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;
|
bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
|
||||||
/// set change at pos
|
/// set change at pos
|
||||||
void setChange(lyx::pos_type pos, Change::Type type);
|
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
|
/// mark as erased
|
||||||
void markErased(bool);
|
void markErased(bool);
|
||||||
/// accept change
|
/// accept change
|
||||||
|
@ -347,7 +347,7 @@ void RowPainter::paintChangeBar()
|
|||||||
pos_type const start = row_.pos();
|
pos_type const start = row_.pos();
|
||||||
pos_type const end = row_.endpos();
|
pos_type const end = row_.endpos();
|
||||||
|
|
||||||
if (start == end || !par_.isChanged(start, end - 1))
|
if (start == end || !par_.isChanged(start, end))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int const height = text_.isLastRow(pit_, row_)
|
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") {
|
} else if (token == "\\change_unchanged") {
|
||||||
// Hack ! Needed for empty paragraphs :/
|
// Hack ! Needed for empty paragraphs :/
|
||||||
// FIXME: is it still ??
|
// FIXME: is it still ??
|
||||||
|
/*
|
||||||
if (!par.size())
|
if (!par.size())
|
||||||
par.cleanChanges();
|
par.cleanChanges();
|
||||||
|
*/
|
||||||
change = Change(Change::UNCHANGED);
|
change = Change(Change::UNCHANGED);
|
||||||
} else if (token == "\\change_inserted") {
|
} else if (token == "\\change_inserted") {
|
||||||
lex.eatLine();
|
lex.eatLine();
|
||||||
@ -375,6 +377,9 @@ void readParagraph(Buffer const & buf, Paragraph & par, LyXLex & lex)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Final change goes to paragraph break:
|
||||||
|
par.setChangeFull(par.size(), change);
|
||||||
|
|
||||||
// Initialize begin_of_body_ on load; redoParagraph maintains
|
// Initialize begin_of_body_ on load; redoParagraph maintains
|
||||||
par.setBeginOfBody();
|
par.setBeginOfBody();
|
||||||
}
|
}
|
||||||
@ -1026,14 +1031,10 @@ namespace {
|
|||||||
void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
|
void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(this == cur.text());
|
BOOST_ASSERT(this == cur.text());
|
||||||
// allow only if at start or end, or all previous is new text
|
|
||||||
Paragraph & cpar = cur.paragraph();
|
Paragraph & cpar = cur.paragraph();
|
||||||
pit_type cpit = cur.pit();
|
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();
|
LyXTextClass const & tclass = cur.buffer().params().getLyXTextClass();
|
||||||
LyXLayout_ptr const & layout = cpar.layout();
|
LyXLayout_ptr const & layout = cpar.layout();
|
||||||
|
|
||||||
@ -1088,6 +1089,12 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
|
|||||||
|
|
||||||
updateCounters(cur.buffer());
|
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
|
// This check is necessary. Otherwise the new empty paragraph will
|
||||||
// be deleted automatically. And it is more friendly for the user!
|
// be deleted automatically. And it is more friendly for the user!
|
||||||
if (cur.pos() != 0 || isempty)
|
if (cur.pos() != 0 || isempty)
|
||||||
@ -1392,18 +1399,34 @@ void LyXText::acceptChange(LCursor & cur)
|
|||||||
if (!cur.selection() && cur.lastpos() != 0)
|
if (!cur.selection() && cur.lastpos() != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CursorSlice const & startc = cur.selBegin();
|
recordUndoSelection(cur, Undo::INSERT);
|
||||||
CursorSlice const & endc = cur.selEnd();
|
|
||||||
if (startc.pit() == endc.pit()) {
|
DocIterator it = cur.selectionBegin();
|
||||||
recordUndoSelection(cur, Undo::INSERT);
|
DocIterator et = cur.selectionEnd();
|
||||||
pars_[startc.pit()].acceptChange(startc.pos(), endc.pos());
|
pit_type pit = it.pit();
|
||||||
finishUndo();
|
Change::Type const type = pars_[pit].lookupChange(it.pos());
|
||||||
cur.clearSelection();
|
for (; pit <= et.pit(); ++pit) {
|
||||||
setCursorIntern(cur, startc.pit(), 0);
|
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
|
if (type == Change::DELETED) {
|
||||||
#warning handle multi par selection
|
ParagraphList & plist = paragraphs();
|
||||||
#endif
|
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)
|
if (!cur.selection() && cur.lastpos() != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CursorSlice const & startc = cur.selBegin();
|
recordUndoSelection(cur, Undo::INSERT);
|
||||||
CursorSlice const & endc = cur.selEnd();
|
|
||||||
if (startc.pit() == endc.pit()) {
|
DocIterator it = cur.selectionBegin();
|
||||||
recordUndoSelection(cur, Undo::INSERT);
|
DocIterator et = cur.selectionEnd();
|
||||||
pars_[startc.pit()].rejectChange(startc.pos(), endc.pos());
|
pit_type pit = it.pit();
|
||||||
finishUndo();
|
Change::Type const type = pars_[pit].lookupChange(it.pos());
|
||||||
cur.clearSelection();
|
for (; pit <= et.pit(); ++pit) {
|
||||||
setCursorIntern(cur, startc.pit(), 0);
|
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
|
if (type == Change::INSERTED) {
|
||||||
#warning handle multi par selection
|
ParagraphList & plist = paragraphs();
|
||||||
#endif
|
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)
|
bool LyXText::backspace(LCursor & cur)
|
||||||
{
|
{
|
||||||
BOOST_ASSERT(this == cur.text());
|
BOOST_ASSERT(this == cur.text());
|
||||||
@ -1569,77 +1681,20 @@ bool LyXText::backspace(LCursor & cur)
|
|||||||
// the the backspace will collapse two paragraphs into
|
// the the backspace will collapse two paragraphs into
|
||||||
// one.
|
// one.
|
||||||
|
|
||||||
// but it's not allowed unless it's new
|
if (cur.buffer().params().tracking_changes) {
|
||||||
Paragraph & par = cur.paragraph();
|
// Previous paragraph, mark "carriage return" as
|
||||||
if (par.isChangeEdited(0, par.size()))
|
// deleted:
|
||||||
return false;
|
Paragraph & par = pars_[cur.pit() - 1];
|
||||||
|
// Take care of a just inserted para break:
|
||||||
// we may paste some paragraphs
|
if (par.lookupChange(par.size()) != Change::INSERTED) {
|
||||||
|
par.setChange(par.size(), Change::DELETED);
|
||||||
// is it an empty paragraph?
|
setCursorIntern(cur, cur.pit() - 1, par.size());
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur.pit() != 0)
|
needsUpdate = backspacePos0(cur);
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// this is the code for a normal backspace, not pasting
|
// this is the code for a normal backspace, not pasting
|
||||||
// any paragraphs
|
// any paragraphs
|
||||||
@ -2186,9 +2241,11 @@ string LyXText::currentState(LCursor & cur)
|
|||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
|
||||||
bool const show_change = buf.params().tracking_changes
|
bool const show_change = buf.params().tracking_changes
|
||||||
&& cur.pos() != cur.lastpos()
|
|
||||||
&& par.lookupChange(cur.pos()) != Change::UNCHANGED;
|
&& par.lookupChange(cur.pos()) != Change::UNCHANGED;
|
||||||
|
|
||||||
|
if (buf.params().tracking_changes)
|
||||||
|
os << "[C] ";
|
||||||
|
|
||||||
if (show_change) {
|
if (show_change) {
|
||||||
Change change = par.lookupChangeFull(cur.pos());
|
Change change = par.lookupChangeFull(cur.pos());
|
||||||
Author const & a = buf.params().authors().get(change.author);
|
Author const & a = buf.params().authors().get(change.author);
|
||||||
|
@ -47,6 +47,9 @@ What's new
|
|||||||
|
|
||||||
* User Interface:
|
* 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).
|
- Convert line endings for external copy/paste on OS X (bug 1955).
|
||||||
|
|
||||||
- Disable saving when document is unchanged (bug 2313)
|
- Disable saving when document is unchanged (bug 2313)
|
||||||
|
Loading…
Reference in New Issue
Block a user