Commit 82ac07fe authored by Robert Knight's avatar Robert Knight
Browse files

Change the character -> text decoding API so that Konsole can produce better...

Change the character -> text decoding API so that Konsole can produce better quality HTML when output is saved.  Fix crashing when resizing display introduced in yesterday's scrolling changes and add assertions to catch invalid memory accesses.

svn path=/trunk/KDE/kdebase/apps/konsole/; revision=669305
parent da548034
......@@ -407,12 +407,11 @@ void Emulation::clearSelection() {
#endif
void Emulation::writeToStream(QTextStream* stream ,
TerminalCharacterDecoder* _decoder ,
void Emulation::writeToStream( TerminalCharacterDecoder* _decoder ,
int startLine ,
int endLine)
{
_currentScreen->writeToStream(stream,_decoder,startLine,endLine);
_currentScreen->writeToStream(_decoder,startLine,endLine);
}
int Emulation::lineCount()
......
......@@ -162,13 +162,12 @@ public:
* into @p stream, using @p decoder to convert the terminal
* characters into text.
*
* @param stream The text stream into which the output history will be written.
* @param decoder A decoder which converts lines of terminal characters with
* appearance attributes into output text. PlainTextDecoder is the most commonly
* used decoder.
* @param startLine The first
*/
virtual void writeToStream(QTextStream* stream,TerminalCharacterDecoder* decoder,int startLine,int endLine);
virtual void writeToStream(TerminalCharacterDecoder* decoder,int startLine,int endLine);
/** Returns the codec used to decode incoming characters. See setCodec() */
......
......@@ -152,10 +152,12 @@ void TerminalImageFilterChain::setImage(const Character* const image , int lines
_linePositions = newLinePositions;
QTextStream lineStream(_buffer);
decoder.begin(&lineStream);
for (int i=0 ; i < lines ; i++)
{
_linePositions->append(_buffer->length());
decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT,&lineStream);
decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT);
// pretend that each line ends with a newline character.
// this prevents a link that occurs at the end of one line
......@@ -169,7 +171,7 @@ void TerminalImageFilterChain::setImage(const Character* const image , int lines
// lines
lineStream << QChar('\n');
}
decoder.end();
}
Filter::Filter() :
......
......@@ -1338,7 +1338,9 @@ QString Screen::selectedText(bool preserve_line_breaks)
QTextStream stream(&result, QIODevice::ReadWrite);
PlainTextDecoder decoder;
writeSelectionToStream(&stream,&decoder);
decoder.begin(&stream);
writeSelectionToStream(&decoder);
decoder.end();
return result;
}
......@@ -1377,7 +1379,7 @@ static QString makeString(int *m, int d, bool stripTrailingSpaces)
}*/
void Screen::writeSelectionToStream(QTextStream* stream , TerminalCharacterDecoder* decoder)
void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder)
{
int top = sel_TL / columns;
int left = sel_TL % columns;
......@@ -1396,16 +1398,17 @@ void Screen::writeSelectionToStream(QTextStream* stream , TerminalCharacterDecod
int count = -1;
if ( y == bottom) count = right - start + 1;
copyLineToStream( y,start,count,stream,decoder );
if (y != bottom)
*stream << '\n';
const bool appendNewLine = ( y != bottom );
copyLineToStream( y,start,count,decoder , appendNewLine );
}
}
void Screen::copyLineToStream(int line , int start, int count,
QTextStream* stream, TerminalCharacterDecoder* decoder)
void Screen::copyLineToStream(int line ,
int start,
int count,
TerminalCharacterDecoder* decoder,
bool appendNewLine)
{
//buffer to hold characters for decoding
//the buffer is static to avoid initialising every
......@@ -1464,9 +1467,16 @@ void Screen::copyLineToStream(int line , int start, int count,
count--;
else
break;
// add new line at end
if ( appendNewLine && (count+1 < MAX_CHARS) )
{
characterBuffer[count] = '\n';
count++;
}
//decode line and write to text stream
decoder->decodeLine( (Character*) characterBuffer , count, LINE_DEFAULT , stream);
decoder->decodeLine( (Character*) characterBuffer , count, LINE_DEFAULT );
}
// Method below has been removed because of its reliance on 'histCursor'
......@@ -1484,12 +1494,12 @@ void Screen::copyLineToStream(int line , int start, int count,
clearSelection();
}*/
void Screen::writeToStream(QTextStream* stream, TerminalCharacterDecoder* decoder, int from, int to)
void Screen::writeToStream(TerminalCharacterDecoder* decoder, int from, int to)
{
sel_begin = loc(0,from);
sel_TL = sel_begin;
sel_BR = loc(columns-1,to);
writeSelectionToStream(stream,decoder);
writeSelectionToStream(decoder);
clearSelection();
}
......
......@@ -284,25 +284,23 @@ public: // these are all `Screen' operations
/**
* Copies part of the output to a stream.
*
* @param stream An output stream which receives the text
* @param decoder A decoder which coverts terminal characters into text
* @param from The first line in the history to retrieve
* @param to The last line in the history to retrieve
*/
void writeToStream(QTextStream* stream , TerminalCharacterDecoder* decoder, int from, int to);
void writeToStream(TerminalCharacterDecoder* decoder, int from, int to);
QString getHistoryLine(int no);
/**
* Copies the selected characters, set using @see setSelBeginXY and @see setSelExtentXY
* into a stream using the specified character decoder.
* into a stream.
*
* @param stream An output stream which receives the converted text
* @param decoder A decoder which converts terminal characters into text. PlainTextDecoder
* is the most commonly used decoder which coverts characters into plain
* text with no formatting.
*/
void writeSelectionToStream(QTextStream* stream , TerminalCharacterDecoder* decoder);
void writeSelectionToStream(TerminalCharacterDecoder* decoder);
void checkSelection(int from, int to);
......@@ -353,10 +351,13 @@ private: // helper
// hist->getLines() + lines - 1
//start - the first column on the line to copy
//count - the number of characters on the line to copy
//stream - the output stream to write the text into
//decoder - a decoder which coverts terminal characters (an Character array) into text
void copyLineToStream(int line, int start, int count, QTextStream* stream,
TerminalCharacterDecoder* decoder);
//appendNewLine - if true a new line character (\n) is appended to the end of the line
void copyLineToStream(int line,
int start,
int count,
TerminalCharacterDecoder* decoder,
bool appendNewLine);
//fills a section of the screen image with the character 'c'
//the parameters are specified as offsets from the start of the screen image.
......
......@@ -50,7 +50,6 @@ Screen* ScreenWindow::screen() const
Character* ScreenWindow::getImage()
{
return _screen->getCookedImage(_currentLine);
}
......@@ -198,7 +197,15 @@ void ScreenWindow::resetScrollCount()
QRect ScreenWindow::scrollRegion() const
{
return _screen->lastScrolledRegion();
QRect rect = _screen->lastScrolledRegion();
// bound scroll region size to size of window
//rect.setHeight( qMin(rect.height() , windowLines()+1) );
//Q_ASSERT( rect.top() >= 0 && rect.left() >= 0 );
//Q_ASSERT( rect.height()-1 <= windowLines() );
return rect;
}
void ScreenWindow::notifyOutputChanged()
......
......@@ -1037,7 +1037,9 @@ void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data)
sessionLines-1 );
QTextStream stream(&data,QIODevice::ReadWrite);
info.session->emulation()->writeToStream( &stream , info.decoder , info.lastLineFetched+1 , copyUpToLine );
info.decoder->begin(&stream);
info.session->emulation()->writeToStream( info.decoder , info.lastLineFetched+1 , copyUpToLine );
info.decoder->end();
// if there are still more lines to process after this request
// then insert a new line character
......@@ -1087,7 +1089,6 @@ void SearchHistoryTask::execute()
if ( !_regExp.isEmpty() )
{
int pos = -1;
int findPos = -1;
const bool forwards = ( _direction == Forwards );
const int startLine = selectionLine + _screenWindow->currentLine() + ( forwards ? 1 : -1 );
const int lastLine = _screenWindow->lineCount() - 1;
......@@ -1102,7 +1103,8 @@ void SearchHistoryTask::execute()
int line = startLine;
//read through and search history in blocks of 10K lines.
//this balances the need to retrieve lots of data from the history each time (for efficient searching)
//this balances the need to retrieve lots of data from the history each time
//(for efficient searching)
//without using silly amounts of memory if the history is very large.
const int maxDelta = qMin(_screenWindow->lineCount(),10000);
int delta = forwards ? maxDelta : -maxDelta;
......@@ -1155,10 +1157,13 @@ void SearchHistoryTask::execute()
}
}
//qDebug() << "Searching lines " << qMin(endLine,line) << " to " << qMax(endLine,line);
emulation->writeToStream(&searchStream,&decoder, qMin(endLine,line) , qMax(endLine,line) );
qDebug() << "Searching lines " << qMin(endLine,line) << " to " << qMax(endLine,line);
decoder.begin(&searchStream);
emulation->writeToStream(&decoder, qMin(endLine,line) , qMax(endLine,line) );
decoder.end();
//qDebug() << "Stream contents length: " << string;
qDebug() << "Stream contents: " << string;
pos = -1;
if (forwards)
pos = string.indexOf(_regExp);
......@@ -1168,21 +1173,8 @@ void SearchHistoryTask::execute()
//if a match is found, position the cursor on that line and update the screen
if ( pos != -1 )
{
//work out how many lines into the current block of text the search result was found
//- looks a little painful, but it only has to be done once per search.
findPos = qMin(line,endLine) + string.left(pos + 1).count(QChar('\n'));
qDebug() << "Found result at line " << findPos;
//update display to show area of history containing selection
_screenWindow->scrollTo(findPos);
_screenWindow->setSelectionStart( 0 , findPos - _screenWindow->currentLine() , false );
_screenWindow->setSelectionEnd( _screenWindow->columnCount() , findPos - _screenWindow->currentLine() );
//qDebug() << "Current line " << _screenWindow->currentLine();
_screenWindow->setTrackOutput(false);
_screenWindow->notifyOutputChanged();
//qDebug() << "Post update current line " << _screenWindow->currentLine();
int findPos = qMin(line,endLine) + string.left(pos + 1).count(QChar('\n'));
highlightResult(findPos);
return;
}
......@@ -1198,6 +1190,23 @@ void SearchHistoryTask::execute()
}
}
void SearchHistoryTask::highlightResult(int findPos)
{
//work out how many lines into the current block of text the search result was found
//- looks a little painful, but it only has to be done once per search.
qDebug() << "Found result at line " << findPos;
//update display to show area of history containing selection
_screenWindow->scrollTo(findPos);
_screenWindow->setSelectionStart( 0 , findPos - _screenWindow->currentLine() , false );
_screenWindow->setSelectionEnd( _screenWindow->columnCount() , findPos - _screenWindow->currentLine() );
//qDebug() << "Current line " << _screenWindow->currentLine();
_screenWindow->setTrackOutput(false);
_screenWindow->notifyOutputChanged();
//qDebug() << "Post update current line " << _screenWindow->currentLine();
}
SearchHistoryTask::SearchHistoryTask(ScreenWindow* window , QObject* parent)
: SessionTask(parent)
, _matchRegExp(false)
......
......@@ -385,6 +385,8 @@ signals:
int endColumn );
private:
void highlightResult(int position);
QRegExp _regExp;
bool _matchRegExp;
bool _matchCase;
......
......@@ -31,7 +31,8 @@
using namespace Konsole;
PlainTextDecoder::PlainTextDecoder()
: _includeTrailingWhitespace(true)
: _output(0)
, _includeTrailingWhitespace(true)
{
}
......@@ -43,10 +44,19 @@ bool PlainTextDecoder::trailingWhitespace() const
{
return _includeTrailingWhitespace;
}
void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/,
QTextStream* output)
void PlainTextDecoder::begin(QTextStream* output)
{
_output = output;
}
void PlainTextDecoder::end()
{
_output = 0;
}
void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
)
{
Q_ASSERT( _output );
//TODO should we ignore or respect the LINE_WRAPPED line property?
//note: we build up a QString and send it to the text stream rather writing into the text
......@@ -75,71 +85,95 @@ void PlainTextDecoder::decodeLine(const Character* const characters, int count,
plainText.append( QChar(characters[i].character) );
}
*output << plainText;
*_output << plainText;
}
HTMLDecoder::HTMLDecoder() :
colorTable(base_color_table)
_output(0)
,_colorTable(base_color_table)
,_innerSpanOpen(false)
,_lastRendition(DEFAULT_RENDITION)
{
}
void HTMLDecoder::begin(QTextStream* output)
{
_output = output;
QString text;
//open monospace span
openSpan(text,"font-family:monospace");
*output << text;
}
void HTMLDecoder::end()
{
Q_ASSERT( _output );
QString text;
closeSpan(text);
*_output << text;
_output = 0;
}
//TODO: Support for LineProperty (mainly double width , double height)
void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/,
QTextStream* output)
void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/
)
{
Q_ASSERT( _output );
QString text;
bool innerSpanOpen = false;
int spaceCount = 0;
UINT8 lastRendition = DEFAULT_RENDITION;
CharacterColor lastForeColor;
CharacterColor lastBackColor;
//open monospace span
openSpan(text,"font-family:monospace");
for (int i=0;i<count;i++)
{
QChar ch(characters[i].character);
//check if appearance of character is different from previous char
if ( characters[i].rendition != lastRendition ||
characters[i].foregroundColor != lastForeColor ||
characters[i].backgroundColor != lastBackColor )
if ( characters[i].rendition != _lastRendition ||
characters[i].foregroundColor != _lastForeColor ||
characters[i].backgroundColor != _lastBackColor )
{
if ( innerSpanOpen )
if ( _innerSpanOpen )
closeSpan(text);
lastRendition = characters[i].rendition;
lastForeColor = characters[i].foregroundColor;
lastBackColor = characters[i].backgroundColor;
_lastRendition = characters[i].rendition;
_lastForeColor = characters[i].foregroundColor;
_lastBackColor = characters[i].backgroundColor;
//build up style string
QString style;
if ( lastRendition & RE_BOLD ||
(colorTable && characters[i].isBold(colorTable)) )
if ( _lastRendition & RE_BOLD ||
(_colorTable && characters[i].isBold(_colorTable)) )
style.append("font-weight:bold;");
if ( lastRendition & RE_UNDERLINE )
if ( _lastRendition & RE_UNDERLINE )
style.append("font-decoration:underline;");
//colours - a colour table must have been defined first
if ( colorTable )
if ( _colorTable )
{
style.append( QString("color:%1;").arg(lastForeColor.color(colorTable).name() ) );
style.append( QString("color:%1;").arg(_lastForeColor.color(_colorTable).name() ) );
if (!characters[i].isTransparent(colorTable))
if (!characters[i].isTransparent(_colorTable))
{
style.append( QString("background-color:%1;").arg(lastBackColor.color(colorTable).name() ) );
style.append( QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name() ) );
}
}
//open the span with the current style
openSpan(text,style);
innerSpanOpen = true;
_innerSpanOpen = true;
}
//handle whitespace
......@@ -168,16 +202,13 @@ void HTMLDecoder::decodeLine(const Character* const characters, int count, LineP
}
//close any remaining open inner spans
if ( innerSpanOpen )
if ( _innerSpanOpen )
closeSpan(text);
//close monospace span
closeSpan(text);
//start new line
text.append("<br>");
*output << text;
*_output << text;
}
void HTMLDecoder::openSpan(QString& text , const QString& style)
......@@ -192,5 +223,5 @@ void HTMLDecoder::closeSpan(QString& text)
void HTMLDecoder::setColorTable(const ColorEntry* table)
{
colorTable = table;
_colorTable = table;
}
......@@ -42,7 +42,12 @@ class TerminalCharacterDecoder
{
public:
virtual ~TerminalCharacterDecoder() {}
/** Begin decoding characters. The resulting text is appended to @p output. */
virtual void begin(QTextStream* output) = 0;
/** End decoding. */
virtual void end() = 0;
/**
* Converts a line of terminal characters with associated properties into a text string
* and writes the string into an output QTextStream.
......@@ -53,8 +58,7 @@ public:
*/
virtual void decodeLine(const Character* const characters,
int count,
LineProperty properties,
QTextStream* output) = 0;
LineProperty properties) = 0;
};
/**
......@@ -78,11 +82,16 @@ public:
*/
bool trailingWhitespace() const;
virtual void begin(QTextStream* output);
virtual void end();
virtual void decodeLine(const Character* const characters,
int count,
LineProperty properties,
QTextStream* output);
LineProperty properties);
private:
QTextStream* _output;
bool _includeTrailingWhitespace;
};
......@@ -105,14 +114,22 @@ public:
virtual void decodeLine(const Character* const characters,
int count,
LineProperty properties,
QTextStream* output);
LineProperty properties);
virtual void begin(QTextStream* output);
virtual void end();
private:
void openSpan(QString& text , const QString& style);
void closeSpan(QString& text);
const ColorEntry* colorTable;
QTextStream* _output;
const ColorEntry* _colorTable;
bool _innerSpanOpen;
UINT8 _lastRendition;
CharacterColor _lastForeColor;
CharacterColor _lastBackColor;
};
}
......
......@@ -708,15 +708,18 @@ void TerminalDisplay::setCursorPos(const int curx, const int cury)
// a cell width of _fontWidth and a cell height of _fontHeight).
void TerminalDisplay::scrollImage(int lines , const QRect& region)
{
if ( lines == 0 || _image == 0 || abs(lines) >= region.height() ) return;
if ( lines == 0
|| _image == 0
|| (region.top() + abs(lines)) >= region.height()
|| this->_usedLines <= region.height() ) return;
QRect scrollRect;
//qDebug() << "Scrolled region: top =" << region.top()
// << ", bottom =" << region.bottom()
// << ", height =" << region.height() << "lines. Image height ="
// << this->_usedLines << "lines"
// << ", scroll =" << lines << "lines";
// qDebug() << "Scrolled region: top =" << region.top()
// << ", bottom =" << region.bottom()
// << ", height =" << region.height() << "lines. Image height ="
// << this->_usedLines << "lines"
// << ", scroll =" << lines << "lines";
void* firstCharPos = &_image[ region.top() * this->_usedColumns ];
void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_usedColumns ];
......@@ -730,12 +733,16 @@ void TerminalDisplay::scrollImage(int lines , const QRect& region)
Q_ASSERT( linesToMove > 0 );
Q_ASSERT( bytesToMove > 0 );
//scroll internal _image
//scroll internal image
if ( lines > 0 )
{
// check that the memory areas that we are going to move are valid
Q_ASSERT( (char*)lastCharPos + bytesToMove <
(char*)(_image + (this->_usedLines * this->_usedColumns)) );
assert( (lines*this->_usedColumns) < _imageSize );
//scroll internal _image down
//scroll internal image down
memmove( firstCharPos , lastCharPos , bytesToMove );
//set region of display to scroll, making sure that
......@@ -746,7 +753,11 @@ void TerminalDisplay::scrollImage(int lines , const QRect& region)
}
else
{
//scroll internal _image up
// check that the memory areas that we are going to move are valid
Q_ASSERT( (char*)firstCharPos + bytesToMove <
(char*)(_image + (this->_usedLines * this->_usedColumns)) );
//scroll internal image up
memmove( lastCharPos , firstCharPos , bytesToMove );
//set region of the display to scroll, making sure that
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment