]> git.lyx.org Git - lyx.git/blob - src/Changes.cpp
Fixed some lines that were too long. It compiled afterwards.
[lyx.git] / src / Changes.cpp
1 /**
2  * \file Changes.cpp
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  * \author Michael Gerz
8  *
9  * Full author contact details are available in file CREDITS.
10  *
11  * Record changes in a paragraph.
12  */
13
14 #include <config.h>
15
16 #include "Changes.h"
17 #include "debug.h"
18 #include "Author.h"
19 #include "BufferParams.h"
20 #include "LaTeXFeatures.h"
21
22 #include <boost/assert.hpp>
23
24 using std::abs;
25 using std::endl;
26 using std::string;
27 using std::max;
28
29 namespace lyx {
30
31 /*
32  * Class Change has a changetime field that specifies the exact time at which
33  * a specific change was made. The change time is used as a guidance for the
34  * user while editing his document. Presently, it is not considered for LaTeX
35  * export.
36  * When merging two adjacent changes, the changetime is not considered,
37  * only the equality of the change type and author is checked (in method
38  * isSimilarTo(...)). If two changes are in fact merged (in method merge()),
39  * the later change time is preserved.
40  */
41
42 bool Change::isSimilarTo(Change const & change)
43 {
44         if (type != change.type) {
45                 return false;
46         }
47
48         if (type == Change::UNCHANGED) {
49                 return true;
50         }
51
52         return author == change.author;
53 }
54
55
56 bool operator==(Change const & l, Change const & r)
57 {
58         if (l.type != r.type) {
59                 return false;
60         }
61
62         // two changes of type UNCHANGED are always equal
63         if (l.type == Change::UNCHANGED) {
64                 return true;
65         }
66
67         return l.author == r.author &&
68                l.changetime == r.changetime;
69 }
70
71
72 bool operator!=(Change const & l, Change const & r)
73 {
74         return !(l == r);
75 }
76
77
78 bool operator==(Changes::Range const & r1, Changes::Range const & r2)
79 {
80         return r1.start == r2.start && r1.end == r2.end;
81 }
82
83
84 bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
85 {
86         return !(r1 == r2);
87 }
88
89
90 bool Changes::Range::intersects(Range const & r) const
91 {
92         return r.start < end && r.end > start; // end itself is not in the range!
93 }
94
95
96 void Changes::set(Change const & change, pos_type const pos)
97 {
98         set(change, pos, pos + 1);
99 }
100
101
102 void Changes::set(Change const & change, pos_type const start, pos_type const end)
103 {
104         if (change.type != Change::UNCHANGED) {
105                 LYXERR(Debug::CHANGES) << "setting change (type: " << change.type
106                         << ", author: " << change.author << ", time: " << change.changetime
107                         << ") in range (" << start << ", " << end << ")" << endl;
108         }
109
110         Range const newRange(start, end);
111
112         ChangeTable::iterator it = table_.begin();
113
114         for (; it != table_.end(); ) {
115                 // current change starts like or follows new change
116                 if (it->range.start >= start) {
117                         break;
118                 }
119
120                 // new change intersects with existing change
121                 if (it->range.end > start) {
122                         pos_type oldEnd = it->range.end;
123                         it->range.end = start;
124
125                         LYXERR(Debug::CHANGES) << "  cutting tail of type " << it->change.type
126                                 << " resulting in range (" << it->range.start << ", "
127                                 << it->range.end << ")" << endl;
128
129                         ++it;
130                         if (oldEnd >= end) {
131                                 LYXERR(Debug::CHANGES) << "  inserting tail in range ("
132                                         << end << ", " << oldEnd << ")" << endl;
133                                 it = table_.insert(it, ChangeRange((it-1)->change, Range(end, oldEnd)));
134                         }
135                         continue;
136                 }
137
138                 ++it;
139         }
140
141         if (change.type != Change::UNCHANGED) {
142                 LYXERR(Debug::CHANGES) << "  inserting change" << endl;
143                 it = table_.insert(it, ChangeRange(change, Range(start, end)));
144                 ++it;
145         }
146
147         for (; it != table_.end(); ) {
148                 // new change 'contains' existing change
149                 if (newRange.contains(it->range)) {
150                         LYXERR(Debug::CHANGES) << "  removing subrange ("
151                                 << it->range.start << ", " << it->range.end << ")" << endl;
152                         it = table_.erase(it);
153                         continue;
154                 }
155
156                 // new change precedes existing change
157                 if (it->range.start >= end) {
158                         break;
159                 }
160
161                 // new change intersects with existing change
162                 it->range.start = end;
163                 LYXERR(Debug::CHANGES) << "  cutting head of type "
164                         << it->change.type << " resulting in range ("
165                         << end << ", " << it->range.end << ")" << endl;
166                 break; // no need for another iteration
167         }
168
169         merge();
170 }
171
172
173 void Changes::erase(pos_type const pos)
174 {
175         LYXERR(Debug::CHANGES) << "Erasing change at position " << pos << endl;
176
177         ChangeTable::iterator it = table_.begin();
178         ChangeTable::iterator end = table_.end();
179
180         for (; it != end; ++it) {
181                 // range (pos,pos+x) becomes (pos,pos+x-1)
182                 if (it->range.start > pos) {
183                         --(it->range.start);
184                 }
185                 // range (pos-x,pos) stays (pos-x,pos)
186                 if (it->range.end > pos) {
187                         --(it->range.end);
188                 }
189         }
190
191         merge();
192 }
193
194
195 void Changes::insert(Change const & change, lyx::pos_type pos)
196 {
197         if (change.type != Change::UNCHANGED) {
198                 LYXERR(Debug::CHANGES) << "Inserting change of type " << change.type
199                         << " at position " << pos << endl;
200         }
201
202         ChangeTable::iterator it = table_.begin();
203         ChangeTable::iterator end = table_.end();
204
205         for (; it != end; ++it) {
206                 // range (pos,pos+x) becomes (pos+1,pos+x+1)
207                 if (it->range.start >= pos) {
208                         ++(it->range.start);
209                 }
210
211                 // range (pos-x,pos) stays as it is
212                 if (it->range.end > pos) {
213                         ++(it->range.end);
214                 }
215         }
216
217         set(change, pos, pos + 1); // set will call merge
218 }
219
220
221 Change const & Changes::lookup(pos_type const pos) const
222 {
223         static Change const noChange = Change(Change::UNCHANGED);
224
225         ChangeTable::const_iterator it = table_.begin();
226         ChangeTable::const_iterator const end = table_.end();
227
228         for (; it != end; ++it) {
229                 if (it->range.contains(pos))
230                         return it->change;
231         }
232
233         return noChange;
234 }
235
236
237 bool Changes::isChanged(pos_type const start, pos_type const end) const
238 {
239         ChangeTable::const_iterator it = table_.begin();
240         ChangeTable::const_iterator const itend = table_.end();
241
242         for (; it != itend; ++it) {
243                 if (it->range.intersects(Range(start, end))) {
244                         LYXERR(Debug::CHANGES) << "found intersection of range ("
245                                 << start << ", " << end << ") with ("
246                                 << it->range.start << ", " << it->range.end
247                                 << ") of type " << it->change.type << endl;
248                         return true;
249                 }
250         }
251         return false;
252 }
253
254
255 void Changes::merge()
256 {
257         ChangeTable::iterator it = table_.begin();
258
259         while (it != table_.end()) {
260                 LYXERR(Debug::CHANGES) << "found change of type " << it->change.type
261                         << " and range (" << it->range.start << ", " << it->range.end
262                         << ")" << endl;
263
264                 if (it->range.start == it->range.end) {
265                         LYXERR(Debug::CHANGES) << "removing empty range for pos "
266                                 << it->range.start << endl;
267
268                         table_.erase(it);
269                         // start again
270                         it = table_.begin();
271                         continue;
272                 }
273
274                 if (it + 1 == table_.end())
275                         break;
276
277                 if (it->change.isSimilarTo((it + 1)->change) && it->range.end == (it + 1)->range.start) {
278                         LYXERR(Debug::CHANGES) << "merging ranges (" << it->range.start << ", "
279                                 << it->range.end << ") and (" << (it + 1)->range.start << ", "
280                                 << (it + 1)->range.end << ")" << endl;
281
282                         (it + 1)->range.start = it->range.start;
283                         (it + 1)->change.changetime = max(it->change.changetime,
284                                                           (it + 1)->change.changetime);
285                         table_.erase(it);
286                         // start again
287                         it = table_.begin();
288                         continue;
289                 }
290
291                 ++it;
292         }
293 }
294
295
296 int Changes::latexMarkChange(odocstream & os, BufferParams const & bparams,
297                              Change const & oldChange, Change const & change)
298 {
299         if (!bparams.outputChanges || oldChange == change)
300                 return 0;
301
302         int column = 0;
303
304         if (oldChange.type != Change::UNCHANGED) {
305                 os << '}'; // close \lyxadded or \lyxdeleted
306                 column++;
307         }
308
309         docstring chgTime;
310         chgTime += ctime(&change.changetime);
311         chgTime.erase(chgTime.end() - 1); // remove trailing '\n'
312
313         if (change.type == Change::DELETED) {
314                 docstring str = "\\lyxdeleted{" +
315                         bparams.authors().get(change.author).name() + "}{" +
316                         chgTime + "}{";
317                 os << str;
318                 column += str.size();
319         } else if (change.type == Change::INSERTED) {
320                 docstring str = "\\lyxadded{" +
321                         bparams.authors().get(change.author).name() + "}{" +
322                         chgTime + "}{";
323                 os << str;
324                 column += str.size();
325         }
326
327         return column;
328 }
329
330
331 void Changes::lyxMarkChange(std::ostream & os, int & column,
332                             Change const & old, Change const & change)
333 {
334         if (old == change)
335                 return;
336
337         column = 0;
338
339         switch (change.type) {
340                 case Change::UNCHANGED:
341                         os << "\n\\change_unchanged\n";
342                         break;
343
344                 case Change::DELETED: {
345                         os << "\n\\change_deleted " << change.author
346                                 << " " << change.changetime << "\n";
347                         break;
348                 }
349
350                 case Change::INSERTED: {
351                         os << "\n\\change_inserted " << change.author
352                                 << " " << change.changetime << "\n";
353                         break;
354                 }
355         }
356 }
357
358
359 void Changes::checkAuthors(AuthorList const & authorList)
360 {
361         ChangeTable::const_iterator it = table_.begin();
362         ChangeTable::const_iterator endit = table_.end();
363         for ( ; it != endit ; ++it) 
364                 if (it->change.type != Change::UNCHANGED)
365                         authorList.get(it->change.author).used(true);
366 }
367
368 } // namespace lyx