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