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