]> git.lyx.org Git - lyx.git/blob - src/changes.C
make it compile again (hopefully)
[lyx.git] / src / changes.C
1 /**
2  * \file changes.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  *
8  * Full author contact details are available in file CREDITS.
9  *
10  * Record changes in a paragraph.
11  */
12
13 #include <config.h>
14
15 #include "changes.h"
16 #include "debug.h"
17
18 #include <boost/assert.hpp>
19
20 using lyx::docstring;
21 using lyx::odocstream;
22 using lyx::pos_type;
23
24 using std::endl;
25 using std::string;
26
27
28 bool operator==(Change const & l, Change const & r)
29 {
30         return l.type == r.type && l.author == r.author
31                 && l.changetime == r.changetime;
32 }
33
34
35 bool operator!=(Change const & l, Change const & r)
36 {
37         return !(l == r);
38 }
39
40
41 bool operator==(Changes::Range const & r1, Changes::Range const & r2)
42 {
43         return r1.start == r2.start && r1.end == r2.end;
44 }
45
46
47 bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
48 {
49         return !(r1 == r2);
50 }
51
52
53 bool Changes::Range::contains(Range const & r) const
54 {
55         return r.start >= start && r.end <= end;
56 }
57
58
59 bool Changes::Range::contained(Range const & r) const
60 {
61         return r.contains(*this);
62 }
63
64
65 bool Changes::Range::contains(pos_type const pos) const
66 {
67         return pos >= start && pos < end;
68 }
69
70
71 bool Changes::Range::containsOrPrecedes(pos_type const pos) const
72 {
73         return pos >= start && pos <= end;
74 }
75
76
77 bool Changes::Range::intersects(Range const & r) const
78 {
79         return contained(r) || contains(r)
80                 || contains(r.start) || contains(r.end);
81 }
82
83
84 Changes::Changes(Change::Type const type)
85         : empty_type_(type)
86 {
87 }
88
89
90 Changes::~Changes()
91 {
92 }
93
94
95 Changes::Changes(Changes const & c)
96 {
97         table_ = c.table_;
98 }
99
100
101 void Changes::record(Change const & change, pos_type const pos)
102 {
103         if (lyxerr.debugging(Debug::CHANGES)) {
104                 lyxerr[Debug::CHANGES] << "record " << change.type
105                         << " at pos " << pos << " with total "
106                         << table_.size() << " changes." << endl;
107         }
108
109         switch (change.type) {
110                 case Change::INSERTED:
111                         add(change, pos);
112                         break;
113                 case Change::DELETED:
114                         del(change, pos);
115                         break;
116                 case Change::UNCHANGED:
117                         set(Change::UNCHANGED, pos);
118                         break;
119         }
120 }
121
122
123 void Changes::set(Change const & change, pos_type const pos)
124 {
125         set(change, pos, pos + 1);
126 }
127
128
129 void Changes::set(Change::Type const type, pos_type const pos)
130 {
131         set(type, pos, pos + 1);
132 }
133
134
135 void Changes::set(Change::Type const type,
136                   pos_type const start, pos_type const end)
137 {
138         set(Change(type), start, end);
139 }
140
141
142 void Changes::set(Change const & change,
143                   pos_type const start, pos_type const end)
144 {
145         ChangeTable::iterator it = table_.begin();
146
147         if (lyxerr.debugging(Debug::CHANGES)) {
148                 lyxerr[Debug::CHANGES] << "changeset of " << change.type
149                         << " author " << change.author << " time " << change.changetime
150                         << " in range " << start << "," << end << endl;
151         }
152
153         Range const new_range(start, end);
154
155         // remove all sub-ranges
156         for (; it != table_.end();) {
157                 if (new_range != it->range && it->range.contained(new_range)) {
158                         if (lyxerr.debugging(Debug::CHANGES)) {
159                                 lyxerr[Debug::CHANGES] << "Removing subrange "
160                                         << it->range.start << "," << it->range.end << endl;
161                         }
162                         it = table_.erase(it);
163                 } else {
164                         ++it;
165                 }
166         }
167
168         it = table_.begin();
169         ChangeTable::iterator const itend = table_.end();
170
171         // find a super-range
172         for (; it != itend; ++it) {
173                 if (it->range.contains(new_range))
174                         break;
175         }
176
177         if (it == itend) {
178                 lyxerr[Debug::CHANGES] << "Inserting change at end" << endl;
179                 table_.push_back(ChangeRange(start, end, change));
180                 merge();
181                 return;
182         }
183
184         if (change.type == it->change.type) {
185                 lyxerr[Debug::CHANGES] << "Change set already." << endl;
186                 it->change = change;
187                 return;
188         }
189
190         ChangeRange c(*it);
191
192         if (lyxerr.debugging(Debug::CHANGES)) {
193                 lyxerr[Debug::CHANGES] << "Using change of type " << c.change.type
194                         << " over " << c.range.start << "," << c.range.end << endl;
195         }
196
197         // split head
198         if (c.range.start < start) {
199                 it = table_.insert(it, ChangeRange(c.range.start, start, c.change));
200                 if (lyxerr.debugging(Debug::CHANGES)) {
201                         lyxerr[Debug::CHANGES] << "Splitting head of type " << c.change.type
202                                 << " over " << c.range.start << "," << start << endl;
203                 }
204                 ++it;
205         }
206
207         // reset this as new type
208         it->range.start = start;
209         it->range.end = end;
210         it->change = change;
211         lyxerr[Debug::CHANGES] << "Resetting to new change" << endl;
212
213         // split tail
214         if (c.range.end > end) {
215                 ++it;
216                 table_.insert(it, ChangeRange(end, c.range.end, c.change));
217                 if (lyxerr.debugging(Debug::CHANGES)) {
218                         lyxerr[Debug::CHANGES] << "Splitting tail of type " << c.change.type
219                                 << " over " << end << "," << c.range.end << endl;
220                 }
221         }
222
223         check();
224         merge();
225 }
226
227
228 void Changes::erase(pos_type const pos)
229 {
230         ChangeTable::iterator it = table_.begin();
231         ChangeTable::iterator end = table_.end();
232
233         bool found = false;
234
235         for (; it != end; ++it) {
236                 Range & range(it->range);
237
238                 if (lyxerr.debugging(Debug::CHANGES)) {
239                         lyxerr[Debug::CHANGES] << "era:Range of type " << it->change.type << " is "
240                                 << it->range.start << "," << it->range.end << endl;
241                 }
242
243                 if (range.contains(pos)) {
244                         found = true;
245                         --range.end;
246                         continue;
247                 }
248
249                 if (found) {
250                         --range.start;
251                         --range.end;
252                 }
253         }
254         check();
255         merge();
256 }
257
258
259 void Changes::del(Change const & change, ChangeTable::size_type const pos)
260 {
261         // this case happens when building from .lyx
262         if (table_.empty()) {
263                 set(change, pos);
264                 return;
265         }
266
267         ChangeTable::iterator it = table_.begin();
268
269         for (; it != table_.end(); ++it) {
270                 Range & range(it->range);
271
272                 if (range.contains(pos)) {
273                         if (it->change.type != Change::INSERTED) {
274                                 set(change, pos);
275                         } else {
276                                 erase(pos);
277                         }
278                         break;
279                 } else if (range.containsOrPrecedes(pos) && it + 1 == table_.end()) {
280                         // this case happens when building from .lyx
281                         set(change, pos);
282                         break;
283                 }
284         }
285 }
286
287
288 void Changes::add(Change const & change, ChangeTable::size_type const pos)
289 {
290         ChangeTable::iterator it = table_.begin();
291         ChangeTable::iterator end = table_.end();
292
293         bool found = false;
294
295         for (; it != end; ++it) {
296                 Range & range(it->range);
297
298                 if (!found && range.containsOrPrecedes(pos)) {
299                         found = true;
300                         if (lyxerr.debugging(Debug::CHANGES)) {
301                                 lyxerr[Debug::CHANGES] << "Found range of "
302                                         << range.start << "," << range.end << endl;
303                         }
304                         ++range.end;
305                         continue;
306                 }
307
308                 if (found) {
309                         ++range.start;
310                         ++range.end;
311                 }
312         }
313         set(change, pos);
314 }
315
316
317 Change const Changes::lookup(pos_type const pos) const
318 {
319         if (!table_.size()) {
320                 if (lyxerr.debugging(Debug::CHANGES))
321                         lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
322                 return Change(empty_type_);
323         }
324
325         ChangeTable::const_iterator it = table_.begin();
326         ChangeTable::const_iterator const end = table_.end();
327
328         for (; it != end; ++it) {
329                 if (it->range.contains(pos))
330                         return it->change;
331         }
332
333         check();
334         BOOST_ASSERT(false && "missing changes for pos");
335         return Change(Change::UNCHANGED);
336 }
337
338
339 bool Changes::isChange(pos_type const start, pos_type const end) const
340 {
341         if (!table_.size()) {
342                 if (lyxerr.debugging(Debug::CHANGES))
343                         lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
344                 return empty_type_ != Change::UNCHANGED;
345         }
346
347         ChangeTable::const_iterator it = table_.begin();
348         ChangeTable::const_iterator const itend = table_.end();
349
350         for (; it != itend; ++it) {
351                 if (lyxerr.debugging(Debug::CHANGES)) {
352                         lyxerr[Debug::CHANGES] << "Looking for " << start << ","
353                                 << end << " in " << it->range.start << ","
354                                 << it->range.end << "of type " << it->change.type << endl;
355                 }
356
357                 if (it->range.intersects(Range(start, end))
358                         && it->change.type != Change::UNCHANGED) {
359                         if (lyxerr.debugging(Debug::CHANGES)) {
360                                 lyxerr[Debug::CHANGES] << "Found intersection of "
361                                         << start << "," << end << " with "
362                                         << it->range.start << "," << it->range.end
363                                         << " of type " << it->change.type << endl;
364                         }
365                         return true;
366                 }
367         }
368
369         return false;
370 }
371
372
373 void Changes::merge()
374 {
375         if (lyxerr.debugging(Debug::CHANGES))
376                 lyxerr[Debug::CHANGES] << "Starting merge" << endl;
377
378         ChangeTable::iterator it = table_.begin();
379
380         while (it != table_.end()) {
381                 if (lyxerr.debugging(Debug::CHANGES)) {
382                         lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
383                                 << it->range.start << "," << it->range.end << endl;
384                 }
385
386                 if (it->range.start == it->range.end) {
387                         if (lyxerr.debugging(Debug::CHANGES)) {
388                                 lyxerr[Debug::CHANGES] << "Removing empty range for pos "
389                                         << it->range.start << endl;
390                         }
391
392                         table_.erase(it);
393                         // start again
394                         it = table_.begin();
395                         continue;
396                 }
397
398                 if (it + 1 == table_.end())
399                         break;
400
401                 if (it->change == (it + 1)->change) {
402                         if (lyxerr.debugging(Debug::CHANGES)) {
403                                 lyxerr[Debug::CHANGES] << "Merging equal ranges "
404                                         << it->range.start << "," << it->range.end
405                                         << " and " << (it + 1)->range.start << ","
406                                         << (it + 1)->range.end << endl;
407                         }
408
409                         (it + 1)->range.start = it->range.start;
410                         table_.erase(it);
411                         // start again
412                         it = table_.begin();
413                         continue;
414                 }
415
416                 ++it;
417         }
418
419         lyxerr[Debug::CHANGES] << "Merge ended" << endl;
420         check();
421 }
422
423
424 void Changes::check() const
425 {
426         ChangeTable::const_iterator it = table_.begin();
427         ChangeTable::const_iterator end = table_.end();
428
429         bool dont_assert = true;
430
431         lyxerr[Debug::CHANGES] << "Changelist:" << endl;
432         for (; it != end; ++it) {
433                 if (lyxerr.debugging(Debug::CHANGES)) {
434                         lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
435                                 << it->range.start << "," << it->range.end << " author "
436                                 << it->change.author << " time " << it->change.changetime << endl;
437                 }
438
439                 if (it + 1 == end)
440                         break;
441
442                 Range const & range(it->range);
443                 Range const & next((it + 1)->range);
444                 if (range.end != next.start)
445                         dont_assert = false;
446         }
447
448         if (lyxerr.debugging(Debug::CHANGES))
449                 lyxerr[Debug::CHANGES] << "End" << endl;
450
451         BOOST_ASSERT(dont_assert);
452 }
453
454
455 int Changes::latexMarkChange(odocstream & os,
456                              Change::Type const old, Change::Type const change,
457                              bool const & output)
458 {
459         if (!output || old == change)
460                 return 0;
461
462         static docstring const start(lyx::from_ascii("\\changestart{}"));
463         static docstring const end(lyx::from_ascii("\\changeend{}"));
464         static docstring const son(lyx::from_ascii("\\overstrikeon{}"));
465         static docstring const soff(lyx::from_ascii("\\overstrikeoff{}"));
466
467         int column = 0;
468
469         if (old == Change::DELETED) {
470                 os << soff;
471                 column += soff.length();
472         }
473
474         switch (change) {
475                 case Change::UNCHANGED:
476                         os << end;
477                         column += end.length();
478                         break;
479
480                 case Change::DELETED:
481                         if (old == Change::UNCHANGED) {
482                                 os << start;
483                                 column += start.length();
484                         }
485                         os << son;
486                         column += son.length();
487                         break;
488
489                 case Change::INSERTED:
490                         if (old == Change::UNCHANGED) {
491                                 os << start;
492                                 column += start.length();
493                         }
494                         break;
495         }
496
497         return column;
498 }
499
500
501 void Changes::lyxMarkChange(std::ostream & os, int & column,
502                             lyx::time_type const curtime,
503                             Change const & old, Change const & change)
504 {
505         if (old == change)
506                 return;
507
508         column = 0;
509
510         switch (change.type) {
511                 case Change::UNCHANGED:
512                         os << "\n\\change_unchanged\n";
513                         break;
514
515                 case Change::DELETED: {
516                         lyx::time_type t = change.changetime;
517                         if (!t)
518                                 t = curtime;
519                         os << "\n\\change_deleted " << change.author
520                                 << " " << t << "\n";
521
522                         break;
523                 }
524
525         case Change::INSERTED: {
526                         lyx::time_type t = change.changetime;
527                         if (!t)
528                                 t = curtime;
529                         os << "\n\\change_inserted " << change.author
530                                 << " " << t << "\n";
531                         break;
532         }
533         }
534 }