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