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>
27 bool operator==(Change const & l, Change const & r)
29 return l.type == r.type && l.author == r.author
30 && l.changetime == r.changetime;
34 bool operator!=(Change const & l, Change const & r)
40 bool operator==(Changes::Range const & r1, Changes::Range const & r2)
42 return r1.start == r2.start && r1.end == r2.end;
46 bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
52 bool Changes::Range::contains(Range const & r) const
54 return r.start >= start && r.end <= end;
58 bool Changes::Range::contains(pos_type const pos) const
60 return pos >= start && pos < end;
64 bool Changes::Range::intersects(Range const & r) const
66 return r.start < end && r.end > start; // end itself is not in the range!
70 void Changes::record(Change const & change, pos_type const pos)
72 if (lyxerr.debugging(Debug::CHANGES)) {
73 lyxerr[Debug::CHANGES] << "record " << change.type
74 << " at pos " << pos << " with total "
75 << table_.size() << " changes." << endl;
78 switch (change.type) {
79 case Change::INSERTED:
85 case Change::UNCHANGED:
86 // FIXME: change tracking (MG)
87 // set(Change::UNCHANGED, pos);
93 void Changes::set(Change const & change, pos_type const pos)
95 set(change, pos, pos + 1);
99 void Changes::set(Change const & change,
100 pos_type const start, pos_type const end)
102 ChangeTable::iterator it = table_.begin();
104 if (lyxerr.debugging(Debug::CHANGES)) {
105 lyxerr[Debug::CHANGES] << "changeset of " << change.type
106 << " author " << change.author << " time " << change.changetime
107 << " in range " << start << "," << end << endl;
110 Range const new_range(start, end);
112 // remove all sub-ranges
113 for (; it != table_.end();) {
114 if (new_range != it->range /*&& it->range.contained(new_range)*/) { // FIXME: change tracking (MG)
115 if (lyxerr.debugging(Debug::CHANGES)) {
116 lyxerr[Debug::CHANGES] << "Removing subrange "
117 << it->range.start << "," << it->range.end << endl;
119 it = table_.erase(it);
126 ChangeTable::iterator const itend = table_.end();
128 // find a super-range
129 for (; it != itend; ++it) {
130 if (it->range.contains(new_range))
135 lyxerr[Debug::CHANGES] << "Inserting change at end" << endl;
136 table_.push_back(ChangeRange(start, end, change));
141 if (change.type == it->change.type) {
142 lyxerr[Debug::CHANGES] << "Change set already." << endl;
149 if (lyxerr.debugging(Debug::CHANGES)) {
150 lyxerr[Debug::CHANGES] << "Using change of type " << c.change.type
151 << " over " << c.range.start << "," << c.range.end << endl;
155 if (c.range.start < start) {
156 it = table_.insert(it, ChangeRange(c.range.start, start, c.change));
157 if (lyxerr.debugging(Debug::CHANGES)) {
158 lyxerr[Debug::CHANGES] << "Splitting head of type " << c.change.type
159 << " over " << c.range.start << "," << start << endl;
164 // reset this as new type
165 it->range.start = start;
168 lyxerr[Debug::CHANGES] << "Resetting to new change" << endl;
171 if (c.range.end > end) {
173 table_.insert(it, ChangeRange(end, c.range.end, c.change));
174 if (lyxerr.debugging(Debug::CHANGES)) {
175 lyxerr[Debug::CHANGES] << "Splitting tail of type " << c.change.type
176 << " over " << end << "," << c.range.end << endl;
184 void Changes::erase(pos_type const pos)
186 ChangeTable::iterator it = table_.begin();
187 ChangeTable::iterator end = table_.end();
191 for (; it != end; ++it) {
192 Range & range(it->range);
194 if (lyxerr.debugging(Debug::CHANGES)) {
195 lyxerr[Debug::CHANGES] << "era:Range of type " << it->change.type << " is "
196 << it->range.start << "," << it->range.end << endl;
199 if (range.contains(pos)) {
214 void Changes::del(Change const & change, ChangeTable::size_type const pos)
216 // this case happens when building from .lyx
217 if (table_.empty()) {
222 ChangeTable::iterator it = table_.begin();
224 for (; it != table_.end(); ++it) {
225 Range & range(it->range);
227 if (range.contains(pos)) {
228 if (it->change.type != Change::INSERTED) {
234 } else if (/*range.containsOrPrecedes(pos) && it + 1 == table_.end()*/ true) { // FIXME: change tracking (MG)
235 // this case happens when building from .lyx
243 void Changes::add(Change const & change, ChangeTable::size_type const pos)
245 ChangeTable::iterator it = table_.begin();
246 ChangeTable::iterator end = table_.end();
250 for (; it != end; ++it) {
251 Range & range(it->range);
253 if (!found /* && range.containsOrPrecedes(pos)*/) { // FIXME: change tracking (MG)
255 if (lyxerr.debugging(Debug::CHANGES)) {
256 lyxerr[Debug::CHANGES] << "Found range of "
257 << range.start << "," << range.end << endl;
272 Change const Changes::lookup(pos_type const pos) const
274 ChangeTable::const_iterator it = table_.begin();
275 ChangeTable::const_iterator const end = table_.end();
277 for (; it != end; ++it) {
278 if (it->range.contains(pos))
282 BOOST_ASSERT(false && "missing changes for pos");
283 return Change(Change::UNCHANGED);
287 bool Changes::isChanged(pos_type const start, pos_type const end) const
289 ChangeTable::const_iterator it = table_.begin();
290 ChangeTable::const_iterator const itend = table_.end();
292 for (; it != itend; ++it) {
293 if (lyxerr.debugging(Debug::CHANGES)) {
294 lyxerr[Debug::CHANGES] << "Looking for " << start << ","
295 << end << " in " << it->range.start << ","
296 << it->range.end << "of type " << it->change.type << endl;
299 if (it->range.intersects(Range(start, end))
300 && it->change.type != Change::UNCHANGED) {
301 if (lyxerr.debugging(Debug::CHANGES)) {
302 lyxerr[Debug::CHANGES] << "Found intersection of "
303 << start << "," << end << " with "
304 << it->range.start << "," << it->range.end
305 << " of type " << it->change.type << endl;
315 void Changes::merge()
317 if (lyxerr.debugging(Debug::CHANGES))
318 lyxerr[Debug::CHANGES] << "Starting merge" << endl;
320 ChangeTable::iterator it = table_.begin();
322 while (it != table_.end()) {
323 if (lyxerr.debugging(Debug::CHANGES)) {
324 lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
325 << it->range.start << "," << it->range.end << endl;
328 if (it->range.start == it->range.end) {
329 if (lyxerr.debugging(Debug::CHANGES)) {
330 lyxerr[Debug::CHANGES] << "Removing empty range for pos "
331 << it->range.start << endl;
340 if (it + 1 == table_.end())
343 if (it->change == (it + 1)->change) {
344 if (lyxerr.debugging(Debug::CHANGES)) {
345 lyxerr[Debug::CHANGES] << "Merging equal ranges "
346 << it->range.start << "," << it->range.end
347 << " and " << (it + 1)->range.start << ","
348 << (it + 1)->range.end << endl;
351 (it + 1)->range.start = it->range.start;
361 lyxerr[Debug::CHANGES] << "Merge ended" << endl;
365 int Changes::latexMarkChange(odocstream & os,
366 Change::Type const old, Change::Type const change,
369 if (!output || old == change)
372 static docstring const start(from_ascii("\\changestart{}"));
373 static docstring const end(from_ascii("\\changeend{}"));
374 static docstring const son(from_ascii("\\overstrikeon{}"));
375 static docstring const soff(from_ascii("\\overstrikeoff{}"));
379 if (old == Change::DELETED) {
381 column += soff.length();
385 case Change::UNCHANGED:
387 column += end.length();
390 case Change::DELETED:
391 if (old == Change::UNCHANGED) {
393 column += start.length();
396 column += son.length();
399 case Change::INSERTED:
400 if (old == Change::UNCHANGED) {
402 column += start.length();
411 void Changes::lyxMarkChange(std::ostream & os, int & column,
412 time_type const curtime,
413 Change const & old, Change const & change)
420 switch (change.type) {
421 case Change::UNCHANGED:
422 os << "\n\\change_unchanged\n";
425 case Change::DELETED: {
426 time_type t = change.changetime;
429 os << "\n\\change_deleted " << change.author
435 case Change::INSERTED: {
436 time_type t = change.changetime;
439 os << "\n\\change_inserted " << change.author