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
43using 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
59KDE4: Can we use QTemporaryFile here, instead of KTempFile?
60
61FIXME: 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
69FIXME: Terminating the history is not properly indicated
70 in the menu. We should throw a signal.
71
72FIXME: 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
89HistoryFile::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
101HistoryFile::~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.
110void 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
125void HistoryFile::unmap()
126{
127 int result = munmap( fileMap , length );
128 Q_ASSERT( result == 0 ); Q_UNUSED( result );
129
130 fileMap = 0;
131}
132
133bool HistoryFile::isMapped() const
134{
135 return (fileMap != 0);
136}
137
138void 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
152void 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
178int HistoryFile::len()
179{
180 return length;
181}
182
183
184// History Scroll abstract base class //////////////////////////////////////
185
186
187HistoryScroll::HistoryScroll(HistoryType* t)
188 : m_histType(t)
189{
190}
191
192HistoryScroll::~HistoryScroll()
193{
194 delete m_histType;
195}
196
197bool 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
215HistoryScrollFile::HistoryScrollFile(const QString &logFileName)
216 : HistoryScroll(new HistoryTypeFile(logFileName)),
217 m_logFileName(logFileName)
218{
219}
220
221HistoryScrollFile::~HistoryScrollFile()
222{
223}
224
225int HistoryScrollFile::getLines()
226{
227 return index.len() / sizeof(int);
228}
229
230int HistoryScrollFile::getLineLen(int lineno)
231{
232 return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character);
233}
234
235bool 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
245int 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
261void 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
266void HistoryScrollFile::addCells(const Character text[], int count)
267{
268 cells.add((unsigned char*)text,count*sizeof(Character));
269}
270
271void 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 //////////////////////////////////////
284HistoryScrollBuffer::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
294HistoryScrollBuffer::~HistoryScrollBuffer()
295{
296 delete[] _historyBuffer;
297}
298
299void 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}
313void 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
321void HistoryScrollBuffer::addLine(bool previousWrapped)
322{
323 _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped;
324}
325
326int HistoryScrollBuffer::getLines()
327{
328 return _usedLines;
329}
330
331int 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
345bool 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
358void 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
381void 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
402int 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
421HistoryScrollNone::HistoryScrollNone()
422 : HistoryScroll(new HistoryTypeNone())
423{
424}
425
426HistoryScrollNone::~HistoryScrollNone()
427{
428}
429
430bool HistoryScrollNone::hasScroll()
431{
432 return false;
433}
434
435int HistoryScrollNone::getLines()
436{
437 return 0;
438}
439
440int HistoryScrollNone::getLineLen(int)
441{
442 return 0;
443}
444
445bool HistoryScrollNone::isWrappedLine(int /*lineno*/)
446{
447 return false;
448}
449
450void HistoryScrollNone::getCells(int, int, int, Character [])
451{
452}
453
454void HistoryScrollNone::addCells(const Character [], int)
455{
456}
457
458void HistoryScrollNone::addLine(bool)
459{
460}
461
462// History Scroll BlockArray //////////////////////////////////////
463
464HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size)
465 : HistoryScroll(new HistoryTypeBlockArray(size))
466{
467 m_blockArray.setHistorySize(size); // nb. of lines.
468}
469
470HistoryScrollBlockArray::~HistoryScrollBlockArray()
471{
472}
473
474int HistoryScrollBlockArray::getLines()
475{
476 return m_lineLengths.count();
477}
478
479int HistoryScrollBlockArray::getLineLen(int lineno)
480{
481 if ( m_lineLengths.contains(lineno) )
482 return m_lineLengths[lineno];
483 else
484 return 0;
485}
486
487bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/)
488{
489 return false;
490}
491
492void 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
508void 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
529void HistoryScrollBlockArray::addLine(bool)
530{
531}
532
533////////////////////////////////////////////////////////////////
534// Compact History Scroll //////////////////////////////////////
535////////////////////////////////////////////////////////////////
536void* 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
549void CompactHistoryBlock::deallocate ( )
550{
551 allocCount--;
552 Q_ASSERT ( allocCount >= 0 );
553}
554
555void* 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
572void 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
596CompactHistoryBlockList::~CompactHistoryBlockList()
597{
598 qDeleteAll ( list.begin(), list.end() );
599 list.clear();
600}
601
602void* CompactHistoryLine::operator new (size_t size, CompactHistoryBlockList& blockList)
603{
604 return blockList.allocate(size);
605}
606
607CompactHistoryLine::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
668CompactHistoryLine::~CompactHistoryLine()
669{
670 //kDebug() << "~CHL";
671 if (length>0) {
672 blockList.deallocate(text);
673 blockList.deallocate(formatArray);
674 }
675 blockList.deallocate(this);
676}
677
678void 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
691void 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
702CompactHistoryScroll::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
711CompactHistoryScroll::~CompactHistoryScroll()
712{
713 qDeleteAll ( lines.begin(), lines.end() );
714 lines.clear();
715}
716
717void 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
729void 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
736void CompactHistoryScroll::addLine ( bool previousWrapped )
737{
738 CompactHistoryLine *line = lines.last();
739 //kDebug() << "last line at address " << line;
740 line->setWrapped(previousWrapped);
741}
742
743int CompactHistoryScroll::getLines()
744{
745 return lines.size();
746}
747
748int 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
757void 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
767void 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
777bool 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
788HistoryType::HistoryType()
789{
790}
791
792HistoryType::~HistoryType()
793{
794}
795
796//////////////////////////////
797
798HistoryTypeNone::HistoryTypeNone()
799{
800}
801
802bool HistoryTypeNone::isEnabled() const
803{
804 return false;
805}
806
807HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const
808{
809 delete old;
810 return new HistoryScrollNone();
811}
812
813int HistoryTypeNone::maximumLineCount() const
814{
815 return 0;
816}
817
818//////////////////////////////
819
820HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size)
821 : m_size(size)
822{
823}
824
825bool HistoryTypeBlockArray::isEnabled() const
826{
827 return true;
828}
829
830int HistoryTypeBlockArray::maximumLineCount() const
831{
832 return m_size;
833}
834
835HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const
836{
837 delete old;
838 return new HistoryScrollBlockArray(m_size);
839}
840
841
842//////////////////////////////
843
844HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines)
845 : m_nbLines(nbLines)
846{
847}
848
849bool HistoryTypeBuffer::isEnabled() const
850{
851 return true;
852}
853
854int HistoryTypeBuffer::maximumLineCount() const
855{
856 return m_nbLines;
857}
858
859HistoryScroll* 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
903HistoryTypeFile::HistoryTypeFile(const QString& fileName)
904 : m_fileName(fileName)
905{
906}
907
908bool HistoryTypeFile::isEnabled() const
909{
910 return true;
911}
912
913const QString& HistoryTypeFile::getFileName() const
914{
915 return m_fileName;
916}
917
918HistoryScroll* 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
950int HistoryTypeFile::maximumLineCount() const
951{
952 return 0;
953}
954
955//////////////////////////////
956
957CompactHistoryType::CompactHistoryType ( unsigned int nbLines )
958 : m_nbLines ( nbLines )
959{
960}
961
962bool CompactHistoryType::isEnabled() const
963{
964 return true;
965}
966
967int CompactHistoryType::maximumLineCount() const
968{
969 return m_nbLines;
970}
971
972HistoryScroll* 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