]> git.lyx.org Git - lyx.git/blob - src/changes.C
03e7b6e82c865c2aed5f027531457a8c818aa841
[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::contains(pos_type const pos) const
60 {
61         return pos >= start && pos < end;
62 }
63
64
65 bool Changes::Range::intersects(Range const & r) const
66 {
67         return r.start < end && r.end > start; // end itself is not in the range!
68 }
69
70
71 Changes::Changes(Change::Type const type)
72         : empty_type_(type)
73 {
74 }
75
76
77 Changes::~Changes()
78 {
79 }
80
81
82 Changes::Changes(Changes const & c)
83 {
84         table_ = c.table_;
85 }
86
87
88 void Changes::record(Change const & change, pos_type const pos)
89 {
90         if (lyxerr.debugging(Debug::CHANGES)) {
91                 lyxerr[Debug::CHANGES] << "record " << change.type
92                         << " at pos " << pos << " with total "
93                         << table_.size() << " changes." << endl;
94         }
95
96         switch (change.type) {
97                 case Change::INSERTED:
98                         add(change, pos);
99                         break;
100                 case Change::DELETED:
101                         del(change, pos);
102                         break;
103                 case Change::UNCHANGED:
104                         // FIXME: change tracking (MG)
105                         // set(Change::UNCHANGED, pos);
106                         break;
107         }
108 }
109
110
111 void Changes::set(Change const & change, pos_type const pos)
112 {
113         set(change, pos, pos + 1);
114 }
115
116
117 void Changes::set(Change const & change,
118                   pos_type const start, pos_type const end)
119 {
120         ChangeTable::iterator it = table_.begin();
121
122         if (lyxerr.debugging(Debug::CHANGES)) {
123                 lyxerr[Debug::CHANGES] << "changeset of " << change.type
124                         << " author " << change.author << " time " << change.changetime
125                         << " in range " << start << "," << end << endl;
126         }
127
128         Range const new_range(start, end);
129
130         // remove all sub-ranges
131         for (; it != table_.end();) {
132                 if (new_range != it->range /*&& it->range.contained(new_range)*/) { // FIXME: change tracking (MG)
133                         if (lyxerr.debugging(Debug::CHANGES)) {
134                                 lyxerr[Debug::CHANGES] << "Removing subrange "
135                                         << it->range.start << "," << it->range.end << endl;
136                         }
137                         it = table_.erase(it);
138                 } else {
139                         ++it;
140                 }
141         }
142
143         it = table_.begin();
144         ChangeTable::iterator const itend = table_.end();
145
146         // find a super-range
147         for (; it != itend; ++it) {
148                 if (it->range.contains(new_range))
149                         break;
150         }
151
152         if (it == itend) {
153                 lyxerr[Debug::CHANGES] << "Inserting change at end" << endl;
154                 table_.push_back(ChangeRange(start, end, change));
155                 merge();
156                 return;
157         }
158
159         if (change.type == it->change.type) {
160                 lyxerr[Debug::CHANGES] << "Change set already." << endl;
161                 it->change = change;
162                 return;
163         }
164
165         ChangeRange c(*it);
166
167         if (lyxerr.debugging(Debug::CHANGES)) {
168                 lyxerr[Debug::CHANGES] << "Using change of type " << c.change.type
169                         << " over " << c.range.start << "," << c.range.end << endl;
170         }
171
172         // split head
173         if (c.range.start < start) {
174                 it = table_.insert(it, ChangeRange(c.range.start, start, c.change));
175                 if (lyxerr.debugging(Debug::CHANGES)) {
176                         lyxerr[Debug::CHANGES] << "Splitting head of type " << c.change.type
177                                 << " over " << c.range.start << "," << start << endl;
178                 }
179                 ++it;
180         }
181
182         // reset this as new type
183         it->range.start = start;
184         it->range.end = end;
185         it->change = change;
186         lyxerr[Debug::CHANGES] << "Resetting to new change" << endl;
187
188         // split tail
189         if (c.range.end > end) {
190                 ++it;
191                 table_.insert(it, ChangeRange(end, c.range.end, c.change));
192                 if (lyxerr.debugging(Debug::CHANGES)) {
193                         lyxerr[Debug::CHANGES] << "Splitting tail of type " << c.change.type
194                                 << " over " << end << "," << c.range.end << endl;
195                 }
196         }
197
198         merge();
199 }
200
201
202 void Changes::erase(pos_type const pos)
203 {
204         ChangeTable::iterator it = table_.begin();
205         ChangeTable::iterator end = table_.end();
206
207         bool found = false;
208
209         for (; it != end; ++it) {
210                 Range & range(it->range);
211
212                 if (lyxerr.debugging(Debug::CHANGES)) {
213                         lyxerr[Debug::CHANGES] << "era:Range of type " << it->change.type << " is "
214                                 << it->range.start << "," << it->range.end << endl;
215                 }
216
217                 if (range.contains(pos)) {
218                         found = true;
219                         --range.end;
220                         continue;
221                 }
222
223                 if (found) {
224                         --range.start;
225                         --range.end;
226                 }
227         }
228         merge();
229 }
230
231
232 void Changes::del(Change const & change, ChangeTable::size_type const pos)
233 {
234         // this case happens when building from .lyx
235         if (table_.empty()) {
236                 set(change, pos);
237                 return;
238         }
239
240         ChangeTable::iterator it = table_.begin();
241
242         for (; it != table_.end(); ++it) {
243                 Range & range(it->range);
244
245                 if (range.contains(pos)) {
246                         if (it->change.type != Change::INSERTED) {
247                                 set(change, pos);
248                         } else {
249                                 erase(pos);
250                         }
251                         break;
252                 } else if (/*range.containsOrPrecedes(pos) && it + 1 == table_.end()*/ true) { // FIXME: change tracking (MG)
253                         // this case happens when building from .lyx
254                         set(change, pos);
255                         break;
256                 }
257         }
258 }
259
260
261 void Changes::add(Change const & change, ChangeTable::size_type const pos)
262 {
263         ChangeTable::iterator it = table_.begin();
264         ChangeTable::iterator end = table_.end();
265
266         bool found = false;
267
268         for (; it != end; ++it) {
269                 Range & range(it->range);
270
271                 if (!found /* && range.containsOrPrecedes(pos)*/) { // FIXME: change tracking (MG)
272                         found = true;
273                         if (lyxerr.debugging(Debug::CHANGES)) {
274                                 lyxerr[Debug::CHANGES] << "Found range of "
275                                         << range.start << "," << range.end << endl;
276                         }
277                         ++range.end;
278                         continue;
279                 }
280
281                 if (found) {
282                         ++range.start;
283                         ++range.end;
284                 }
285         }
286         set(change, pos);
287 }
288
289
290 Change const Changes::lookup(pos_type const pos) const
291 {
292         if (!table_.size()) {
293                 if (lyxerr.debugging(Debug::CHANGES))
294                         lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
295                 return Change(empty_type_);
296         }
297
298         ChangeTable::const_iterator it = table_.begin();
299         ChangeTable::const_iterator const end = table_.end();
300
301         for (; it != end; ++it) {
302                 if (it->range.contains(pos))
303                         return it->change;
304         }
305
306         BOOST_ASSERT(false && "missing changes for pos");
307         return Change(Change::UNCHANGED);
308 }
309
310
311 bool Changes::isChange(pos_type const start, pos_type const end) const
312 {
313         if (!table_.size()) {
314                 if (lyxerr.debugging(Debug::CHANGES))
315                         lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
316                 return empty_type_ != Change::UNCHANGED;
317         }
318
319         ChangeTable::const_iterator it = table_.begin();
320         ChangeTable::const_iterator const itend = table_.end();
321
322         for (; it != itend; ++it) {
323                 if (lyxerr.debugging(Debug::CHANGES)) {
324                         lyxerr[Debug::CHANGES] << "Looking for " << start << ","
325                                 << end << " in " << it->range.start << ","
326                                 << it->range.end << "of type " << it->change.type << endl;
327                 }
328
329                 if (it->range.intersects(Range(start, end))
330                         && it->change.type != Change::UNCHANGED) {
331                         if (lyxerr.debugging(Debug::CHANGES)) {
332                                 lyxerr[Debug::CHANGES] << "Found intersection of "
333                                         << start << "," << end << " with "
334                                         << it->range.start << "," << it->range.end
335                                         << " of type " << it->change.type << endl;
336                         }
337                         return true;
338                 }
339         }
340
341         return false;
342 }
343
344
345 void Changes::merge()
346 {
347         if (lyxerr.debugging(Debug::CHANGES))
348                 lyxerr[Debug::CHANGES] << "Starting merge" << endl;
349
350         ChangeTable::iterator it = table_.begin();
351
352         while (it != table_.end()) {
353                 if (lyxerr.debugging(Debug::CHANGES)) {
354                         lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
355                                 << it->range.start << "," << it->range.end << endl;
356                 }
357
358                 if (it->range.start == it->range.end) {
359                         if (lyxerr.debugging(Debug::CHANGES)) {
360                                 lyxerr[Debug::CHANGES] << "Removing empty range for pos "
361                                         << it->range.start << endl;
362                         }
363
364                         table_.erase(it);
365                         // start again
366                         it = table_.begin();
367                         continue;
368                 }
369
370                 if (it + 1 == table_.end())
371                         break;
372
373                 if (it->change == (it + 1)->change) {
374                         if (lyxerr.debugging(Debug::CHANGES)) {
375                                 lyxerr[Debug::CHANGES] << "Merging equal ranges "
376                                         << it->range.start << "," << it->range.end
377                                         << " and " << (it + 1)->range.start << ","
378                                         << (it + 1)->range.end << endl;
379                         }
380
381                         (it + 1)->range.start = it->range.start;
382                         table_.erase(it);
383                         // start again
384                         it = table_.begin();
385                         continue;
386                 }
387
388                 ++it;
389         }
390
391         lyxerr[Debug::CHANGES] << "Merge ended" << endl;
392 }
393
394
395 int Changes::latexMarkChange(odocstream & os,
396                              Change::Type const old, Change::Type const change,
397                              bool const & output)
398 {
399         if (!output || old == change)
400                 return 0;
401
402         static docstring const start(lyx::from_ascii("\\changestart{}"));
403         static docstring const end(lyx::from_ascii("\\changeend{}"));
404         static docstring const son(lyx::from_ascii("\\overstrikeon{}"));
405         static docstring const soff(lyx::from_ascii("\\overstrikeoff{}"));
406
407         int column = 0;
408
409         if (old == Change::DELETED) {
410                 os << soff;
411                 column += soff.length();
412         }
413
414         switch (change) {
415                 case Change::UNCHANGED:
416                         os << end;
417                         column += end.length();
418                         break;
419
420                 case Change::DELETED:
421                         if (old == Change::UNCHANGED) {
422                                 os << start;
423                                 column += start.length();
424                         }
425                         os << son;
426                         column += son.length();
427                         break;
428
429                 case Change::INSERTED:
430                         if (old == Change::UNCHANGED) {
431                                 os << start;
432                                 column += start.length();
433                         }
434                         break;
435         }
436
437         return column;
438 }
439
440
441 void Changes::lyxMarkChange(std::ostream & os, int & column,
442                             lyx::time_type const curtime,
443                             Change const & old, Change const & change)
444 {
445         if (old == change)
446                 return;
447
448         column = 0;
449
450         switch (change.type) {
451                 case Change::UNCHANGED:
452                         os << "\n\\change_unchanged\n";
453                         break;
454
455                 case Change::DELETED: {
456                         lyx::time_type t = change.changetime;
457                         if (!t)
458                                 t = curtime;
459                         os << "\n\\change_deleted " << change.author
460                                 << " " << t << "\n";
461
462                         break;
463                 }
464
465         case Change::INSERTED: {
466                         lyx::time_type t = change.changetime;
467                         if (!t)
468                                 t = curtime;
469                         os << "\n\\change_inserted " << change.author
470                                 << " " << t << "\n";
471                         break;
472         }
473         }
474 }