--- a/hedgewars/uChat.pas Mon May 04 17:48:57 2015 +0300
+++ b/hedgewars/uChat.pas Mon May 04 17:49:15 2015 +0300
@@ -36,7 +36,7 @@
uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils;
const MaxStrIndex = 27;
- MaxInputStrLen = 240;
+ MaxInputStrLen = 200;
type TChatLine = record
Tex: PTexture;
@@ -47,19 +47,15 @@
end;
TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen);
-type TInputStrL = array[0..260] of byte;
-
var Strs: array[0 .. MaxStrIndex] of TChatLine;
MStrs: array[0 .. MaxStrIndex] of shortstring;
LocalStrs: array[0 .. MaxStrIndex] of shortstring;
- LocalStrsL: array[0 .. MaxStrIndex] of TInputStrL;
missedCount: LongWord;
lastStr: LongWord;
localLastStr: LongInt;
history: LongInt;
visibleCount: LongWord;
InputStr: TChatLine;
- InputStrL: TInputStrL; // for full str + 4-byte utf-8 char
ChatReady: boolean;
showAll: boolean;
liveLua: boolean;
@@ -72,8 +68,6 @@
const
- InputStrLNoPred: byte = 255;
-
colors: array[#0..#6] of TSDL_Color = (
(r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything
(r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White]
@@ -98,6 +92,13 @@
const Padding = 2;
ClHeight = 2 * Padding + 16; // font height
+// relevant for UTF-8 handling
+function IsFirstCharByte(c: char): boolean; inline;
+begin
+ // based on https://en.wikipedia.org/wiki/UTF-8#Description
+ IsFirstCharByte:= (byte(c) and $C0) <> $80;
+end;
+
function charIsForHogSpeech(c: char): boolean;
begin
exit((c = '"') or (c = '''') or (c = '-'));
@@ -427,7 +428,6 @@
// put in input history
localLastStr:= (localLastStr + 1) mod MaxStrIndex;
LocalStrs[localLastStr]:= s;
- LocalStrsL[localLastStr]:= InputStrL;
end;
t:= LocalTeam;
@@ -576,7 +576,7 @@
end;
procedure DelBytesFromInputStrBack(endIdx: integer; count: byte);
-var i, startIdx: integer;
+var startIdx: integer;
begin
// nothing to do if count is 0
if count = 0 then
@@ -588,45 +588,48 @@
// delete bytes from string
Delete(InputStr.s, startIdx, count);
- // wipe utf8 info for deleted char
- InputStrL[endIdx]:= InputStrLNoPred;
-
- // shift utf8 char info to reflect new string
- for i:= endIdx + 1 to Length(InputStr.s) + count do
- begin
- if InputStrL[i] <> InputStrLNoPred then
- begin
- InputStrL[i-count]:= InputStrL[i] - count;
- InputStrL[i]:= InputStrLNoPred;
- end;
- end;
-
SetLine(InputStr, InputStr.s, true);
end;
-// returns count of removed bytes
-function DelCharFromInputStr(idx: integer): integer;
-var btw: byte;
+procedure MoveCursorToPreviousChar();
begin
- // note: idx is always at last byte of utf8 chars. cuz relevant for InputStrL
-
- if (Length(InputStr.s) < 1) or (idx < 1) or (idx > Length(InputStr.s)) then
- exit(0);
-
- btw:= byte(idx) - InputStrL[idx];
-
- DelCharFromInputStr:= btw;
-
- DelBytesFromInputStrBack(idx, btw);
+ if cursorPos > 0 then
+ begin
+ while (not IsFirstCharByte(InputStr.s[cursorPos])) do
+ begin
+ dec(cursorPos);
+ end;
+ dec(cursorPos);
+ end;
end;
-// unchecked
-procedure DoCursorStepForward();
+procedure MoveCursorToNextChar();
begin
- // go to end of next utf8-char
- repeat
- inc(cursorPos);
- until InputStrL[cursorPos] <> InputStrLNoPred;
+ if cursorPos < Length(InputStr.s) then
+ begin
+ inc(cursorPos, 2);
+ while (cursorPos < Length(InputStr.s)) and (not IsFirstCharByte(InputStr.s[cursorPos])) do
+ begin
+ inc(cursorPos);
+ end;
+ dec(cursorPos);
+ end;
+end;
+
+procedure DeleteLastUTF8CharFromStr(var s: shortstring);
+var l: byte;
+begin
+ l:= Length(s);
+
+ while (l > 1) and (not IsFirstCharByte(s[l])) do
+ begin
+ dec(l);
+ end;
+
+ if l > 0 then
+ dec(l);
+
+ s[0]:= char(l);
end;
procedure DeleteSelected();
@@ -635,8 +638,8 @@
begin
DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
cursorPos:= min(cursorPos, selectedPos);
- ResetSelection();
end;
+ ResetSelection();
UpdateCursorCoords();
end;
@@ -656,10 +659,6 @@
function GetInputCharSkipClass(index: LongInt): TCharSkip;
var c: char;
begin
- // multi-byte chars counts as letter
- if (index > 1) and (InputStrL[index] <> index - 1) then
- exit(numalpha);
-
c:= InputStr.s[index];
// non-ascii counts as letter
@@ -700,33 +699,31 @@
begin
skip:= GetInputCharSkipClass(cursorPos);
if skip = wspace then
- cursorPos:= InputStrL[cursorPos];
+ MoveCursorToPreviousChar();
end;
// skip same-type chars
while (cursorPos > 0) and (GetInputCharSkipClass(cursorPos) = skip) do
- cursorPos:= InputStrL[cursorPos];
+ MoveCursorToPreviousChar();
end
else
begin
// skip same-type chars
while cursorPos < Length(InputStr.s) do
begin
- DoCursorStepForward();
+ MoveCursorToNextChar();
if (GetInputCharSkipClass(cursorPos) <> skip) then
begin
- // go back 1 char
- cursorPos:= InputStrL[cursorPos];
+ MoveCursorToPreviousChar();
break;
end;
end;
// skip trailing whitespace, similar to Qt
while cursorPos < Length(InputStr.s) do
begin
- DoCursorStepForward();
+ MoveCursorToNextChar();
if (GetInputCharSkipClass(cursorPos) <> wspace) then
begin
- // go back 1 char
- cursorPos:= InputStrL[cursorPos];
+ MoveCursorToPreviousChar();
break;
end;
end;
@@ -748,42 +745,44 @@
end;
end;
-// TODO: honor utf8, don't break utf8 chars when shifting chars beyond limit
procedure InsertIntoInputStr(s: shortstring);
-var i, l, il, lastc: integer;
+var limit: integer;
begin
- // safe length for string
- l:= min(MaxInputStrLen-cursorPos, Length(s));
- s:= copy(s,1,l);
+ // we check limit for trailing stuff before insertion limit for a reason
+ // (possible remaining space after too long UTF8-insertion has been shortened)
- // if we insert rather than append, shift info in InputStrL accordingly
- if cursorPos < Length(InputStr.s) then
+ // length limit for stuff to that will trail the insertion
+ limit:= max(cursorPos, MaxInputStrLen-Length(s));
+
+ while Length(InputStr.s) > limit do
begin
- for i:= Length(InputStr.s) downto cursorPos + 1 do
- begin
- if InputStrL[i] <> InputStrLNoPred then
- begin
- il:= i + l;
- // only shift if not overflowing
- if il <= MaxInputStrLen then
- InputStrL[il]:= InputStrL[i] + l;
- InputStrL[i]:= InputStrLNoPred;
- end;
- end;
+ DeleteLastUTF8CharFromStr(InputStr.s);
end;
- InputStrL[cursorPos + l]:= cursorPos;
- // insert string truncated to safe length
- Insert(s, InputStr.s, cursorPos + 1);
- if Length(InputStr.s) > MaxInputStrLen then
- InputStr.s[0]:= char(MaxInputStrLen);
+ // length limit for stuff to insert
+ limit:= max(0, MaxInputStrLen-cursorPos);
+
+ if limit = 0 then
+ s:= ''
+ else while Length(s) > limit do
+ begin
+ DeleteLastUTF8CharFromStr(s);
+ end;
- SetLine(InputStr, InputStr.s, true);
+ if Length(s) > 0 then
+ begin
+ // insert string truncated to safe length
+ Insert(s, InputStr.s, cursorPos + 1);
- // move cursor to end of inserted string
- lastc:= MaxInputStrLen;
- cursorPos:= min(lastc, cursorPos + l);
- UpdateCursorCoords();
+ if Length(InputStr.s) > MaxInputStrLen then
+ InputStr.s[0]:= char(MaxInputStrLen);
+
+ SetLine(InputStr, InputStr.s, true);
+
+ // move cursor to end of inserted string
+ inc(cursorPos, Length(s));
+ UpdateCursorCoords();
+ end;
end;
procedure PasteFromClipboard();
@@ -821,60 +820,41 @@
begin
if selectedPos < 0 then
begin
- if ctrl then
- skip:= GetInputCharSkipClass(cursorPos);
-
- // remove char before cursor
- dec(cursorPos, DelCharFromInputStr(cursorPos));
+ HandleSelection(true);
// delete more if ctrl is held
- if ctrl and (selectedPos < 0) then
- begin
- HandleSelection(true);
- SkipInputChars(skip, true);
- DeleteSelected();
- end
+ if ctrl then
+ SkipInputChars(GetInputCharSkipClass(cursorPos), true)
else
- UpdateCursorCoords();
+ MoveCursorToPreviousChar();
- end
- else
- DeleteSelected();
+ end;
+
+ DeleteSelected();
+ UpdateCursorCoords();
end;
SDLK_DELETE:
begin
if selectedPos < 0 then
begin
- // remove char after cursor
- if cursorPos < Length(InputStr.s) then
- begin
- DoCursorStepForward();
- if ctrl then
- skip:= GetInputCharSkipClass(cursorPos);
-
- // delete char
- dec(cursorPos, DelCharFromInputStr(cursorPos));
+ HandleSelection(true);
- // delete more if ctrl is held
- if ctrl and (cursorPos < Length(InputStr.s)) then
- begin
- HandleSelection(true);
- SkipInputChars(skip, false);
- DeleteSelected();
- end;
- end
+ // delete more if ctrl is held
+ if ctrl then
+ SkipInputChars(GetInputCharSkipClass(cursorPos), false)
else
- UpdateCursorCoords();
- end
- else
- DeleteSelected();
+ MoveCursorToNextChar();
+
+ end;
+
+ DeleteSelected();
+ UpdateCursorCoords();
end;
SDLK_ESCAPE:
begin
if Length(InputStr.s) > 0 then
begin
SetLine(InputStr, '', true);
- FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
ResetCursor();
end
else CleanupInput
@@ -885,7 +865,6 @@
begin
AcceptChatString(InputStr.s);
SetLine(InputStr, '', false);
- FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
ResetCursor();
end;
CleanupInput
@@ -898,12 +877,10 @@
if (index > localLastStr) then
begin
SetLine(InputStr, '', true);
- FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
end
else
begin
SetLine(InputStr, LocalStrs[index], true);
- InputStrL:= LocalStrsL[index];
end;
cursorPos:= Length(InputStr.s);
ResetSelection();
@@ -946,7 +923,7 @@
begin
HandleSelection(selMode);
// go to end of previous utf8-char
- cursorPos:= InputStrL[cursorPos];
+ MoveCursorToPreviousChar();
end
else // if we're leaving selection mode, jump to its left end
begin
@@ -971,7 +948,7 @@
if selMode or (selectedPos < 0) then
begin
HandleSelection(selMode);
- DoCursorStepForward();
+ MoveCursorToNextChar();
end
else // if we're leaving selection mode, jump to its right end
begin
@@ -1018,7 +995,10 @@
begin
// paste
if ctrl then
- PasteFromClipboard()
+ begin
+ DeleteSelected();
+ PasteFromClipboard();
+ end
else
action:= false;
end;
@@ -1062,6 +1042,7 @@
if Length(InputStr.s) + btw > MaxInputStrLen then
exit;
+ // if speech bubble quotes are used as first input, add the closing quote and place cursor inbetween
if (Length(InputStr.s) = 0) and (Length(utf8) = 1) and (charIsForHogSpeech(utf8[1])) then
begin
InsertIntoInputStr(utf8);
@@ -1127,9 +1108,6 @@
else
begin
SetLine(InputStr, '/team ', true);
- // update InputStrL and cursor accordingly
- // this allows cursor-jumping over '/team ' as if it was a single char
- InputStrL[6]:= 0;
cursorPos:= 6;
UpdateCursorCoords();
end;
@@ -1162,8 +1140,6 @@
for i:= 0 to MaxStrIndex do
Strs[i].Tex := nil;
- FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
-
LastKeyPressTick:= 0;
ResetCursor();
end;