1 | /* |
---|
2 | This file is part of Konsole, an X terminal. |
---|
3 | Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> |
---|
4 | |
---|
5 | This program is free software; you can redistribute it and/or modify |
---|
6 | it under the terms of the GNU General Public License as published by |
---|
7 | the Free Software Foundation; either version 2 of the License, or |
---|
8 | (at your option) any later version. |
---|
9 | |
---|
10 | This program is distributed in the hope that it will be useful, |
---|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | GNU General Public License for more details. |
---|
14 | |
---|
15 | You should have received a copy of the GNU General Public License |
---|
16 | along with this program; if not, write to the Free Software |
---|
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
---|
18 | 02110-1301 USA. |
---|
19 | */ |
---|
20 | |
---|
21 | // Own |
---|
22 | #include "History.h" |
---|
23 | |
---|
24 | // System |
---|
25 | #include <algorithm> |
---|
26 | #include <iostream> |
---|
27 | #include <cstdlib> |
---|
28 | #include <cstdio> |
---|
29 | #include <sys/types.h> |
---|
30 | #include <sys/mman.h> |
---|
31 | #include <unistd.h> |
---|
32 | #include <cerrno> |
---|
33 | |
---|
34 | #include <QtDebug> |
---|
35 | |
---|
36 | // KDE |
---|
37 | //#include <kde_file.h> |
---|
38 | //#include <kdebug.h> |
---|
39 | |
---|
40 | // Reasonable line size |
---|
41 | #define LINE_SIZE 1024 |
---|
42 | #define KDE_lseek lseek |
---|
43 | |
---|
44 | using namespace Konsole; |
---|
45 | |
---|
46 | /* |
---|
47 | An arbitrary long scroll. |
---|
48 | |
---|
49 | One can modify the scroll only by adding either cells |
---|
50 | or newlines, but access it randomly. |
---|
51 | |
---|
52 | The model is that of an arbitrary wide typewriter scroll |
---|
53 | in that the scroll is a serie of lines and each line is |
---|
54 | a serie of cells with no overwriting permitted. |
---|
55 | |
---|
56 | The implementation provides arbitrary length and numbers |
---|
57 | of cells and line/column indexed read access to the scroll |
---|
58 | at constant costs. |
---|
59 | |
---|
60 | KDE4: Can we use QTemporaryFile here, instead of KTempFile? |
---|
61 | |
---|
62 | FIXME: some complain about the history buffer consuming the |
---|
63 | memory of their machines. This problem is critical |
---|
64 | since the history does not behave gracefully in cases |
---|
65 | where the memory is used up completely. |
---|
66 | |
---|
67 | I put in a workaround that should handle it problem |
---|
68 | now gracefully. I'm not satisfied with the solution. |
---|
69 | |
---|
70 | FIXME: Terminating the history is not properly indicated |
---|
71 | in the menu. We should throw a signal. |
---|
72 | |
---|
73 | FIXME: There is noticeable decrease in speed, also. Perhaps, |
---|
74 | there whole feature needs to be revisited therefore. |
---|
75 | Disadvantage of a more elaborated, say block-oriented |
---|
76 | scheme with wrap around would be it's complexity. |
---|
77 | */ |
---|
78 | |
---|
79 | //FIXME: temporary replacement for tmpfile |
---|
80 | // this is here one for debugging purpose. |
---|
81 | |
---|
82 | //#define tmpfile xTmpFile |
---|
83 | |
---|
84 | // History File /////////////////////////////////////////// |
---|
85 | |
---|
86 | /* |
---|
87 | A Row(X) data type which allows adding elements to the end. |
---|
88 | */ |
---|
89 | |
---|
90 | HistoryFile::HistoryFile() |
---|
91 | : ion(-1), |
---|
92 | length(0), |
---|
93 | fileMap(nullptr), |
---|
94 | readWriteBalance(0) |
---|
95 | { |
---|
96 | if (tmpFile.open()) |
---|
97 | { |
---|
98 | tmpFile.setAutoRemove(true); |
---|
99 | ion = tmpFile.handle(); |
---|
100 | } |
---|
101 | } |
---|
102 | |
---|
103 | HistoryFile::~HistoryFile() |
---|
104 | { |
---|
105 | if (fileMap) |
---|
106 | unmap(); |
---|
107 | } |
---|
108 | |
---|
109 | //TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large, |
---|
110 | //(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time, |
---|
111 | //to avoid this. |
---|
112 | void HistoryFile::map() |
---|
113 | { |
---|
114 | Q_ASSERT( fileMap == nullptr ); |
---|
115 | |
---|
116 | fileMap = (char*)mmap( nullptr , length , PROT_READ , MAP_PRIVATE , ion , 0 ); |
---|
117 | |
---|
118 | //if mmap'ing fails, fall back to the read-lseek combination |
---|
119 | if ( fileMap == MAP_FAILED ) |
---|
120 | { |
---|
121 | readWriteBalance = 0; |
---|
122 | fileMap = nullptr; |
---|
123 | //qDebug() << __FILE__ << __LINE__ << ": mmap'ing history failed. errno = " << errno; |
---|
124 | } |
---|
125 | } |
---|
126 | |
---|
127 | void HistoryFile::unmap() |
---|
128 | { |
---|
129 | int result = munmap( fileMap , length ); |
---|
130 | Q_ASSERT( result == 0 ); Q_UNUSED( result ); |
---|
131 | |
---|
132 | fileMap = nullptr; |
---|
133 | } |
---|
134 | |
---|
135 | bool HistoryFile::isMapped() const |
---|
136 | { |
---|
137 | return (fileMap != nullptr); |
---|
138 | } |
---|
139 | |
---|
140 | void HistoryFile::add(const unsigned char* bytes, int len) |
---|
141 | { |
---|
142 | if ( fileMap ) |
---|
143 | unmap(); |
---|
144 | |
---|
145 | readWriteBalance++; |
---|
146 | |
---|
147 | int rc = 0; |
---|
148 | |
---|
149 | rc = KDE_lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; } |
---|
150 | rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; } |
---|
151 | length += rc; |
---|
152 | } |
---|
153 | |
---|
154 | void HistoryFile::get(unsigned char* bytes, int len, int loc) |
---|
155 | { |
---|
156 | //count number of get() calls vs. number of add() calls. |
---|
157 | //If there are many more get() calls compared with add() |
---|
158 | //calls (decided by using MAP_THRESHOLD) then mmap the log |
---|
159 | //file to improve performance. |
---|
160 | readWriteBalance--; |
---|
161 | if ( !fileMap && readWriteBalance < MAP_THRESHOLD ) |
---|
162 | map(); |
---|
163 | |
---|
164 | if ( fileMap ) |
---|
165 | { |
---|
166 | for (int i=0;i<len;i++) |
---|
167 | bytes[i]=fileMap[loc+i]; |
---|
168 | } |
---|
169 | else |
---|
170 | { |
---|
171 | int rc = 0; |
---|
172 | |
---|
173 | if (loc < 0 || len < 0 || loc + len > length) |
---|
174 | fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc); |
---|
175 | rc = KDE_lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; } |
---|
176 | rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; } |
---|
177 | } |
---|
178 | } |
---|
179 | |
---|
180 | int HistoryFile::len() |
---|
181 | { |
---|
182 | return length; |
---|
183 | } |
---|
184 | |
---|
185 | |
---|
186 | // History Scroll abstract base class ////////////////////////////////////// |
---|
187 | |
---|
188 | |
---|
189 | HistoryScroll::HistoryScroll(HistoryType* t) |
---|
190 | : m_histType(t) |
---|
191 | { |
---|
192 | } |
---|
193 | |
---|
194 | HistoryScroll::~HistoryScroll() |
---|
195 | { |
---|
196 | delete m_histType; |
---|
197 | } |
---|
198 | |
---|
199 | bool HistoryScroll::hasScroll() |
---|
200 | { |
---|
201 | return true; |
---|
202 | } |
---|
203 | |
---|
204 | // History Scroll File ////////////////////////////////////// |
---|
205 | |
---|
206 | /* |
---|
207 | The history scroll makes a Row(Row(Cell)) from |
---|
208 | two history buffers. The index buffer contains |
---|
209 | start of line positions which refers to the cells |
---|
210 | buffer. |
---|
211 | |
---|
212 | Note that index[0] addresses the second line |
---|
213 | (line #1), while the first line (line #0) starts |
---|
214 | at 0 in cells. |
---|
215 | */ |
---|
216 | |
---|
217 | HistoryScrollFile::HistoryScrollFile(const QString &logFileName) |
---|
218 | : HistoryScroll(new HistoryTypeFile(logFileName)), |
---|
219 | m_logFileName(logFileName) |
---|
220 | { |
---|
221 | } |
---|
222 | |
---|
223 | HistoryScrollFile::~HistoryScrollFile() |
---|
224 | { |
---|
225 | } |
---|
226 | |
---|
227 | int HistoryScrollFile::getLines() |
---|
228 | { |
---|
229 | return index.len() / sizeof(int); |
---|
230 | } |
---|
231 | |
---|
232 | int HistoryScrollFile::getLineLen(int lineno) |
---|
233 | { |
---|
234 | return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character); |
---|
235 | } |
---|
236 | |
---|
237 | bool HistoryScrollFile::isWrappedLine(int lineno) |
---|
238 | { |
---|
239 | if (lineno>=0 && lineno <= getLines()) { |
---|
240 | unsigned char flag; |
---|
241 | lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char)); |
---|
242 | return flag; |
---|
243 | } |
---|
244 | return false; |
---|
245 | } |
---|
246 | |
---|
247 | int HistoryScrollFile::startOfLine(int lineno) |
---|
248 | { |
---|
249 | if (lineno <= 0) return 0; |
---|
250 | if (lineno <= getLines()) |
---|
251 | { |
---|
252 | |
---|
253 | if (!index.isMapped()) |
---|
254 | index.map(); |
---|
255 | |
---|
256 | int res = 0; |
---|
257 | index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int)); |
---|
258 | return res; |
---|
259 | } |
---|
260 | return cells.len(); |
---|
261 | } |
---|
262 | |
---|
263 | void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[]) |
---|
264 | { |
---|
265 | cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character)); |
---|
266 | } |
---|
267 | |
---|
268 | void HistoryScrollFile::addCells(const Character text[], int count) |
---|
269 | { |
---|
270 | cells.add((unsigned char*)text,count*sizeof(Character)); |
---|
271 | } |
---|
272 | |
---|
273 | void HistoryScrollFile::addLine(bool previousWrapped) |
---|
274 | { |
---|
275 | if (index.isMapped()) |
---|
276 | index.unmap(); |
---|
277 | |
---|
278 | int locn = cells.len(); |
---|
279 | index.add((unsigned char*)&locn,sizeof(int)); |
---|
280 | unsigned char flags = previousWrapped ? 0x01 : 0x00; |
---|
281 | lineflags.add((unsigned char*)&flags,sizeof(unsigned char)); |
---|
282 | } |
---|
283 | |
---|
284 | |
---|
285 | // History Scroll Buffer ////////////////////////////////////// |
---|
286 | HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount) |
---|
287 | : HistoryScroll(new HistoryTypeBuffer(maxLineCount)) |
---|
288 | ,_historyBuffer() |
---|
289 | ,_maxLineCount(0) |
---|
290 | ,_usedLines(0) |
---|
291 | ,_head(0) |
---|
292 | { |
---|
293 | setMaxNbLines(maxLineCount); |
---|
294 | } |
---|
295 | |
---|
296 | HistoryScrollBuffer::~HistoryScrollBuffer() |
---|
297 | { |
---|
298 | delete[] _historyBuffer; |
---|
299 | } |
---|
300 | |
---|
301 | void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells) |
---|
302 | { |
---|
303 | _head++; |
---|
304 | if ( _usedLines < _maxLineCount ) |
---|
305 | _usedLines++; |
---|
306 | |
---|
307 | if ( _head >= _maxLineCount ) |
---|
308 | { |
---|
309 | _head = 0; |
---|
310 | } |
---|
311 | |
---|
312 | _historyBuffer[bufferIndex(_usedLines-1)] = cells; |
---|
313 | _wrappedLine[bufferIndex(_usedLines-1)] = false; |
---|
314 | } |
---|
315 | void HistoryScrollBuffer::addCells(const Character a[], int count) |
---|
316 | { |
---|
317 | HistoryLine newLine(count); |
---|
318 | std::copy(a,a+count,newLine.begin()); |
---|
319 | |
---|
320 | addCellsVector(newLine); |
---|
321 | } |
---|
322 | |
---|
323 | void HistoryScrollBuffer::addLine(bool previousWrapped) |
---|
324 | { |
---|
325 | _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped; |
---|
326 | } |
---|
327 | |
---|
328 | int HistoryScrollBuffer::getLines() |
---|
329 | { |
---|
330 | return _usedLines; |
---|
331 | } |
---|
332 | |
---|
333 | int HistoryScrollBuffer::getLineLen(int lineNumber) |
---|
334 | { |
---|
335 | Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); |
---|
336 | |
---|
337 | if ( lineNumber < _usedLines ) |
---|
338 | { |
---|
339 | return _historyBuffer[bufferIndex(lineNumber)].size(); |
---|
340 | } |
---|
341 | else |
---|
342 | { |
---|
343 | return 0; |
---|
344 | } |
---|
345 | } |
---|
346 | |
---|
347 | bool HistoryScrollBuffer::isWrappedLine(int lineNumber) |
---|
348 | { |
---|
349 | Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); |
---|
350 | |
---|
351 | if (lineNumber < _usedLines) |
---|
352 | { |
---|
353 | //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)]; |
---|
354 | return _wrappedLine[bufferIndex(lineNumber)]; |
---|
355 | } |
---|
356 | else |
---|
357 | return false; |
---|
358 | } |
---|
359 | |
---|
360 | void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character buffer[]) |
---|
361 | { |
---|
362 | if ( count == 0 ) return; |
---|
363 | |
---|
364 | Q_ASSERT( lineNumber < _maxLineCount ); |
---|
365 | |
---|
366 | if (lineNumber >= _usedLines) |
---|
367 | { |
---|
368 | memset(static_cast<void*>(buffer), 0, count * sizeof(Character)); |
---|
369 | return; |
---|
370 | } |
---|
371 | |
---|
372 | const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)]; |
---|
373 | |
---|
374 | //kDebug() << "startCol " << startColumn; |
---|
375 | //kDebug() << "line.size() " << line.size(); |
---|
376 | //kDebug() << "count " << count; |
---|
377 | |
---|
378 | Q_ASSERT( startColumn <= line.size() - count ); |
---|
379 | |
---|
380 | memcpy(buffer, line.constData() + startColumn , count * sizeof(Character)); |
---|
381 | } |
---|
382 | |
---|
383 | void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount) |
---|
384 | { |
---|
385 | HistoryLine* oldBuffer = _historyBuffer; |
---|
386 | HistoryLine* newBuffer = new HistoryLine[lineCount]; |
---|
387 | |
---|
388 | for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ ) |
---|
389 | { |
---|
390 | newBuffer[i] = oldBuffer[bufferIndex(i)]; |
---|
391 | } |
---|
392 | |
---|
393 | _usedLines = qMin(_usedLines,(int)lineCount); |
---|
394 | _maxLineCount = lineCount; |
---|
395 | _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1; |
---|
396 | |
---|
397 | _historyBuffer = newBuffer; |
---|
398 | delete[] oldBuffer; |
---|
399 | |
---|
400 | _wrappedLine.resize(lineCount); |
---|
401 | dynamic_cast<HistoryTypeBuffer*>(m_histType)->m_nbLines = lineCount; |
---|
402 | } |
---|
403 | |
---|
404 | int HistoryScrollBuffer::bufferIndex(int lineNumber) const |
---|
405 | { |
---|
406 | Q_ASSERT( lineNumber >= 0 ); |
---|
407 | Q_ASSERT( lineNumber < _maxLineCount ); |
---|
408 | Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head ); |
---|
409 | |
---|
410 | if ( _usedLines == _maxLineCount ) |
---|
411 | { |
---|
412 | return (_head+lineNumber+1) % _maxLineCount; |
---|
413 | } |
---|
414 | else |
---|
415 | { |
---|
416 | return lineNumber; |
---|
417 | } |
---|
418 | } |
---|
419 | |
---|
420 | |
---|
421 | // History Scroll None ////////////////////////////////////// |
---|
422 | |
---|
423 | HistoryScrollNone::HistoryScrollNone() |
---|
424 | : HistoryScroll(new HistoryTypeNone()) |
---|
425 | { |
---|
426 | } |
---|
427 | |
---|
428 | HistoryScrollNone::~HistoryScrollNone() |
---|
429 | { |
---|
430 | } |
---|
431 | |
---|
432 | bool HistoryScrollNone::hasScroll() |
---|
433 | { |
---|
434 | return false; |
---|
435 | } |
---|
436 | |
---|
437 | int HistoryScrollNone::getLines() |
---|
438 | { |
---|
439 | return 0; |
---|
440 | } |
---|
441 | |
---|
442 | int HistoryScrollNone::getLineLen(int) |
---|
443 | { |
---|
444 | return 0; |
---|
445 | } |
---|
446 | |
---|
447 | bool HistoryScrollNone::isWrappedLine(int /*lineno*/) |
---|
448 | { |
---|
449 | return false; |
---|
450 | } |
---|
451 | |
---|
452 | void HistoryScrollNone::getCells(int, int, int, Character []) |
---|
453 | { |
---|
454 | } |
---|
455 | |
---|
456 | void HistoryScrollNone::addCells(const Character [], int) |
---|
457 | { |
---|
458 | } |
---|
459 | |
---|
460 | void HistoryScrollNone::addLine(bool) |
---|
461 | { |
---|
462 | } |
---|
463 | |
---|
464 | // History Scroll BlockArray ////////////////////////////////////// |
---|
465 | |
---|
466 | HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size) |
---|
467 | : HistoryScroll(new HistoryTypeBlockArray(size)) |
---|
468 | { |
---|
469 | m_blockArray.setHistorySize(size); // nb. of lines. |
---|
470 | } |
---|
471 | |
---|
472 | HistoryScrollBlockArray::~HistoryScrollBlockArray() |
---|
473 | { |
---|
474 | } |
---|
475 | |
---|
476 | int HistoryScrollBlockArray::getLines() |
---|
477 | { |
---|
478 | return m_lineLengths.count(); |
---|
479 | } |
---|
480 | |
---|
481 | int HistoryScrollBlockArray::getLineLen(int lineno) |
---|
482 | { |
---|
483 | if ( m_lineLengths.contains(lineno) ) |
---|
484 | return m_lineLengths[lineno]; |
---|
485 | else |
---|
486 | return 0; |
---|
487 | } |
---|
488 | |
---|
489 | bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/) |
---|
490 | { |
---|
491 | return false; |
---|
492 | } |
---|
493 | |
---|
494 | void HistoryScrollBlockArray::getCells(int lineno, int colno, |
---|
495 | int count, Character res[]) |
---|
496 | { |
---|
497 | if (!count) return; |
---|
498 | |
---|
499 | const Block *b = m_blockArray.at(lineno); |
---|
500 | |
---|
501 | if (!b) { |
---|
502 | memset(static_cast<void*>(res), 0, count * sizeof(Character)); // still better than random data |
---|
503 | return; |
---|
504 | } |
---|
505 | |
---|
506 | Q_ASSERT(((colno + count) * sizeof(Character)) < ENTRIES); |
---|
507 | memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character)); |
---|
508 | } |
---|
509 | |
---|
510 | void HistoryScrollBlockArray::addCells(const Character a[], int count) |
---|
511 | { |
---|
512 | Block *b = m_blockArray.lastBlock(); |
---|
513 | |
---|
514 | if (!b) return; |
---|
515 | |
---|
516 | // put cells in block's data |
---|
517 | Q_ASSERT((count * sizeof(Character)) < ENTRIES); |
---|
518 | |
---|
519 | memset(b->data, 0, sizeof(b->data)); |
---|
520 | |
---|
521 | memcpy(b->data, a, count * sizeof(Character)); |
---|
522 | b->size = count * sizeof(Character); |
---|
523 | |
---|
524 | size_t res = m_blockArray.newBlock(); |
---|
525 | Q_ASSERT(res > 0); |
---|
526 | Q_UNUSED( res ); |
---|
527 | |
---|
528 | m_lineLengths.insert(m_blockArray.getCurrent(), count); |
---|
529 | } |
---|
530 | |
---|
531 | void HistoryScrollBlockArray::addLine(bool) |
---|
532 | { |
---|
533 | } |
---|
534 | |
---|
535 | //////////////////////////////////////////////////////////////// |
---|
536 | // Compact History Scroll ////////////////////////////////////// |
---|
537 | //////////////////////////////////////////////////////////////// |
---|
538 | void* CompactHistoryBlock::allocate ( size_t length ) |
---|
539 | { |
---|
540 | Q_ASSERT ( length > 0 ); |
---|
541 | if ( tail-blockStart+length > blockLength ) |
---|
542 | return nullptr; |
---|
543 | |
---|
544 | void* block = tail; |
---|
545 | tail += length; |
---|
546 | //kDebug() << "allocated " << length << " bytes at address " << block; |
---|
547 | allocCount++; |
---|
548 | return block; |
---|
549 | } |
---|
550 | |
---|
551 | void CompactHistoryBlock::deallocate ( ) |
---|
552 | { |
---|
553 | allocCount--; |
---|
554 | Q_ASSERT ( allocCount >= 0 ); |
---|
555 | } |
---|
556 | |
---|
557 | void* CompactHistoryBlockList::allocate(size_t size) |
---|
558 | { |
---|
559 | CompactHistoryBlock* block; |
---|
560 | if ( list.isEmpty() || list.last()->remaining() < size) |
---|
561 | { |
---|
562 | block = new CompactHistoryBlock(); |
---|
563 | list.append ( block ); |
---|
564 | //kDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size(); |
---|
565 | } |
---|
566 | else |
---|
567 | { |
---|
568 | block = list.last(); |
---|
569 | //kDebug() << "old block used, remaining " << block->remaining(); |
---|
570 | } |
---|
571 | return block->allocate(size); |
---|
572 | } |
---|
573 | |
---|
574 | void CompactHistoryBlockList::deallocate(void* ptr) |
---|
575 | { |
---|
576 | Q_ASSERT( !list.isEmpty()); |
---|
577 | |
---|
578 | int i=0; |
---|
579 | CompactHistoryBlock *block = list.at(i); |
---|
580 | while ( i<list.size() && !block->contains(ptr) ) |
---|
581 | { |
---|
582 | i++; |
---|
583 | block=list.at(i); |
---|
584 | } |
---|
585 | |
---|
586 | Q_ASSERT( i<list.size() ); |
---|
587 | |
---|
588 | block->deallocate(); |
---|
589 | |
---|
590 | if (!block->isInUse()) |
---|
591 | { |
---|
592 | list.removeAt(i); |
---|
593 | delete block; |
---|
594 | //kDebug() << "block deleted, new size = " << list.size(); |
---|
595 | } |
---|
596 | } |
---|
597 | |
---|
598 | CompactHistoryBlockList::~CompactHistoryBlockList() |
---|
599 | { |
---|
600 | qDeleteAll ( list.begin(), list.end() ); |
---|
601 | list.clear(); |
---|
602 | } |
---|
603 | |
---|
604 | void* CompactHistoryLine::operator new (size_t size, CompactHistoryBlockList& blockList) |
---|
605 | { |
---|
606 | return blockList.allocate(size); |
---|
607 | } |
---|
608 | |
---|
609 | CompactHistoryLine::CompactHistoryLine ( const TextLine& line, CompactHistoryBlockList& bList ) |
---|
610 | : blockList(bList), |
---|
611 | formatLength(0) |
---|
612 | { |
---|
613 | length=line.size(); |
---|
614 | |
---|
615 | if (!line.empty()) { |
---|
616 | formatLength=1; |
---|
617 | int k=1; |
---|
618 | |
---|
619 | // count number of different formats in this text line |
---|
620 | Character c = line[0]; |
---|
621 | while ( k<length ) |
---|
622 | { |
---|
623 | if ( !(line[k].equalsFormat(c))) |
---|
624 | { |
---|
625 | formatLength++; // format change detected |
---|
626 | c=line[k]; |
---|
627 | } |
---|
628 | k++; |
---|
629 | } |
---|
630 | |
---|
631 | //kDebug() << "number of different formats in string: " << formatLength; |
---|
632 | formatArray = (CharacterFormat*) blockList.allocate(sizeof(CharacterFormat)*formatLength); |
---|
633 | Q_ASSERT (formatArray!=nullptr); |
---|
634 | text = (quint16*) blockList.allocate(sizeof(quint16)*line.size()); |
---|
635 | Q_ASSERT (text!=nullptr); |
---|
636 | |
---|
637 | length=line.size(); |
---|
638 | wrapped=false; |
---|
639 | |
---|
640 | // record formats and their positions in the format array |
---|
641 | c=line[0]; |
---|
642 | formatArray[0].setFormat ( c ); |
---|
643 | formatArray[0].startPos=0; // there's always at least 1 format (for the entire line, unless a change happens) |
---|
644 | |
---|
645 | k=1; // look for possible format changes |
---|
646 | int j=1; |
---|
647 | while ( k<length && j<formatLength ) |
---|
648 | { |
---|
649 | if (!(line[k].equalsFormat(c))) |
---|
650 | { |
---|
651 | c=line[k]; |
---|
652 | formatArray[j].setFormat(c); |
---|
653 | formatArray[j].startPos=k; |
---|
654 | //kDebug() << "format entry " << j << " at pos " << formatArray[j].startPos << " " << &(formatArray[j].startPos) ; |
---|
655 | j++; |
---|
656 | } |
---|
657 | k++; |
---|
658 | } |
---|
659 | |
---|
660 | // copy character values |
---|
661 | for ( int i=0; i<line.size(); i++ ) |
---|
662 | { |
---|
663 | text[i]=line[i].character; |
---|
664 | //kDebug() << "char " << i << " at mem " << &(text[i]); |
---|
665 | } |
---|
666 | } |
---|
667 | //kDebug() << "line created, length " << length << " at " << &(length); |
---|
668 | } |
---|
669 | |
---|
670 | CompactHistoryLine::~CompactHistoryLine() |
---|
671 | { |
---|
672 | //kDebug() << "~CHL"; |
---|
673 | if (length>0) { |
---|
674 | blockList.deallocate(text); |
---|
675 | blockList.deallocate(formatArray); |
---|
676 | } |
---|
677 | blockList.deallocate(this); |
---|
678 | } |
---|
679 | |
---|
680 | void CompactHistoryLine::getCharacter ( int index, Character &r ) |
---|
681 | { |
---|
682 | Q_ASSERT ( index < length ); |
---|
683 | int formatPos=0; |
---|
684 | while ( ( formatPos+1 ) < formatLength && index >= formatArray[formatPos+1].startPos ) |
---|
685 | formatPos++; |
---|
686 | |
---|
687 | r.character=text[index]; |
---|
688 | r.rendition = formatArray[formatPos].rendition; |
---|
689 | r.foregroundColor = formatArray[formatPos].fgColor; |
---|
690 | r.backgroundColor = formatArray[formatPos].bgColor; |
---|
691 | } |
---|
692 | |
---|
693 | void CompactHistoryLine::getCharacters ( Character* array, int length, int startColumn ) |
---|
694 | { |
---|
695 | Q_ASSERT ( startColumn >= 0 && length >= 0 ); |
---|
696 | Q_ASSERT ( startColumn+length <= ( int ) getLength() ); |
---|
697 | |
---|
698 | for ( int i=startColumn; i<length+startColumn; i++ ) |
---|
699 | { |
---|
700 | getCharacter ( i, array[i-startColumn] ); |
---|
701 | } |
---|
702 | } |
---|
703 | |
---|
704 | CompactHistoryScroll::CompactHistoryScroll ( unsigned int maxLineCount ) |
---|
705 | : HistoryScroll ( new CompactHistoryType ( maxLineCount ) ) |
---|
706 | ,lines() |
---|
707 | ,blockList() |
---|
708 | { |
---|
709 | //kDebug() << "scroll of length " << maxLineCount << " created"; |
---|
710 | setMaxNbLines ( maxLineCount ); |
---|
711 | } |
---|
712 | |
---|
713 | CompactHistoryScroll::~CompactHistoryScroll() |
---|
714 | { |
---|
715 | qDeleteAll ( lines.begin(), lines.end() ); |
---|
716 | lines.clear(); |
---|
717 | } |
---|
718 | |
---|
719 | void CompactHistoryScroll::addCellsVector ( const TextLine& cells ) |
---|
720 | { |
---|
721 | CompactHistoryLine *line; |
---|
722 | line = new(blockList) CompactHistoryLine ( cells, blockList ); |
---|
723 | |
---|
724 | if ( lines.size() > ( int ) _maxLineCount ) |
---|
725 | { |
---|
726 | delete lines.takeAt ( 0 ); |
---|
727 | } |
---|
728 | lines.append ( line ); |
---|
729 | } |
---|
730 | |
---|
731 | void CompactHistoryScroll::addCells ( const Character a[], int count ) |
---|
732 | { |
---|
733 | TextLine newLine ( count ); |
---|
734 | std::copy ( a,a+count,newLine.begin() ); |
---|
735 | addCellsVector ( newLine ); |
---|
736 | } |
---|
737 | |
---|
738 | void CompactHistoryScroll::addLine ( bool previousWrapped ) |
---|
739 | { |
---|
740 | CompactHistoryLine *line = lines.last(); |
---|
741 | //kDebug() << "last line at address " << line; |
---|
742 | line->setWrapped(previousWrapped); |
---|
743 | } |
---|
744 | |
---|
745 | int CompactHistoryScroll::getLines() |
---|
746 | { |
---|
747 | return lines.size(); |
---|
748 | } |
---|
749 | |
---|
750 | int CompactHistoryScroll::getLineLen ( int lineNumber ) |
---|
751 | { |
---|
752 | Q_ASSERT ( lineNumber >= 0 && lineNumber < lines.size() ); |
---|
753 | CompactHistoryLine* line = lines[lineNumber]; |
---|
754 | //kDebug() << "request for line at address " << line; |
---|
755 | return line->getLength(); |
---|
756 | } |
---|
757 | |
---|
758 | |
---|
759 | void CompactHistoryScroll::getCells ( int lineNumber, int startColumn, int count, Character buffer[] ) |
---|
760 | { |
---|
761 | if ( count == 0 ) return; |
---|
762 | Q_ASSERT ( lineNumber < lines.size() ); |
---|
763 | CompactHistoryLine* line = lines[lineNumber]; |
---|
764 | Q_ASSERT ( startColumn >= 0 ); |
---|
765 | Q_ASSERT ( (unsigned int)startColumn <= line->getLength() - count ); |
---|
766 | line->getCharacters ( buffer, count, startColumn ); |
---|
767 | } |
---|
768 | |
---|
769 | void CompactHistoryScroll::setMaxNbLines ( unsigned int lineCount ) |
---|
770 | { |
---|
771 | _maxLineCount = lineCount; |
---|
772 | |
---|
773 | while (lines.size() > (int) lineCount) { |
---|
774 | delete lines.takeAt(0); |
---|
775 | } |
---|
776 | //kDebug() << "set max lines to: " << _maxLineCount; |
---|
777 | } |
---|
778 | |
---|
779 | bool CompactHistoryScroll::isWrappedLine ( int lineNumber ) |
---|
780 | { |
---|
781 | Q_ASSERT ( lineNumber < lines.size() ); |
---|
782 | return lines[lineNumber]->isWrapped(); |
---|
783 | } |
---|
784 | |
---|
785 | |
---|
786 | ////////////////////////////////////////////////////////////////////// |
---|
787 | // History Types |
---|
788 | ////////////////////////////////////////////////////////////////////// |
---|
789 | |
---|
790 | HistoryType::HistoryType() |
---|
791 | { |
---|
792 | } |
---|
793 | |
---|
794 | HistoryType::~HistoryType() |
---|
795 | { |
---|
796 | } |
---|
797 | |
---|
798 | ////////////////////////////// |
---|
799 | |
---|
800 | HistoryTypeNone::HistoryTypeNone() |
---|
801 | { |
---|
802 | } |
---|
803 | |
---|
804 | bool HistoryTypeNone::isEnabled() const |
---|
805 | { |
---|
806 | return false; |
---|
807 | } |
---|
808 | |
---|
809 | HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const |
---|
810 | { |
---|
811 | delete old; |
---|
812 | return new HistoryScrollNone(); |
---|
813 | } |
---|
814 | |
---|
815 | int HistoryTypeNone::maximumLineCount() const |
---|
816 | { |
---|
817 | return 0; |
---|
818 | } |
---|
819 | |
---|
820 | ////////////////////////////// |
---|
821 | |
---|
822 | HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size) |
---|
823 | : m_size(size) |
---|
824 | { |
---|
825 | } |
---|
826 | |
---|
827 | bool HistoryTypeBlockArray::isEnabled() const |
---|
828 | { |
---|
829 | return true; |
---|
830 | } |
---|
831 | |
---|
832 | int HistoryTypeBlockArray::maximumLineCount() const |
---|
833 | { |
---|
834 | return m_size; |
---|
835 | } |
---|
836 | |
---|
837 | HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const |
---|
838 | { |
---|
839 | delete old; |
---|
840 | return new HistoryScrollBlockArray(m_size); |
---|
841 | } |
---|
842 | |
---|
843 | |
---|
844 | ////////////////////////////// |
---|
845 | |
---|
846 | HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines) |
---|
847 | : m_nbLines(nbLines) |
---|
848 | { |
---|
849 | } |
---|
850 | |
---|
851 | bool HistoryTypeBuffer::isEnabled() const |
---|
852 | { |
---|
853 | return true; |
---|
854 | } |
---|
855 | |
---|
856 | int HistoryTypeBuffer::maximumLineCount() const |
---|
857 | { |
---|
858 | return m_nbLines; |
---|
859 | } |
---|
860 | |
---|
861 | HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const |
---|
862 | { |
---|
863 | if (old) |
---|
864 | { |
---|
865 | HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old); |
---|
866 | if (oldBuffer) |
---|
867 | { |
---|
868 | oldBuffer->setMaxNbLines(m_nbLines); |
---|
869 | return oldBuffer; |
---|
870 | } |
---|
871 | |
---|
872 | HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines); |
---|
873 | int lines = old->getLines(); |
---|
874 | int startLine = 0; |
---|
875 | if (lines > (int) m_nbLines) |
---|
876 | startLine = lines - m_nbLines; |
---|
877 | |
---|
878 | Character line[LINE_SIZE]; |
---|
879 | for(int i = startLine; i < lines; i++) |
---|
880 | { |
---|
881 | int size = old->getLineLen(i); |
---|
882 | if (size > LINE_SIZE) |
---|
883 | { |
---|
884 | Character *tmp_line = new Character[size]; |
---|
885 | old->getCells(i, 0, size, tmp_line); |
---|
886 | newScroll->addCells(tmp_line, size); |
---|
887 | newScroll->addLine(old->isWrappedLine(i)); |
---|
888 | delete [] tmp_line; |
---|
889 | } |
---|
890 | else |
---|
891 | { |
---|
892 | old->getCells(i, 0, size, line); |
---|
893 | newScroll->addCells(line, size); |
---|
894 | newScroll->addLine(old->isWrappedLine(i)); |
---|
895 | } |
---|
896 | } |
---|
897 | delete old; |
---|
898 | return newScroll; |
---|
899 | } |
---|
900 | return new HistoryScrollBuffer(m_nbLines); |
---|
901 | } |
---|
902 | |
---|
903 | ////////////////////////////// |
---|
904 | |
---|
905 | HistoryTypeFile::HistoryTypeFile(const QString& fileName) |
---|
906 | : m_fileName(fileName) |
---|
907 | { |
---|
908 | } |
---|
909 | |
---|
910 | bool HistoryTypeFile::isEnabled() const |
---|
911 | { |
---|
912 | return true; |
---|
913 | } |
---|
914 | |
---|
915 | const QString& HistoryTypeFile::getFileName() const |
---|
916 | { |
---|
917 | return m_fileName; |
---|
918 | } |
---|
919 | |
---|
920 | HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const |
---|
921 | { |
---|
922 | if (dynamic_cast<HistoryFile *>(old)) |
---|
923 | return old; // Unchanged. |
---|
924 | |
---|
925 | HistoryScroll *newScroll = new HistoryScrollFile(m_fileName); |
---|
926 | |
---|
927 | Character line[LINE_SIZE]; |
---|
928 | int lines = (old != nullptr) ? old->getLines() : 0; |
---|
929 | for(int i = 0; i < lines; i++) |
---|
930 | { |
---|
931 | int size = old->getLineLen(i); |
---|
932 | if (size > LINE_SIZE) |
---|
933 | { |
---|
934 | Character *tmp_line = new Character[size]; |
---|
935 | old->getCells(i, 0, size, tmp_line); |
---|
936 | newScroll->addCells(tmp_line, size); |
---|
937 | newScroll->addLine(old->isWrappedLine(i)); |
---|
938 | delete [] tmp_line; |
---|
939 | } |
---|
940 | else |
---|
941 | { |
---|
942 | old->getCells(i, 0, size, line); |
---|
943 | newScroll->addCells(line, size); |
---|
944 | newScroll->addLine(old->isWrappedLine(i)); |
---|
945 | } |
---|
946 | } |
---|
947 | |
---|
948 | delete old; |
---|
949 | return newScroll; |
---|
950 | } |
---|
951 | |
---|
952 | int HistoryTypeFile::maximumLineCount() const |
---|
953 | { |
---|
954 | return 0; |
---|
955 | } |
---|
956 | |
---|
957 | ////////////////////////////// |
---|
958 | |
---|
959 | CompactHistoryType::CompactHistoryType ( unsigned int nbLines ) |
---|
960 | : m_nbLines ( nbLines ) |
---|
961 | { |
---|
962 | } |
---|
963 | |
---|
964 | bool CompactHistoryType::isEnabled() const |
---|
965 | { |
---|
966 | return true; |
---|
967 | } |
---|
968 | |
---|
969 | int CompactHistoryType::maximumLineCount() const |
---|
970 | { |
---|
971 | return m_nbLines; |
---|
972 | } |
---|
973 | |
---|
974 | HistoryScroll* CompactHistoryType::scroll ( HistoryScroll *old ) const |
---|
975 | { |
---|
976 | if ( old ) |
---|
977 | { |
---|
978 | CompactHistoryScroll *oldBuffer = dynamic_cast<CompactHistoryScroll*> ( old ); |
---|
979 | if ( oldBuffer ) |
---|
980 | { |
---|
981 | oldBuffer->setMaxNbLines ( m_nbLines ); |
---|
982 | return oldBuffer; |
---|
983 | } |
---|
984 | delete old; |
---|
985 | } |
---|
986 | return new CompactHistoryScroll ( m_nbLines ); |
---|
987 | } |
---|