From 9a47042620bec87617f0b4f5d50568535668fe26 Mon Sep 17 00:00:00 2001 From: Steven Luo Date: Mon, 5 May 2014 05:28:34 -0700 Subject: [PATCH] Fix inserting characters into the middle of an East Asian wide char We currently assume that the column we're inserting a char into is the start of a display character, and compute the length of the subsequence of mText storing the column's contents accordingly. This breaks, however, when inserting into the second column spanned by an East Asian wide character. Detect this case and handle it specially when we need to find the start of the next independent column's contents (such as when computing the length of the existing sequence stored in this column). Also, to preserve column alignment, pad the column before with a space; if the character being inserted is a wide character, clobber the next column's contents too. Fixes the second coming of #145, and potentially other difficult-to-reproduce bugs concerning East Asian wide character support as well. --- .../emulatorview/UnicodeTranscript.java | 96 ++++++++++++++----- 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/libraries/emulatorview/src/jackpal/androidterm/emulatorview/UnicodeTranscript.java b/libraries/emulatorview/src/jackpal/androidterm/emulatorview/UnicodeTranscript.java index ca9111b64..9a9566c6c 100644 --- a/libraries/emulatorview/src/jackpal/androidterm/emulatorview/UnicodeTranscript.java +++ b/libraries/emulatorview/src/jackpal/androidterm/emulatorview/UnicodeTranscript.java @@ -875,9 +875,25 @@ public void setChar(int column, int codePoint) { int charWidth = UnicodeTranscript.charWidth(codePoint); int oldCharWidth = UnicodeTranscript.charWidth(text, pos); + if (charWidth == 2 && column == columns - 1) { + // A width 2 character doesn't fit in the last column. + codePoint = ' '; + charWidth = 1; + } + + boolean wasExtraColForWideChar = false; + if (oldCharWidth == 2 && column > 0) { + /* If the previous screen column starts at the same offset in the + * array as this one, this column must be the second column used + * by an East Asian wide character */ + wasExtraColForWideChar = (findStartOfColumn(column - 1) == pos); + } + // Get the number of elements in the mText array this column uses now int oldLen; - if (column + oldCharWidth < columns) { + if (wasExtraColForWideChar && column + 1 < columns) { + oldLen = findStartOfColumn(column + 1) - pos; + } else if (column + oldCharWidth < columns) { oldLen = findStartOfColumn(column+oldCharWidth) - pos; } else { oldLen = spaceUsed - pos; @@ -927,44 +943,72 @@ public void setChar(int column, int codePoint) { } /* - * Handle cases where charWidth changes - * width 1 -> width 2: should clobber the contents of the next - * column (if next column contains wide char, need to pad with a space) - * width 2 -> width 1: pad with a space after the new character + * Handle cases where we need to pad with spaces to preserve column + * alignment + * + * width 2 -> width 1: pad with a space before or after the new + * character, depending on which of the two previously-occupied columns + * we wrote into + * + * inserting width 2 character into the second column of an existing + * width 2 character: pad with a space before the new character */ - if (oldCharWidth == 2 && charWidth == 1) { - // Pad with a space + if (oldCharWidth == 2 && charWidth == 1 || wasExtraColForWideChar && charWidth == 2) { int nextPos = pos + newLen; + char[] newText = text; if (spaceUsed + 1 > text.length) { // Array needs growing - char[] newText = new char[text.length + columns]; - System.arraycopy(text, 0, newText, 0, nextPos); + newText = new char[text.length + columns]; + System.arraycopy(text, 0, newText, 0, wasExtraColForWideChar ? pos : nextPos); + } + if (wasExtraColForWideChar) { + // Padding goes before the new character + System.arraycopy(text, pos, newText, pos + 1, spaceUsed - pos); + newText[pos] = ' '; + } else { + // Padding goes after the new character System.arraycopy(text, nextPos, newText, nextPos + 1, spaceUsed - nextPos); + newText[nextPos] = ' '; + } + + if (newText != text) { + // Update mText to point to the newly grown array mText = text = newText; - } else { - System.arraycopy(text, nextPos, text, nextPos + 1, spaceUsed - nextPos); } - text[nextPos] = ' '; // Update space used - ++offset[0]; + spaceUsed = ++offset[0]; - // Correct the offset for the next column to reflect width change - if (column == 0) { - offset[1] = (short) (newLen - 1); - } else if (column + 1 < columns) { - offset[column + 1] = (short) (offset[column] + newLen - 1); + // Correct the offset for the just-modified column to reflect + // width change + if (wasExtraColForWideChar) { + ++offset[column]; + ++pos; + } else { + if (column == 0) { + offset[1] = (short) (newLen - 1); + } else if (column + 1 < columns) { + offset[column + 1] = (short) (offset[column] + newLen - 1); + } + ++column; } - ++column; + ++shift; - } else if (oldCharWidth == 1 && charWidth == 2) { - if (column == columns - 1) { - // A width 2 character doesn't fit in the last column. - text[pos] = ' '; - offset[0] = (short) (pos + 1); - shift = 0; - } else if (column == columns - 2) { + } + + /* + * Handle cases where we need to clobber the contents of the next + * column in order to preserve column alignment + * + * width 1 -> width 2: should clobber the contents of the next + * column (if next column contains wide char, need to pad with a space) + * + * inserting width 2 character into the second column of an existing + * width 2 character: same + */ + if (oldCharWidth == 1 && charWidth == 2 || wasExtraColForWideChar && charWidth == 2) { + if (column == columns - 2) { // Correct offset for the next column to reflect width change offset[column + 1] = (short) (offset[column] - 1);