3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
10 * Record changes in a paragraph.
18 #include <boost/assert.hpp>
25 bool operator==(Change const & l, Change const & r)
27 return l.type == r.type && l.author == r.author
28 && l.changetime == r.changetime;
32 bool operator!=(Change const & l, Change const & r)
38 bool operator==(Changes::Range const & r1, Changes::Range const & r2)
40 return r1.start == r2.start && r1.end == r2.end;
44 bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
50 bool Changes::Range::contains(Range const & r) const
52 return r.start >= start && r.end <= end;
56 bool Changes::Range::contained(Range const & r) const
58 return r.contains(*this);
62 bool Changes::Range::contains(pos_type pos) const
64 return pos >= start && pos < end;
68 bool Changes::Range::loose_contains(pos_type pos) const
70 return pos >= start && pos <= end;
74 bool Changes::Range::intersects(Range const & r) const
76 return contained(r) || contains(r)
77 || contains(r.start) || contains(r.end);
81 Changes::Changes(Change::Type type)
92 Changes::Changes(Changes const & c)
98 void Changes::record(Change change, pos_type pos)
100 if (lyxerr.debugging(Debug::CHANGES)) {
101 lyxerr[Debug::CHANGES] << "record " << change.type
102 << " at pos " << pos << " with total "
103 << table_.size() << " changes." << endl;
106 switch (change.type) {
107 case Change::INSERTED:
110 case Change::DELETED:
113 case Change::UNCHANGED:
114 set(Change::UNCHANGED, pos);
120 void Changes::set(Change change, pos_type pos)
122 set(change, pos, pos + 1);
126 void Changes::set(Change::Type type, pos_type pos)
128 set(type, pos, pos + 1);
132 void Changes::set(Change::Type type, pos_type start, pos_type end)
134 set(Change(type), start, end);
138 void Changes::set(Change change, pos_type start, pos_type end)
140 ChangeTable::iterator it = table_.begin();
142 if (lyxerr.debugging(Debug::CHANGES)) {
143 lyxerr[Debug::CHANGES] << "changeset of " << change.type
144 << " author " << change.author << " time " << change.changetime
145 << " in range " << start << "," << end << endl;
148 Range const new_range(start, end);
150 // remove all sub-ranges
151 for (; it != table_.end();) {
152 if (new_range != it->range && it->range.contained(new_range)) {
153 if (lyxerr.debugging(Debug::CHANGES)) {
154 lyxerr[Debug::CHANGES] << "Removing subrange "
155 << it->range.start << "," << it->range.end << endl;
157 it = table_.erase(it);
164 ChangeTable::iterator itend = table_.end();
166 // find a super-range
167 for (; it != itend; ++it) {
168 if (it->range.contains(new_range))
173 lyxerr[Debug::CHANGES] << "Inserting change at end" << endl;
174 table_.push_back(ChangeRange(start, end, change));
179 if (change.type == it->change.type) {
180 lyxerr[Debug::CHANGES] << "Change set already." << endl;
187 if (lyxerr.debugging(Debug::CHANGES)) {
188 lyxerr[Debug::CHANGES] << "Using change of type " << c.change.type
189 << " over " << c.range.start << "," << c.range.end << endl;
193 if (c.range.start < start) {
194 it = table_.insert(it, ChangeRange(c.range.start, start, c.change));
195 if (lyxerr.debugging(Debug::CHANGES)) {
196 lyxerr[Debug::CHANGES] << "Splitting head of type " << c.change.type
197 << " over " << c.range.start << "," << start << endl;
202 // reset this as new type
203 it->range.start = start;
206 lyxerr[Debug::CHANGES] << "Resetting to new change" << endl;
209 if (c.range.end > end) {
211 table_.insert(it, ChangeRange(end, c.range.end, c.change));
212 if (lyxerr.debugging(Debug::CHANGES)) {
213 lyxerr[Debug::CHANGES] << "Splitting tail of type " << c.change.type
214 << " over " << end << "," << c.range.end << endl;
223 void Changes::erase(pos_type pos)
225 ChangeTable::iterator it = table_.begin();
226 ChangeTable::iterator end = table_.end();
230 for (; it != end; ++it) {
231 Range & range(it->range);
233 if (lyxerr.debugging(Debug::CHANGES)) {
234 lyxerr[Debug::CHANGES] << "era:Range of type " << it->change.type << " is "
235 << it->range.start << "," << it->range.end << endl;
238 if (range.contains(pos)) {
254 void Changes::del(Change change, ChangeTable::size_type pos)
256 // this case happens when building from .lyx
257 if (table_.empty()) {
262 ChangeTable::iterator it = table_.begin();
264 for (; it != table_.end(); ++it) {
265 Range & range(it->range);
267 if (range.contains(pos)) {
268 if (it->change.type != Change::INSERTED) {
274 } else if (range.loose_contains(pos) && it + 1 == table_.end()) {
275 // this case happens when building from .lyx
283 void Changes::add(Change change, ChangeTable::size_type pos)
285 ChangeTable::iterator it = table_.begin();
286 ChangeTable::iterator end = table_.end();
290 for (; it != end; ++it) {
291 Range & range(it->range);
293 if (!found && range.loose_contains(pos)) {
295 if (lyxerr.debugging(Debug::CHANGES)) {
296 lyxerr[Debug::CHANGES] << "Found range of "
297 << range.start << "," << range.end << endl;
312 Change const Changes::lookupFull(pos_type pos) const
314 if (!table_.size()) {
315 if (lyxerr.debugging(Debug::CHANGES))
316 lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
317 return Change(empty_type_);
320 ChangeTable::const_iterator it = table_.begin();
321 ChangeTable::const_iterator end = table_.end();
323 for (; it != end; ++it) {
324 if (it->range.contains(pos))
330 return Change(Change::UNCHANGED);
334 Change::Type Changes::lookup(pos_type pos) const
336 if (!table_.size()) {
337 if (lyxerr.debugging(Debug::CHANGES))
338 lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
342 ChangeTable::const_iterator it = table_.begin();
343 ChangeTable::const_iterator end = table_.end();
345 for (; it != end; ++it) {
346 if (it->range.contains(pos))
347 return it->change.type;
352 return Change::UNCHANGED;
356 bool Changes::isChange(pos_type start, pos_type end) const
358 if (!table_.size()) {
359 if (lyxerr.debugging(Debug::CHANGES))
360 lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
361 return empty_type_ != Change::UNCHANGED;
364 ChangeTable::const_iterator it = table_.begin();
365 ChangeTable::const_iterator itend = table_.end();
367 for (; it != itend; ++it) {
368 if (lyxerr.debugging(Debug::CHANGES)) {
369 lyxerr[Debug::CHANGES] << "Looking for " << start << ","
370 << end << " in " << it->range.start << ","
371 << it->range.end << "of type " << it->change.type << endl;
374 if (it->range.intersects(Range(start, end))
375 && it->change.type != Change::UNCHANGED) {
376 if (lyxerr.debugging(Debug::CHANGES)) {
377 lyxerr[Debug::CHANGES] << "Found intersection of "
378 << start << "," << end << " with "
379 << it->range.start << "," << it->range.end
380 << " of type " << it->change.type << endl;
390 bool Changes::isChangeEdited(lyx::pos_type start, lyx::pos_type end) const
392 if (!table_.size()) {
393 if (lyxerr.debugging(Debug::CHANGES))
394 lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
395 return empty_type_ != Change::INSERTED;
398 ChangeTable::const_iterator it = table_.begin();
399 ChangeTable::const_iterator itend = table_.end();
401 for (; it != itend; ++it) {
402 if (it->range.intersects(Range(start, end ? end - 1 : 0))
403 && it->change.type != Change::INSERTED) {
411 void Changes::merge()
413 if (lyxerr.debugging(Debug::CHANGES))
414 lyxerr[Debug::CHANGES] << "Starting merge" << endl;
416 ChangeTable::iterator it = table_.begin();
418 while (it != table_.end()) {
419 if (lyxerr.debugging(Debug::CHANGES)) {
420 lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
421 << it->range.start << "," << it->range.end << endl;
424 if (it->range.start == it->range.end) {
425 if (lyxerr.debugging(Debug::CHANGES)) {
426 lyxerr[Debug::CHANGES] << "Removing empty range for pos "
427 << it->range.start << endl;
436 if (it + 1 == table_.end())
439 if (it->change == (it + 1)->change) {
440 if (lyxerr.debugging(Debug::CHANGES)) {
441 lyxerr[Debug::CHANGES] << "Merging equal ranges "
442 << it->range.start << "," << it->range.end
443 << " and " << (it + 1)->range.start << ","
444 << (it + 1)->range.end << endl;
447 (it + 1)->range.start = it->range.start;
457 lyxerr[Debug::CHANGES] << "Merge ended" << endl;
462 void Changes::check() const
464 ChangeTable::const_iterator it = table_.begin();
465 ChangeTable::const_iterator end = table_.end();
467 bool dont_assert(true);
469 lyxerr[Debug::CHANGES] << "Changelist:" << endl;
470 for (; it != end; ++it) {
471 if (lyxerr.debugging(Debug::CHANGES)) {
472 lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
473 << it->range.start << "," << it->range.end << " author "
474 << it->change.author << " time " << it->change.changetime << endl;
480 Range const & range(it->range);
481 Range const & next((it + 1)->range);
482 if (range.end != next.start)
486 if (lyxerr.debugging(Debug::CHANGES))
487 lyxerr[Debug::CHANGES] << "End" << endl;
489 BOOST_ASSERT(dont_assert);
493 int Changes::latexMarkChange(std::ostream & os, Change::Type old, Change::Type change)
498 string const start("\\changestart{}");
499 string const end("\\changeend{}");
500 string const son("\\overstrikeon{}");
501 string const soff("\\overstrikeoff{}");
505 if (old == Change::DELETED) {
507 column += soff.length();
511 case Change::UNCHANGED:
513 column += end.length();
516 case Change::DELETED:
517 if (old == Change::UNCHANGED) {
519 column += start.length();
522 column += son.length();
525 case Change::INSERTED:
526 if (old == Change::UNCHANGED) {
528 column += start.length();
537 void Changes::lyxMarkChange(std::ostream & os, int & column, lyx::time_type curtime,
538 Change const & old, Change const & change)
545 switch (change.type) {
546 case Change::UNCHANGED:
547 os << "\n\\change_unchanged\n";
550 case Change::DELETED: {
551 lyx::time_type t(change.changetime);
554 os << "\n\\change_deleted " << change.author
560 case Change::INSERTED:
561 lyx::time_type t(change.changetime);
564 os << "\n\\change_inserted " << change.author