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