hedgewars/uChat.pas
branchqmlfrontend
changeset 10922 999d95494fe7
parent 10921 05e6f3b02612
child 10926 43612076e989
equal deleted inserted replaced
10908:1bd7a3a28b18 10922:999d95494fe7
    34 
    34 
    35 implementation
    35 implementation
    36 uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils;
    36 uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils;
    37 
    37 
    38 const MaxStrIndex = 27;
    38 const MaxStrIndex = 27;
    39       MaxInputStrLen = 240;
    39       MaxInputStrLen = 200;
    40 
    40 
    41 type TChatLine = record
    41 type TChatLine = record
    42     Tex: PTexture;
    42     Tex: PTexture;
    43     Time: Longword;
    43     Time: Longword;
    44     Width: LongInt;
    44     Width: LongInt;
    45     s: shortstring;
    45     s: shortstring;
    46     Color: TSDL_Color;
    46     Color: TSDL_Color;
    47     end;
    47     end;
    48     TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen);
    48     TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen);
    49 
    49 
    50 type TInputStrL = array[0..260] of byte;
       
    51 
       
    52 var Strs: array[0 .. MaxStrIndex] of TChatLine;
    50 var Strs: array[0 .. MaxStrIndex] of TChatLine;
    53     MStrs: array[0 .. MaxStrIndex] of shortstring;
    51     MStrs: array[0 .. MaxStrIndex] of shortstring;
    54     LocalStrs: array[0 .. MaxStrIndex] of shortstring;
    52     LocalStrs: array[0 .. MaxStrIndex] of shortstring;
    55     LocalStrsL: array[0 .. MaxStrIndex] of TInputStrL;
       
    56     missedCount: LongWord;
    53     missedCount: LongWord;
    57     lastStr: LongWord;
    54     lastStr: LongWord;
    58     localLastStr: LongInt;
    55     localLastStr: LongInt;
    59     history: LongInt;
    56     history: LongInt;
    60     visibleCount: LongWord;
    57     visibleCount: LongWord;
    61     InputStr: TChatLine;
    58     InputStr: TChatLine;
    62     InputStrL: TInputStrL; // for full str + 4-byte utf-8 char
       
    63     ChatReady: boolean;
    59     ChatReady: boolean;
    64     showAll: boolean;
    60     showAll: boolean;
    65     liveLua: boolean;
    61     liveLua: boolean;
    66     ChatHidden: boolean;
    62     ChatHidden: boolean;
    67     firstDraw: boolean;
    63     firstDraw: boolean;
    70     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
    66     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
    71     LastKeyPressTick: LongWord;
    67     LastKeyPressTick: LongWord;
    72 
    68 
    73 
    69 
    74 const
    70 const
    75     InputStrLNoPred: byte = 255;
       
    76 
       
    77     colors: array[#0..#6] of TSDL_Color = (
    71     colors: array[#0..#6] of TSDL_Color = (
    78             (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything
    72             (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything
    79             (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White]
    73             (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White]
    80             (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple]
    74             (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple]
    81             (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime]
    75             (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime]
    96 
    90 
    97 
    91 
    98 const Padding  = 2;
    92 const Padding  = 2;
    99       ClHeight = 2 * Padding + 16; // font height
    93       ClHeight = 2 * Padding + 16; // font height
   100 
    94 
       
    95 // relevant for UTF-8 handling
       
    96 function IsFirstCharByte(c: char): boolean; inline;
       
    97 begin
       
    98     // based on https://en.wikipedia.org/wiki/UTF-8#Description
       
    99     IsFirstCharByte:= (byte(c) and $C0) <> $80;
       
   100 end;
       
   101 
   101 function charIsForHogSpeech(c: char): boolean;
   102 function charIsForHogSpeech(c: char): boolean;
   102 begin
   103 begin
   103 exit((c = '"') or (c = '''') or (c = '-'));
   104 exit((c = '"') or (c = '''') or (c = '-'));
   104 end;
   105 end;
   105 
   106 
   425 if s <> LocalStrs[localLastStr] then
   426 if s <> LocalStrs[localLastStr] then
   426     begin
   427     begin
   427     // put in input history
   428     // put in input history
   428     localLastStr:= (localLastStr + 1) mod MaxStrIndex;
   429     localLastStr:= (localLastStr + 1) mod MaxStrIndex;
   429     LocalStrs[localLastStr]:= s;
   430     LocalStrs[localLastStr]:= s;
   430     LocalStrsL[localLastStr]:= InputStrL;
       
   431     end;
   431     end;
   432 
   432 
   433 t:= LocalTeam;
   433 t:= LocalTeam;
   434 x:= 0;
   434 x:= 0;
   435 if (s[1] = '"') and (s[Length(s)] = '"')
   435 if (s[1] = '"') and (s[Length(s)] = '"')
   574     GameState:= gsGame;
   574     GameState:= gsGame;
   575     ResetKbd;
   575     ResetKbd;
   576 end;
   576 end;
   577 
   577 
   578 procedure DelBytesFromInputStrBack(endIdx: integer; count: byte);
   578 procedure DelBytesFromInputStrBack(endIdx: integer; count: byte);
   579 var i, startIdx: integer;
   579 var startIdx: integer;
   580 begin
   580 begin
   581     // nothing to do if count is 0
   581     // nothing to do if count is 0
   582     if count = 0 then
   582     if count = 0 then
   583         exit;
   583         exit;
   584 
   584 
   586     startIdx:= endIdx - (count - 1);
   586     startIdx:= endIdx - (count - 1);
   587 
   587 
   588     // delete bytes from string
   588     // delete bytes from string
   589     Delete(InputStr.s, startIdx, count);
   589     Delete(InputStr.s, startIdx, count);
   590 
   590 
   591     // wipe utf8 info for deleted char
       
   592     InputStrL[endIdx]:= InputStrLNoPred;
       
   593 
       
   594     // shift utf8 char info to reflect new string
       
   595     for i:= endIdx + 1 to Length(InputStr.s) + count do
       
   596         begin
       
   597         if InputStrL[i] <> InputStrLNoPred then
       
   598             begin
       
   599             InputStrL[i-count]:= InputStrL[i] - count;
       
   600             InputStrL[i]:= InputStrLNoPred;
       
   601             end;
       
   602         end;
       
   603 
       
   604     SetLine(InputStr, InputStr.s, true);
   591     SetLine(InputStr, InputStr.s, true);
   605 end;
   592 end;
   606 
   593 
   607 // returns count of removed bytes
   594 procedure MoveCursorToPreviousChar();
   608 function DelCharFromInputStr(idx: integer): integer;
   595 begin
   609 var btw: byte;
   596     if cursorPos > 0 then
   610 begin
   597         begin
   611     // note: idx is always at last byte of utf8 chars. cuz relevant for InputStrL
   598         while (not IsFirstCharByte(InputStr.s[cursorPos])) do
   612 
   599             begin
   613     if (Length(InputStr.s) < 1) or (idx < 1) or (idx > Length(InputStr.s)) then
   600             dec(cursorPos);
   614         exit(0);
   601             end;
   615 
   602         dec(cursorPos);
   616     btw:= byte(idx) - InputStrL[idx];
   603         end;
   617 
   604 end;
   618     DelCharFromInputStr:= btw;
   605 
   619 
   606 procedure MoveCursorToNextChar();
   620     DelBytesFromInputStrBack(idx, btw);
   607 begin
   621 end;
   608     if cursorPos <  Length(InputStr.s) then
   622 
   609         begin
   623 // unchecked
   610         inc(cursorPos, 2);
   624 procedure DoCursorStepForward();
   611         while (cursorPos <  Length(InputStr.s)) and (not IsFirstCharByte(InputStr.s[cursorPos])) do
   625 begin
   612             begin
   626     // go to end of next utf8-char
   613             inc(cursorPos);
   627     repeat
   614             end;
   628         inc(cursorPos);
   615         dec(cursorPos);
   629     until InputStrL[cursorPos] <> InputStrLNoPred;
   616         end;
       
   617 end;
       
   618 
       
   619 procedure DeleteLastUTF8CharFromStr(var s: shortstring);
       
   620 var l: byte;
       
   621 begin
       
   622     l:= Length(s);
       
   623 
       
   624     while (l > 1) and (not IsFirstCharByte(s[l])) do
       
   625         begin
       
   626         dec(l);
       
   627         end;
       
   628 
       
   629     if l > 0 then
       
   630         dec(l);
       
   631 
       
   632     s[0]:= char(l);
   630 end;
   633 end;
   631 
   634 
   632 procedure DeleteSelected();
   635 procedure DeleteSelected();
   633 begin
   636 begin
   634     if (selectedPos >= 0) and (cursorPos <> selectedPos) then
   637     if (selectedPos >= 0) and (cursorPos <> selectedPos) then
   635         begin
   638         begin
   636         DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
   639         DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
   637         cursorPos:= min(cursorPos, selectedPos);
   640         cursorPos:= min(cursorPos, selectedPos);
   638         ResetSelection();
   641         end;
   639         end;
   642     ResetSelection();
   640     UpdateCursorCoords();
   643     UpdateCursorCoords();
   641 end;
   644 end;
   642 
   645 
   643 procedure HandleSelection(enabled: boolean);
   646 procedure HandleSelection(enabled: boolean);
   644 begin
   647 begin
   654 type TCharSkip = ( none, wspace, numalpha, special );
   657 type TCharSkip = ( none, wspace, numalpha, special );
   655 
   658 
   656 function GetInputCharSkipClass(index: LongInt): TCharSkip;
   659 function GetInputCharSkipClass(index: LongInt): TCharSkip;
   657 var  c: char;
   660 var  c: char;
   658 begin
   661 begin
   659     // multi-byte chars counts as letter
       
   660     if (index > 1) and (InputStrL[index] <> index - 1) then
       
   661         exit(numalpha);
       
   662 
       
   663     c:= InputStr.s[index];
   662     c:= InputStr.s[index];
   664 
   663 
   665     // non-ascii counts as letter
   664     // non-ascii counts as letter
   666     if c > #127 then
   665     if c > #127 then
   667         exit(numalpha);
   666         exit(numalpha);
   698     // skip trailing whitespace, similar to Qt
   697     // skip trailing whitespace, similar to Qt
   699     while (skip = wspace) and (cursorPos > 0) do
   698     while (skip = wspace) and (cursorPos > 0) do
   700         begin
   699         begin
   701         skip:= GetInputCharSkipClass(cursorPos);
   700         skip:= GetInputCharSkipClass(cursorPos);
   702         if skip = wspace then
   701         if skip = wspace then
   703             cursorPos:= InputStrL[cursorPos];
   702             MoveCursorToPreviousChar();
   704         end;
   703         end;
   705     // skip same-type chars
   704     // skip same-type chars
   706     while (cursorPos > 0) and (GetInputCharSkipClass(cursorPos) = skip) do
   705     while (cursorPos > 0) and (GetInputCharSkipClass(cursorPos) = skip) do
   707         cursorPos:= InputStrL[cursorPos];
   706         MoveCursorToPreviousChar();
   708     end
   707     end
   709 else
   708 else
   710     begin
   709     begin
   711     // skip same-type chars
   710     // skip same-type chars
   712     while cursorPos < Length(InputStr.s) do
   711     while cursorPos < Length(InputStr.s) do
   713         begin
   712         begin
   714         DoCursorStepForward();
   713         MoveCursorToNextChar();
   715         if (GetInputCharSkipClass(cursorPos) <> skip) then
   714         if (GetInputCharSkipClass(cursorPos) <> skip) then
   716             begin
   715             begin
   717             // go back 1 char
   716             MoveCursorToPreviousChar();
   718             cursorPos:= InputStrL[cursorPos];
       
   719             break;
   717             break;
   720             end;
   718             end;
   721         end;
   719         end;
   722     // skip trailing whitespace, similar to Qt
   720     // skip trailing whitespace, similar to Qt
   723     while cursorPos < Length(InputStr.s) do
   721     while cursorPos < Length(InputStr.s) do
   724         begin
   722         begin
   725         DoCursorStepForward();
   723         MoveCursorToNextChar();
   726         if (GetInputCharSkipClass(cursorPos) <> wspace) then
   724         if (GetInputCharSkipClass(cursorPos) <> wspace) then
   727             begin
   725             begin
   728             // go back 1 char
   726             MoveCursorToPreviousChar();
   729             cursorPos:= InputStrL[cursorPos];
       
   730             break;
   727             break;
   731             end;
   728             end;
   732         end;
   729         end;
   733     end;
   730     end;
   734 end;
   731 end;
   746         selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos));
   743         selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos));
   747         CopyToClipboard(selection);
   744         CopyToClipboard(selection);
   748         end;
   745         end;
   749 end;
   746 end;
   750 
   747 
   751 // TODO: honor utf8, don't break utf8 chars when shifting chars beyond limit
       
   752 procedure InsertIntoInputStr(s: shortstring);
   748 procedure InsertIntoInputStr(s: shortstring);
   753 var i, l, il, lastc: integer;
   749 var limit: integer;
   754 begin
   750 begin
   755     // safe length for string
   751     // we check limit for trailing stuff before insertion limit for a reason
   756     l:= min(MaxInputStrLen-cursorPos, Length(s));
   752     // (possible remaining space after too long UTF8-insertion has been shortened)
   757     s:= copy(s,1,l);
   753 
   758 
   754     // length limit for stuff to that will trail the insertion
   759     // if we insert rather than append, shift info in InputStrL accordingly
   755     limit:= max(cursorPos, MaxInputStrLen-Length(s));
   760     if cursorPos < Length(InputStr.s) then
   756 
   761         begin
   757     while Length(InputStr.s) > limit do
   762         for i:= Length(InputStr.s) downto cursorPos + 1 do
   758         begin
   763             begin
   759         DeleteLastUTF8CharFromStr(InputStr.s);
   764             if InputStrL[i] <> InputStrLNoPred then
   760         end;
   765                 begin
   761 
   766                 il:= i + l;
   762     // length limit for stuff to insert
   767                 // only shift if not overflowing
   763     limit:= max(0, MaxInputStrLen-cursorPos);
   768                 if il <= MaxInputStrLen then
   764 
   769                     InputStrL[il]:= InputStrL[i] + l;
   765     if limit = 0 then
   770                 InputStrL[i]:= InputStrLNoPred;
   766         s:= ''
   771                 end;
   767     else while Length(s) > limit do
   772             end;
   768         begin
   773         end;
   769         DeleteLastUTF8CharFromStr(s);
   774 
   770         end;
   775     InputStrL[cursorPos + l]:= cursorPos;
   771 
   776     // insert string truncated to safe length
   772     if Length(s) > 0 then
   777     Insert(s, InputStr.s, cursorPos + 1);
   773         begin
   778     if Length(InputStr.s) > MaxInputStrLen then
   774         // insert string truncated to safe length
   779         InputStr.s[0]:= char(MaxInputStrLen);
   775         Insert(s, InputStr.s, cursorPos + 1);
   780 
   776 
   781     SetLine(InputStr, InputStr.s, true);
   777         if Length(InputStr.s) > MaxInputStrLen then
   782 
   778             InputStr.s[0]:= char(MaxInputStrLen);
   783     // move cursor to end of inserted string
   779 
   784     lastc:= MaxInputStrLen;
   780         SetLine(InputStr, InputStr.s, true);
   785     cursorPos:= min(lastc, cursorPos + l);
   781 
   786     UpdateCursorCoords();
   782         // move cursor to end of inserted string
       
   783         inc(cursorPos, Length(s));
       
   784         UpdateCursorCoords();
       
   785         end;
   787 end;
   786 end;
   788 
   787 
   789 procedure PasteFromClipboard();
   788 procedure PasteFromClipboard();
   790 begin
   789 begin
   791     SendIPC(_S'Y');
   790     SendIPC(_S'Y');
   819     case Sym of
   818     case Sym of
   820         SDLK_BACKSPACE:
   819         SDLK_BACKSPACE:
   821             begin
   820             begin
   822             if selectedPos < 0 then
   821             if selectedPos < 0 then
   823                 begin
   822                 begin
       
   823                 HandleSelection(true);
       
   824 
       
   825                 // delete more if ctrl is held
   824                 if ctrl then
   826                 if ctrl then
   825                     skip:= GetInputCharSkipClass(cursorPos);
   827                     SkipInputChars(GetInputCharSkipClass(cursorPos), true)
   826 
   828                 else
   827                 // remove char before cursor
   829                     MoveCursorToPreviousChar();
   828                 dec(cursorPos, DelCharFromInputStr(cursorPos));
   830 
       
   831                 end;
       
   832 
       
   833             DeleteSelected();
       
   834             UpdateCursorCoords();
       
   835             end;
       
   836         SDLK_DELETE:
       
   837             begin
       
   838             if selectedPos < 0 then
       
   839                 begin
       
   840                 HandleSelection(true);
   829 
   841 
   830                 // delete more if ctrl is held
   842                 // delete more if ctrl is held
   831                 if ctrl and (selectedPos < 0) then
   843                 if ctrl then
   832                     begin
   844                     SkipInputChars(GetInputCharSkipClass(cursorPos), false)
   833                     HandleSelection(true);
       
   834                     SkipInputChars(skip, true);
       
   835                     DeleteSelected();
       
   836                     end
       
   837                 else
   845                 else
   838                     UpdateCursorCoords();
   846                     MoveCursorToNextChar();
   839 
   847 
   840                 end
   848                 end;
   841             else
   849 
   842                 DeleteSelected();
   850             DeleteSelected();
   843             end;
   851             UpdateCursorCoords();
   844         SDLK_DELETE:
       
   845             begin
       
   846             if selectedPos < 0 then
       
   847                 begin
       
   848                 // remove char after cursor
       
   849                 if cursorPos < Length(InputStr.s) then
       
   850                     begin
       
   851                     DoCursorStepForward();
       
   852                     if ctrl then
       
   853                         skip:= GetInputCharSkipClass(cursorPos);
       
   854 
       
   855                     // delete char
       
   856                     dec(cursorPos, DelCharFromInputStr(cursorPos));
       
   857 
       
   858                     // delete more if ctrl is held
       
   859                     if ctrl and (cursorPos < Length(InputStr.s)) then
       
   860                         begin
       
   861                         HandleSelection(true);
       
   862                         SkipInputChars(skip, false);
       
   863                         DeleteSelected();
       
   864                         end;
       
   865                     end
       
   866                 else
       
   867                     UpdateCursorCoords();
       
   868                 end
       
   869             else
       
   870                 DeleteSelected();
       
   871             end;
   852             end;
   872         SDLK_ESCAPE:
   853         SDLK_ESCAPE:
   873             begin
   854             begin
   874             if Length(InputStr.s) > 0 then
   855             if Length(InputStr.s) > 0 then
   875                 begin
   856                 begin
   876                 SetLine(InputStr, '', true);
   857                 SetLine(InputStr, '', true);
   877                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   878                 ResetCursor();
   858                 ResetCursor();
   879                 end
   859                 end
   880             else CleanupInput
   860             else CleanupInput
   881             end;
   861             end;
   882         SDLK_RETURN, SDLK_KP_ENTER:
   862         SDLK_RETURN, SDLK_KP_ENTER:
   883             begin
   863             begin
   884             if Length(InputStr.s) > 0 then
   864             if Length(InputStr.s) > 0 then
   885                 begin
   865                 begin
   886                 AcceptChatString(InputStr.s);
   866                 AcceptChatString(InputStr.s);
   887                 SetLine(InputStr, '', false);
   867                 SetLine(InputStr, '', false);
   888                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   889                 ResetCursor();
   868                 ResetCursor();
   890                 end;
   869                 end;
   891             CleanupInput
   870             CleanupInput
   892             end;
   871             end;
   893         SDLK_UP, SDLK_DOWN:
   872         SDLK_UP, SDLK_DOWN:
   896             if (Sym = SDLK_DOWN) and (history > 0) then dec(history);
   875             if (Sym = SDLK_DOWN) and (history > 0) then dec(history);
   897             index:= localLastStr - history + 1;
   876             index:= localLastStr - history + 1;
   898             if (index > localLastStr) then
   877             if (index > localLastStr) then
   899                 begin
   878                 begin
   900                 SetLine(InputStr, '', true);
   879                 SetLine(InputStr, '', true);
   901                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   902                 end
   880                 end
   903             else
   881             else
   904                 begin
   882                 begin
   905                 SetLine(InputStr, LocalStrs[index], true);
   883                 SetLine(InputStr, LocalStrs[index], true);
   906                 InputStrL:= LocalStrsL[index];
       
   907                 end;
   884                 end;
   908             cursorPos:= Length(InputStr.s);
   885             cursorPos:= Length(InputStr.s);
   909             ResetSelection();
   886             ResetSelection();
   910             UpdateCursorCoords();
   887             UpdateCursorCoords();
   911             end;
   888             end;
   944 
   921 
   945                 if selMode or (selectedPos < 0) then
   922                 if selMode or (selectedPos < 0) then
   946                     begin
   923                     begin
   947                     HandleSelection(selMode);
   924                     HandleSelection(selMode);
   948                     // go to end of previous utf8-char
   925                     // go to end of previous utf8-char
   949                     cursorPos:= InputStrL[cursorPos];
   926                     MoveCursorToPreviousChar();
   950                     end
   927                     end
   951                 else // if we're leaving selection mode, jump to its left end
   928                 else // if we're leaving selection mode, jump to its left end
   952                     begin
   929                     begin
   953                     cursorPos:= min(cursorPos, selectedPos);
   930                     cursorPos:= min(cursorPos, selectedPos);
   954                     ResetSelection();
   931                     ResetSelection();
   969                 begin
   946                 begin
   970 
   947 
   971                 if selMode or (selectedPos < 0) then
   948                 if selMode or (selectedPos < 0) then
   972                     begin
   949                     begin
   973                     HandleSelection(selMode);
   950                     HandleSelection(selMode);
   974                     DoCursorStepForward();
   951                     MoveCursorToNextChar();
   975                     end
   952                     end
   976                 else // if we're leaving selection mode, jump to its right end
   953                 else // if we're leaving selection mode, jump to its right end
   977                     begin
   954                     begin
   978                     cursorPos:= max(cursorPos, selectedPos);
   955                     cursorPos:= max(cursorPos, selectedPos);
   979                     ResetSelection();
   956                     ResetSelection();
  1016             end;
   993             end;
  1017         SDLK_v:
   994         SDLK_v:
  1018             begin
   995             begin
  1019             // paste
   996             // paste
  1020             if ctrl then
   997             if ctrl then
  1021                 PasteFromClipboard()
   998                 begin
       
   999                 DeleteSelected();
       
  1000                 PasteFromClipboard();
       
  1001                 end
  1022             else
  1002             else
  1023                 action:= false;
  1003                 action:= false;
  1024             end;
  1004             end;
  1025         SDLK_x:
  1005         SDLK_x:
  1026             begin
  1006             begin
  1060         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
  1040         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
  1061 
  1041 
  1062         if Length(InputStr.s) + btw > MaxInputStrLen then
  1042         if Length(InputStr.s) + btw > MaxInputStrLen then
  1063             exit;
  1043             exit;
  1064 
  1044 
       
  1045         // if speech bubble quotes are used as first input, add the closing quote and place cursor inbetween
  1065         if (Length(InputStr.s) = 0) and (Length(utf8) = 1) and (charIsForHogSpeech(utf8[1])) then
  1046         if (Length(InputStr.s) = 0) and (Length(utf8) = 1) and (charIsForHogSpeech(utf8[1])) then
  1066             begin
  1047             begin
  1067             InsertIntoInputStr(utf8);
  1048             InsertIntoInputStr(utf8);
  1068             InsertIntoInputStr(utf8);
  1049             InsertIntoInputStr(utf8);
  1069             cursorPos:= 1;
  1050             cursorPos:= 1;
  1125     if length(s) = 0 then
  1106     if length(s) = 0 then
  1126         SetLine(InputStr, '', true)
  1107         SetLine(InputStr, '', true)
  1127     else
  1108     else
  1128         begin
  1109         begin
  1129         SetLine(InputStr, '/team ', true);
  1110         SetLine(InputStr, '/team ', true);
  1130         // update InputStrL and cursor accordingly
       
  1131         // this allows cursor-jumping over '/team ' as if it was a single char
       
  1132         InputStrL[6]:= 0;
       
  1133         cursorPos:= 6;
  1111         cursorPos:= 6;
  1134         UpdateCursorCoords();
  1112         UpdateCursorCoords();
  1135         end;
  1113         end;
  1136 end;
  1114 end;
  1137 
  1115 
  1160     inputStr.s:= '';
  1138     inputStr.s:= '';
  1161     inputStr.Tex := nil;
  1139     inputStr.Tex := nil;
  1162     for i:= 0 to MaxStrIndex do
  1140     for i:= 0 to MaxStrIndex do
  1163         Strs[i].Tex := nil;
  1141         Strs[i].Tex := nil;
  1164 
  1142 
  1165     FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
  1166 
       
  1167     LastKeyPressTick:= 0;
  1143     LastKeyPressTick:= 0;
  1168     ResetCursor();
  1144     ResetCursor();
  1169 end;
  1145 end;
  1170 
  1146 
  1171 procedure freeModule;
  1147 procedure freeModule;