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