summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Astals Cid <aacid@kde.org>2011-06-26 15:32:30 (GMT)
committerAlbert Astals Cid <aacid@kde.org>2011-06-26 15:32:30 (GMT)
commitc335324f31e946d4e3a0c63d1fbed8c114aea987 (patch)
tree88e3342abd23f1249ef210f5bd9014cea143108f
parent3913792ce6fb6cb887d98045241e1b6458920a10 (diff)
Support Unicode decomposed characters
BUGS: 96536
-rw-r--r--src/Character.h30
-rw-r--r--src/Emulation.cpp48
-rw-r--r--src/Screen.cpp35
-rw-r--r--src/Screen.h18
-rw-r--r--src/TerminalCharacterDecoder.cpp47
-rw-r--r--src/TerminalDisplay.cpp128
-rw-r--r--src/TerminalDisplay.h2
7 files changed, 243 insertions, 65 deletions
diff --git a/src/Character.h b/src/Character.h
index f903fba..133fce2 100644
--- a/src/Character.h
+++ b/src/Character.h
@@ -70,19 +70,12 @@ public:
quint8 _r = DEFAULT_RENDITION)
: character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {}
- union
- {
- /** The unicode character value for this character. */
- quint16 character;
- /**
- * Experimental addition which allows a single Character instance to contain more than
- * one unicode character.
- *
- * charSequence is a hash code which can be used to look up the unicode
- * character sequence in the ExtendedCharTable used to create the sequence.
- */
- quint16 charSequence;
- };
+ /** The unicode character value for this character.
+ *
+ * if RE_EXTENDED_CHAR character is a hash code which can be used to look up the unicode
+ * character sequence in the ExtendedCharTable used to create the sequence.
+ */
+ quint16 character;
/** A combination of RENDITION flags which specify options for drawing the character. */
quint8 rendition;
@@ -119,6 +112,17 @@ public:
* renditions or colors.
*/
friend bool operator != (const Character& a, const Character& b);
+
+ inline bool isLineChar() const
+ {
+ return (rendition & RE_EXTENDED_CHAR) ? false : ((character & 0xFF80) == 0x2500);
+ }
+
+ inline bool isSpace() const
+ {
+ return (rendition & RE_EXTENDED_CHAR) ? false : QChar(character).isSpace();
+ }
+
};
inline bool operator == (const Character& a, const Character& b)
diff --git a/src/Emulation.cpp b/src/Emulation.cpp
index e482f87..e769833 100644
--- a/src/Emulation.cpp
+++ b/src/Emulation.cpp
@@ -47,6 +47,9 @@
#include "Screen.h"
#include "TerminalCharacterDecoder.h"
#include "ScreenWindow.h"
+#include "SessionManager.h"
+#include "Session.h"
+#include "TerminalDisplay.h"
using namespace Konsole;
@@ -389,11 +392,13 @@ ushort ExtendedCharTable::createExtendedChar(const ushort* unicodePoints , ushor
{
// look for this sequence of points in the table
ushort hash = extendedCharHash(unicodePoints,length);
+ const ushort initialHash = hash;
+ bool triedCleaningSolution = false;
// check existing entry for match
- while ( extendedCharTable.contains(hash) )
+ while ( extendedCharTable.contains(hash) && hash != 0 ) // 0 has a special meaning for chars so we don't use it
{
- if ( extendedCharMatch(hash,unicodePoints,length) )
+ if ( extendedCharMatch(hash, unicodePoints, length) )
{
// this sequence already has an entry in the table,
// return its hash
@@ -404,6 +409,45 @@ ushort ExtendedCharTable::createExtendedChar(const ushort* unicodePoints , ushor
// if hash is already used by another, different sequence of unicode character
// points then try next hash
hash++;
+
+ if (hash == initialHash)
+ {
+ if (!triedCleaningSolution)
+ {
+ triedCleaningSolution = true;
+ // All the hashes are full, go to all Screens and try to free any
+ // This is slow but should happen very rarely
+ QSet<ushort> usedExtendedChars;
+ const SessionManager *sm = SessionManager::instance();
+ foreach(const Session *s, sm->sessions())
+ {
+ foreach(const TerminalDisplay* td, s->views())
+ {
+ usedExtendedChars += td->screenWindow()->screen()->usedExtendedChars();
+
+ }
+ }
+
+ QHash<ushort,ushort*>::iterator it = extendedCharTable.begin();
+ QHash<ushort,ushort*>::iterator itEnd = extendedCharTable.end();
+ while (it != itEnd)
+ {
+ if (usedExtendedChars.contains(it.key()))
+ {
+ ++it;
+ }
+ else
+ {
+ it = extendedCharTable.erase(it);
+ }
+ }
+ }
+ else
+ {
+ kWarning() << "Using all the extended char hashes, going to miss this extended character";
+ return 0;
+ }
+ }
}
}
diff --git a/src/Screen.cpp b/src/Screen.cpp
index b332200..03a4bbd 100644
--- a/src/Screen.cpp
+++ b/src/Screen.cpp
@@ -557,7 +557,10 @@ void Screen::backspace()
screenLines[cuY].resize(cuX+1);
if (BS_CLEARS)
+ {
screenLines[cuY][cuX].character = ' ';
+ screenLines[cuY][cuX].rendition = screenLines[cuY][cuX].rendition & ~RE_EXTENDED_CHAR;
+ }
}
void Screen::tab(int n)
@@ -631,8 +634,38 @@ void Screen::displayCharacter(unsigned short c)
// putting the cursor one right to the last column of the screen.
int w = konsole_wcwidth(c);
- if (w <= 0)
+ if (w < 0)
return;
+ else if (w == 0)
+ {
+ if (QChar(c).category() != QChar::Mark_NonSpacing)
+ return;
+ int charToCombineWithX = -1;
+ int charToCombineWithY = -1;
+ if (cuX == 0)
+ {
+ if (cuY > 0)
+ {
+ charToCombineWithX = columns - 1;
+ charToCombineWithY = cuY - 1;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ charToCombineWithX = cuX - 1;
+ charToCombineWithY = cuY;
+ }
+ Character& currentChar = screenLines[charToCombineWithY][charToCombineWithX];
+ Q_ASSERT((currentChar.rendition & RE_EXTENDED_CHAR) == 0);
+ const ushort chars[2] = { currentChar.character, c };
+ currentChar.rendition |= RE_EXTENDED_CHAR;
+ currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, 2);
+ return;
+ }
if (cuX+w > columns) {
if (getMode(MODE_Wrap)) {
diff --git a/src/Screen.h b/src/Screen.h
index ba99145..35f52b1 100644
--- a/src/Screen.h
+++ b/src/Screen.h
@@ -25,6 +25,7 @@
// Qt
#include <QtCore/QRect>
+#include <QtCore/QSet>
#include <QtCore/QTextStream>
#include <QtCore/QVarLengthArray>
@@ -555,6 +556,23 @@ public:
{
return _currentTerminalDisplay;
}
+
+ QSet<ushort> usedExtendedChars() const
+ {
+ QSet<ushort> result;
+ for (int i = 0; i < lines; ++i)
+ {
+ const ImageLine &il = screenLines[i];
+ for (int j = 0; j < columns; ++j)
+ {
+ if (il[j].rendition & RE_EXTENDED_CHAR)
+ {
+ result << il[j].character;
+ }
+ }
+ }
+ return result;
+ }
private:
diff --git a/src/TerminalCharacterDecoder.cpp b/src/TerminalCharacterDecoder.cpp
index cd05f65..1232afe 100644
--- a/src/TerminalCharacterDecoder.cpp
+++ b/src/TerminalCharacterDecoder.cpp
@@ -93,7 +93,7 @@ void PlainTextDecoder::decodeLine(const Character* const characters, int count,
{
for (int i = count-1 ; i >= 0 ; i--)
{
- if ( characters[i].character != ' ' )
+ if ( !characters[i].isSpace() )
break;
else
outputCount--;
@@ -102,8 +102,22 @@ void PlainTextDecoder::decodeLine(const Character* const characters, int count,
for (int i=0;i<outputCount;)
{
- plainText.append( QChar(characters[i].character) );
- i += qMax(1,konsole_wcwidth(characters[i].character));
+ if (characters[i].rendition & RE_EXTENDED_CHAR)
+ {
+ ushort extendedCharLength = 0;
+ const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
+ if (chars)
+ {
+ const QString s = QString::fromUtf16(chars, extendedCharLength);
+ plainText.append(s);
+ i += string_width(s);
+ }
+ }
+ else
+ {
+ plainText.append( QChar(characters[i].character) );
+ i += qMax(1,konsole_wcwidth(characters[i].character));
+ }
}
*_output << plainText;
}
@@ -155,8 +169,6 @@ void HTMLDecoder::decodeLine(const Character* const characters, int count, LineP
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 ||
@@ -202,7 +214,7 @@ void HTMLDecoder::decodeLine(const Character* const characters, int count, LineP
}
//handle whitespace
- if (ch.isSpace())
+ if (characters[i].isSpace())
spaceCount++;
else
spaceCount = 0;
@@ -211,13 +223,26 @@ void HTMLDecoder::decodeLine(const Character* const characters, int count, LineP
//output current character
if (spaceCount < 2)
{
- //escape HTML tag characters and just display others as they are
- if ( ch == '<' )
- text.append("&lt;");
- else if (ch == '>')
+ if (characters[i].rendition & RE_EXTENDED_CHAR)
+ {
+ ushort extendedCharLength = 0;
+ const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(characters[i].character, extendedCharLength);
+ if (chars)
+ {
+ text.append(QString::fromUtf16(chars, extendedCharLength));
+ }
+ }
+ else
+ {
+ //escape HTML tag characters and just display others as they are
+ const QChar ch = characters[i].character;
+ if ( ch == '<' )
+ text.append("&lt;");
+ else if (ch == '>')
text.append("&gt;");
- else
+ else
text.append(ch);
+ }
}
else
{
diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp
index 3df8aa4..bb3d641 100644
--- a/src/TerminalDisplay.cpp
+++ b/src/TerminalDisplay.cpp
@@ -190,12 +190,10 @@ void TerminalDisplay::setColorTable(const ColorEntry table[])
QCodec.
*/
-static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
static inline bool isLineCharString(const QString& string)
{
- return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
+ return (string.length() > 0) && ((string.at(0).unicode() & 0xFF80) == 0x2500);
}
-
// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.
@@ -1011,10 +1009,9 @@ void TerminalDisplay::updateImage()
// where characters exceed their cell width.
if (dirtyMask[x])
{
- quint16 c = newLine[x+0].character;
- if ( !c )
+ if (!newLine[x+0].character)
continue;
- const bool lineDraw = isLineChar(c);
+ const bool lineDraw = newLine[x+0].isLineChar();
const bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
cr = newLine[x].rendition;
_clipboard = newLine[x].backgroundColor;
@@ -1033,7 +1030,7 @@ void TerminalDisplay::updateImage()
ch.backgroundColor != _clipboard ||
ch.rendition != cr ||
!dirtyMask[x+len] ||
- isLineChar(c) != lineDraw ||
+ ch.isLineChar() != lineDraw ||
nextIsDoubleWidth != doubleWidth )
break;
}
@@ -1318,7 +1315,7 @@ void TerminalDisplay::paintFilters(QPainter& painter)
// display in _columns
// ignore whitespace at the end of the lines
- while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 )
+ while ( _image[loc(endColumn,line)].isSpace() && endColumn > 0 )
endColumn--;
// increment here because the column which we want to set 'endColumn' to
@@ -1380,21 +1377,21 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
const int rlx = qMin(_usedColumns-1, qMax(0,(rect.right() - tLx - _leftMargin ) / _fontWidth));
const int rly = qMin(_usedLines-1, qMax(0,(rect.bottom() - tLy - _topMargin ) / _fontHeight));
- const int bufferSize = _usedColumns;
+ const int numberOfColumns = _usedColumns;
QString unistr;
- unistr.reserve(bufferSize);
+ unistr.reserve(numberOfColumns);
for (int y = luy; y <= rly; y++)
{
- quint16 c = _image[loc(lux,y)].character;
int x = lux;
- if(!c && x)
+ if(!_image[loc(lux,y)].character && x)
x--; // Search for start of multi-column character
for (; x <= rlx; x++)
{
int len = 1;
int p = 0;
- // reset our buffer to the maximal size
+ // reset our buffer to the number of columns
+ int bufferSize = numberOfColumns;
unistr.resize(bufferSize);
QChar *disstrU = unistr.data();
@@ -1403,18 +1400,24 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
{
// sequence of characters
ushort extendedCharLength = 0;
- ushort* chars = ExtendedCharTable::instance
- .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength);
- for ( int index = 0 ; index < extendedCharLength ; index++ )
+ const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(_image[loc(x,y)].character, extendedCharLength);
+ if (chars)
{
+ Q_ASSERT(extendedCharLength > 1);
+ bufferSize += extendedCharLength - 1;
+ unistr.resize(bufferSize);
+ disstrU = unistr.data();
+ for ( int index = 0 ; index < extendedCharLength ; index++ )
+ {
Q_ASSERT( p < bufferSize );
disstrU[p++] = chars[index];
+ }
}
}
else
{
// single character
- c = _image[loc(x,y)].character;
+ const quint16 c = _image[loc(x,y)].character;
if (c)
{
Q_ASSERT( p < bufferSize );
@@ -1422,7 +1425,7 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
}
}
- const bool lineDraw = isLineChar(c);
+ const bool lineDraw = _image[loc(x,y)].isLineChar();
const bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0);
const CharacterColor currentForeground = _image[loc(x,y)].foregroundColor;
const CharacterColor currentBackground = _image[loc(x,y)].backgroundColor;
@@ -1433,10 +1436,37 @@ void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect)
_image[loc(x+len,y)].backgroundColor == currentBackground &&
_image[loc(x+len,y)].rendition == currentRendition &&
(_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth &&
- isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment!
+ _image[loc(x+len,y)].isLineChar() == lineDraw)
{
- if (c)
- disstrU[p++] = c; //fontMap(c);
+ const quint16 c = _image[loc(x+len,y)].character;
+ if ( currentRendition & RE_EXTENDED_CHAR )
+ {
+ // sequence of characters
+ ushort extendedCharLength = 0;
+ const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(c, extendedCharLength);
+ if (chars)
+ {
+ Q_ASSERT(extendedCharLength > 1);
+ bufferSize += extendedCharLength - 1;
+ unistr.resize(bufferSize);
+ disstrU = unistr.data();
+ for ( int index = 0 ; index < extendedCharLength ; index++ )
+ {
+ Q_ASSERT( p < bufferSize );
+ disstrU[p++] = chars[index];
+ }
+ }
+ }
+ else
+ {
+ // single character
+ if (c)
+ {
+ Q_ASSERT( p < bufferSize );
+ disstrU[p++] = c; //fontMap(c);
+ }
+ }
+
if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
len++; // Skip trailing part of multi-column character
len++;
@@ -1947,9 +1977,9 @@ void TerminalDisplay::extendSelection( const QPoint& position )
QPoint left = left_not_right ? here : _iPntSelCorr;
i = loc(left.x(),left.y());
if (i>=0 && i<=_imageSize) {
- selClass = charClass(_image[i].character);
+ selClass = charClass(_image[i]);
while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) ))
- && charClass(_image[i-1].character) == selClass )
+ && charClass(_image[i-1]) == selClass )
{ i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} }
}
@@ -1957,9 +1987,9 @@ void TerminalDisplay::extendSelection( const QPoint& position )
QPoint right = left_not_right ? _iPntSelCorr : here;
i = loc(right.x(),right.y());
if (i>=0 && i<=_imageSize) {
- selClass = charClass(_image[i].character);
+ selClass = charClass(_image[i]);
while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) ))
- && charClass(_image[i+1].character) == selClass )
+ && charClass(_image[i+1]) == selClass )
{ i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } }
}
@@ -2029,7 +2059,7 @@ void TerminalDisplay::extendSelection( const QPoint& position )
{
i = loc(right.x(),right.y());
if (i>=0 && i<=_imageSize) {
- selClass = charClass(_image[i-1].character);
+ selClass = charClass(_image[i-1]);
/* if (selClass == ' ')
{
while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
@@ -2201,12 +2231,12 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
_wordSelectionMode = true;
// find word boundaries...
- const QChar selClass = charClass(_image[i].character);
+ const QChar selClass = charClass(_image[i]);
{
// find the start of the word
int x = bgnSel.x();
while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) ))
- && charClass(_image[i-1].character) == selClass )
+ && charClass(_image[i-1]) == selClass )
{
i--;
if (x>0)
@@ -2225,7 +2255,7 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
i = loc( endSel.x(), endSel.y() );
x = endSel.x();
while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) ))
- && charClass(_image[i+1].character) == selClass )
+ && charClass(_image[i+1]) == selClass )
{
i++;
if (x<_usedColumns-1)
@@ -2240,8 +2270,12 @@ void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev)
endSel.setX(x);
// In word selection mode don't select @ (64) if at end of word.
- if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) )
+ if ( ((_image[i].rendition & RE_EXTENDED_CHAR) == 0) &&
+ ( QChar( _image[i].character ) == '@' ) &&
+ ( ( endSel.x() - bgnSel.x() ) > 0 ) )
+ {
endSel.setX( x - 1 );
+ }
_actSel = 2; // within selection
@@ -2334,13 +2368,13 @@ void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev)
if (_tripleClickMode == SelectForwardsFromCursor) {
// find word boundary start
int i = loc(_iPntSel.x(),_iPntSel.y());
- const QChar selClass = charClass(_image[i].character);
+ const QChar selClass = charClass(_image[i]);
int x = _iPntSel.x();
while ( ((x>0) ||
(_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) )
)
- && charClass(_image[i-1].character) == selClass )
+ && charClass(_image[i-1]) == selClass )
{
i--;
if (x>0)
@@ -2380,14 +2414,34 @@ bool TerminalDisplay::focusNextPrevChild( bool next )
}
-QChar TerminalDisplay::charClass(QChar qch) const
+QChar TerminalDisplay::charClass(const Character &ch) const
{
- if ( qch.isSpace() ) return ' ';
+ if (ch.rendition & RE_EXTENDED_CHAR)
+ {
+ ushort extendedCharLength = 0;
+ const ushort* chars = ExtendedCharTable::instance.lookupExtendedChar(ch.character, extendedCharLength);
+ if (chars && extendedCharLength > 0)
+ {
+ const QString s = QString::fromUtf16(chars, extendedCharLength);
+ if (_wordCharacters.contains(s, Qt::CaseInsensitive))
+ return 'a';
+ bool allLetterOrNumber = true;
+ for (int i = 0; allLetterOrNumber && i < s.size(); ++i)
+ allLetterOrNumber = s.at(i).isLetterOrNumber();
+ return allLetterOrNumber ? 'a' : s.at(0);
+ }
+ return 0;
+ }
+ else
+ {
+ const QChar qch(ch.character);
+ if ( qch.isSpace() ) return ' ';
- if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) )
- return 'a';
+ if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) )
+ return 'a';
- return qch;
+ return qch;
+ }
}
void TerminalDisplay::setWordCharacters(const QString& wc)
diff --git a/src/TerminalDisplay.h b/src/TerminalDisplay.h
index 61de038..0fd4a90 100644
--- a/src/TerminalDisplay.h
+++ b/src/TerminalDisplay.h
@@ -591,7 +591,7 @@ protected:
// - A space (returns ' ')
// - Part of a word (returns 'a')
// - Other characters (returns the input character)
- QChar charClass(QChar ch) const;
+ QChar charClass(const Character &ch) const;
void clearImage();