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