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