hedgewars/uChat.pas
branchqmlfrontend
changeset 10886 99273b7afbff
parent 10868 acb03a9712c3
child 10919 8aed2bfc43c5
equal deleted inserted replaced
10823:1ff3dd3705b1 10886:99273b7afbff
    26 procedure freeModule;
    26 procedure freeModule;
    27 procedure ReloadLines;
    27 procedure ReloadLines;
    28 procedure CleanupInput;
    28 procedure CleanupInput;
    29 procedure AddChatString(s: shortstring);
    29 procedure AddChatString(s: shortstring);
    30 procedure DrawChat;
    30 procedure DrawChat;
    31 procedure KeyPressChat(Key, Sym: Longword);
    31 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
    32 procedure SendHogSpeech(s: shortstring);
    32 procedure SendHogSpeech(s: shortstring);
       
    33 procedure CopyToClipboard(var newContent: shortstring);
    33 
    34 
    34 implementation
    35 implementation
    35 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;
    36 
    37 
    37 const MaxStrIndex = 27;
    38 const MaxStrIndex = 27;
       
    39       MaxInputStrLen = 240;
    38 
    40 
    39 type TChatLine = record
    41 type TChatLine = record
    40     Tex: PTexture;
    42     Tex: PTexture;
    41     Time: Longword;
    43     Time: Longword;
    42     Width: LongInt;
    44     Width: LongInt;
    43     s: shortstring;
    45     s: shortstring;
    44     Color: TSDL_Color;
    46     Color: TSDL_Color;
    45     end;
    47     end;
    46     TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen);
    48     TChatCmd = (ccQuit, ccPause, ccFinish, ccShowHistory, ccFullScreen);
    47 
    49 
       
    50 type TInputStrL = array[0..260] of byte;
       
    51 
    48 var Strs: array[0 .. MaxStrIndex] of TChatLine;
    52 var Strs: array[0 .. MaxStrIndex] of TChatLine;
    49     MStrs: array[0 .. MaxStrIndex] of shortstring;
    53     MStrs: array[0 .. MaxStrIndex] of shortstring;
    50     LocalStrs: array[0 .. MaxStrIndex] of shortstring;
    54     LocalStrs: array[0 .. MaxStrIndex] of shortstring;
       
    55     LocalStrsL: array[0 .. MaxStrIndex] of TInputStrL;
    51     missedCount: LongWord;
    56     missedCount: LongWord;
    52     lastStr: LongWord;
    57     lastStr: LongWord;
    53     localLastStr: LongInt;
    58     localLastStr: LongInt;
    54     history: LongInt;
    59     history: LongInt;
    55     visibleCount: LongWord;
    60     visibleCount: LongWord;
    56     InputStr: TChatLine;
    61     InputStr: TChatLine;
    57     InputStrL: array[0..260] of char; // for full str + 4-byte utf-8 char
    62     InputStrL: TInputStrL; // for full str + 4-byte utf-8 char
    58     ChatReady: boolean;
    63     ChatReady: boolean;
    59     showAll: boolean;
    64     showAll: boolean;
    60     liveLua: boolean;
    65     liveLua: boolean;
    61     ChatHidden: boolean;
    66     ChatHidden: boolean;
       
    67     firstDraw: boolean;
       
    68     InputLinePrefix: TChatLine;
       
    69     // cursor
       
    70     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
       
    71     LastKeyPressTick: LongWord;
       
    72 
    62 
    73 
    63 const
    74 const
       
    75     InputStrLNoPred: byte = 255;
       
    76 
    64     colors: array[#0..#6] of TSDL_Color = (
    77     colors: array[#0..#6] of TSDL_Color = (
    65             (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything
    78             (r:$FF; g:$FF; b:$FF; a:$FF), // unused, feel free to take it for anything
    66             (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White]
    79             (r:$FF; g:$FF; b:$FF; a:$FF), // chat message [White]
    67             (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple]
    80             (r:$FF; g:$00; b:$FF; a:$FF), // action message [Purple]
    68             (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime]
    81             (r:$90; g:$FF; b:$90; a:$FF), // join/leave message [Lime]
    83 
    96 
    84 
    97 
    85 const Padding  = 2;
    98 const Padding  = 2;
    86       ClHeight = 2 * Padding + 16; // font height
    99       ClHeight = 2 * Padding + 16; // font height
    87 
   100 
       
   101 function charIsForHogSpeech(c: char): boolean;
       
   102 begin
       
   103 exit((c = '"') or (c = '''') or (c = '-'));
       
   104 end;
       
   105 
       
   106 procedure ResetSelection();
       
   107 begin
       
   108     selectedPos:= -1;
       
   109 end;
       
   110 
       
   111 procedure UpdateCursorCoords();
       
   112 var font: THWFont;
       
   113     str : shortstring;
       
   114     coff, soff: LongInt;
       
   115 begin
       
   116     if cursorPos = selectedPos then
       
   117         ResetSelection();
       
   118 
       
   119     // calculate cursor offset
       
   120 
       
   121     str:= InputStr.s;
       
   122     font:= CheckCJKFont(ansistring(str), fnt16);
       
   123 
       
   124     // get only substring before cursor to determine length
       
   125     // SetLength(str, cursorPos); // makes pas2c unhappy
       
   126     str[0]:= char(cursorPos);
       
   127     // get render size of text
       
   128     TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil);
       
   129 
       
   130     cursorX:= 2 + coff;
       
   131 
       
   132     // calculate selection width on screen
       
   133     if selectedPos >= 0 then
       
   134         begin
       
   135         if selectedPos > cursorPos then
       
   136             str:= InputStr.s;
       
   137         // SetLength(str, selectedPos); // makes pas2c unhappy
       
   138         str[0]:= char(selectedPos);
       
   139         TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @soff, nil);
       
   140         selectionDx:= soff - coff;
       
   141         end
       
   142     else
       
   143         selectionDx:= 0;
       
   144 end;
       
   145 
       
   146 
       
   147 procedure ResetCursor();
       
   148 begin
       
   149     ResetSelection();
       
   150     cursorPos:= 0;
       
   151     UpdateCursorCoords();
       
   152 end;
       
   153 
    88 procedure RenderChatLineTex(var cl: TChatLine; var str: shortstring);
   154 procedure RenderChatLineTex(var cl: TChatLine; var str: shortstring);
    89 var strSurface,
   155 var strSurface,
    90     resSurface: PSDL_Surface;
   156     resSurface: PSDL_Surface;
    91     dstrect   : TSDL_Rect; // destination rectangle for blitting
   157     dstrect   : TSDL_Rect; // destination rectangle for blitting
    92     font      : THWFont;
   158     font      : THWFont;
   137 
   203 
   138 if isInput then
   204 if isInput then
   139     begin
   205     begin
   140     cl.s:= str;
   206     cl.s:= str;
   141     color:= colors[#6];
   207     color:= colors[#6];
   142     str:= UserNick + '> ' + str + '_'
   208     str:= str + ' ';
   143     end
   209     end
   144 else
   210 else
   145     begin
   211     begin
   146     color:= colors[str[1]];
   212     if str[1] <= High(colors) then
   147     delete(str, 1, 1);
   213         begin
       
   214         color:= colors[str[1]];
       
   215         delete(str, 1, 1);
       
   216         end
       
   217     // fallback if invalid color
       
   218     else
       
   219         color:= colors[Low(colors)];
       
   220 
   148     cl.s:= str;
   221     cl.s:= str;
   149     end;
   222     end;
   150 
   223 
   151 cl.color:= color;
   224 cl.color:= color;
   152 
   225 
   188 SetLine(Strs[lastStr], s, false);
   261 SetLine(Strs[lastStr], s, false);
   189 
   262 
   190 inc(visibleCount)
   263 inc(visibleCount)
   191 end;
   264 end;
   192 
   265 
       
   266 procedure CheckPasteBuffer(); forward;
       
   267 
       
   268 procedure UpdateInputLinePrefix();
       
   269 begin
       
   270 if liveLua then
       
   271     begin
       
   272     InputLinePrefix.color:= colors[#1];
       
   273     InputLinePrefix.s:= '[Lua] >';
       
   274     end
       
   275 else
       
   276     begin
       
   277     InputLinePrefix.color:= colors[#6];
       
   278     InputLinePrefix.s:= UserNick + '>';
       
   279     end;
       
   280 
       
   281 FreeAndNilTexture(InputLinePrefix.Tex);
       
   282 end;
       
   283 
   193 procedure DrawChat;
   284 procedure DrawChat;
   194 var i, t, left, top, cnt: LongInt;
   285 var i, t, left, top, cnt: LongInt;
       
   286     selRect: TSDL_Rect;
       
   287     c: char;
   195 begin
   288 begin
   196 ChatReady:= true; // maybe move to somewhere else?
   289 ChatReady:= true; // maybe move to somewhere else?
   197 
   290 
   198 if ChatHidden and (not showAll) then
   291 if ChatHidden and (not showAll) then
   199     visibleCount:= 0;
   292     visibleCount:= 0;
   202 left:= 4 - cScreenWidth div 2;
   295 left:= 4 - cScreenWidth div 2;
   203 top := 10 + visibleCount * ClHeight; // we start with input line (if any)
   296 top := 10 + visibleCount * ClHeight; // we start with input line (if any)
   204 
   297 
   205 // draw chat input line first and under all other lines
   298 // draw chat input line first and under all other lines
   206 if (GameState = gsChat) and (InputStr.Tex <> nil) then
   299 if (GameState = gsChat) and (InputStr.Tex <> nil) then
       
   300     begin
       
   301     CheckPasteBuffer();
       
   302 
       
   303     if InputLinePrefix.Tex = nil then
       
   304         RenderChatLineTex(InputLinePrefix, InputLinePrefix.s);
       
   305 
       
   306     DrawTexture(left, top, InputLinePrefix.Tex);
       
   307     inc(left, InputLinePrefix.Width);
   207     DrawTexture(left, top, InputStr.Tex);
   308     DrawTexture(left, top, InputStr.Tex);
   208 
   309 
       
   310     if firstDraw then
       
   311         begin
       
   312         UpdateCursorCoords();
       
   313         firstDraw:= false;
       
   314         end;
       
   315 
       
   316     if selectedPos < 0 then
       
   317         begin
       
   318         // draw cursor
       
   319         if ((RealTicks - LastKeyPressTick) and 512) < 256 then
       
   320             DrawLineOnScreen(left + cursorX, top + 2, left + cursorX, top + ClHeight - 2, 2.0, $00, $FF, $FF, $FF);
       
   321         end
       
   322     else // draw selection
       
   323         begin
       
   324         selRect.y:= top + 2;
       
   325         selRect.h:= clHeight - 4;
       
   326         if selectionDx < 0 then
       
   327             begin
       
   328             selRect.x:= left + cursorX + selectionDx;
       
   329             selRect.w:= -selectionDx;
       
   330             end
       
   331         else
       
   332             begin
       
   333             selRect.x:= left + cursorX;
       
   334             selRect.w:= selectionDx;
       
   335             end;
       
   336 
       
   337         DrawRect(selRect, $FF, $FF, $FF, $40, true);
       
   338         end;
       
   339 
       
   340     dec(left, InputLinePrefix.Width);
       
   341 
       
   342 
       
   343     if (Length(InputStr.s) > 0) and ((CursorPos = 1) or (CursorPos = 2)) then
       
   344         begin
       
   345         c:= InputStr.s[1];
       
   346         if charIsForHogSpeech(c) then
       
   347             begin
       
   348             SpeechHogNumber:= 0;
       
   349             if Length(InputStr.s) > 1 then
       
   350                 begin
       
   351                 c:= InputStr.s[2];
       
   352                 if (c > '0') and (c < '9') then
       
   353                     SpeechHogNumber:= byte(c) - 48;
       
   354                 end;
       
   355             // default to current hedgehog (if own) or first hedgehog
       
   356             if SpeechHogNumber = 0 then
       
   357                 begin
       
   358                 if not CurrentTeam^.ExtDriven then
       
   359                     SpeechHogNumber:= CurrentTeam^.CurrHedgehog + 1
       
   360                 else
       
   361                     SpeechHogNumber:= 1;
       
   362                 end;
       
   363             end;
       
   364         end
       
   365     else
       
   366         SpeechHogNumber:= -1;
       
   367     end
       
   368 else
       
   369     SpeechHogNumber:= -1;
       
   370 
       
   371 // draw chat lines
   209 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then
   372 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then
   210     begin
   373     begin
   211     if MissedCount <> 0 then // there are chat strings we missed, so print them now
   374     if MissedCount <> 0 then // there are chat strings we missed, so print them now
   212         begin
   375         begin
   213         for i:= 0 to MissedCount - 1 do
   376         for i:= 0 to MissedCount - 1 do
   262 if s <> LocalStrs[localLastStr] then
   425 if s <> LocalStrs[localLastStr] then
   263     begin
   426     begin
   264     // put in input history
   427     // put in input history
   265     localLastStr:= (localLastStr + 1) mod MaxStrIndex;
   428     localLastStr:= (localLastStr + 1) mod MaxStrIndex;
   266     LocalStrs[localLastStr]:= s;
   429     LocalStrs[localLastStr]:= s;
       
   430     LocalStrsL[localLastStr]:= InputStrL;
   267     end;
   431     end;
   268 
   432 
   269 t:= LocalTeam;
   433 t:= LocalTeam;
   270 x:= 0;
   434 x:= 0;
   271 if (s[1] = '"') and (s[Length(s)] = '"')
   435 if (s[1] = '"') and (s[Length(s)] = '"')
   363             else
   527             else
   364                 begin
   528                 begin
   365                 AddFileLog('[Lua] chat input string parsing disabled');
   529                 AddFileLog('[Lua] chat input string parsing disabled');
   366                 AddChatString(#3 + 'Lua parsing: OFF');
   530                 AddChatString(#3 + 'Lua parsing: OFF');
   367                 end;
   531                 end;
       
   532             UpdateInputLinePrefix();
   368             end;
   533             end;
   369         exit
   534         exit
   370         end;
   535         end;
   371 
   536 
   372     // hedghog animations/taunts and engine commands
   537     // hedghog animations/taunts and engine commands
   408 {$ENDIF}
   573 {$ENDIF}
   409     GameState:= gsGame;
   574     GameState:= gsGame;
   410     ResetKbd;
   575     ResetKbd;
   411 end;
   576 end;
   412 
   577 
   413 procedure KeyPressChat(Key, Sym: Longword);
   578 procedure DelBytesFromInputStrBack(endIdx: integer; count: byte);
       
   579 var i, startIdx: integer;
       
   580 begin
       
   581     // nothing to do if count is 0
       
   582     if count = 0 then
       
   583         exit;
       
   584 
       
   585     // first byte to delete
       
   586     startIdx:= endIdx - (count - 1);
       
   587 
       
   588     // delete bytes from string
       
   589     Delete(InputStr.s, startIdx, count);
       
   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);
       
   605 end;
       
   606 
       
   607 // returns count of removed bytes
       
   608 function DelCharFromInputStr(idx: integer): integer;
       
   609 var btw: byte;
       
   610 begin
       
   611     // note: idx is always at last byte of utf8 chars. cuz relevant for InputStrL
       
   612 
       
   613     if (Length(InputStr.s) < 1) or (idx < 1) or (idx > Length(InputStr.s)) then
       
   614         exit(0);
       
   615 
       
   616     btw:= byte(idx) - InputStrL[idx];
       
   617 
       
   618     DelCharFromInputStr:= btw;
       
   619 
       
   620     DelBytesFromInputStrBack(idx, btw);
       
   621 end;
       
   622 
       
   623 // unchecked
       
   624 procedure DoCursorStepForward();
       
   625 begin
       
   626     // go to end of next utf8-char
       
   627     repeat
       
   628         inc(cursorPos);
       
   629     until InputStrL[cursorPos] <> InputStrLNoPred;
       
   630 end;
       
   631 
       
   632 procedure DeleteSelected();
       
   633 begin
       
   634     if (selectedPos >= 0) and (cursorPos <> selectedPos) then
       
   635         begin
       
   636         DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
       
   637         cursorPos:= min(cursorPos, selectedPos);
       
   638         ResetSelection();
       
   639         end;
       
   640     UpdateCursorCoords();
       
   641 end;
       
   642 
       
   643 procedure HandleSelection(enabled: boolean);
       
   644 begin
       
   645 if enabled then
       
   646     begin
       
   647     if selectedPos < 0 then
       
   648         selectedPos:= cursorPos;
       
   649     end
       
   650 else
       
   651     ResetSelection();
       
   652 end;
       
   653 
       
   654 type TCharSkip = ( none, wspace, numalpha, special );
       
   655 
       
   656 function GetInputCharSkipClass(index: LongInt): TCharSkip;
       
   657 var  c: char;
       
   658 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];
       
   664 
       
   665     // non-ascii counts as letter
       
   666     if c > #127 then
       
   667         exit(numalpha);
       
   668 
       
   669     // low-ascii whitespaces and DEL
       
   670     if (c < #33) or (c = #127) then
       
   671         exit(wspace);
       
   672 
       
   673     // low-ascii special chars
       
   674     if c < #48 then
       
   675         exit(special);
       
   676 
       
   677     // digits
       
   678     if c < #58 then
       
   679         exit(numalpha);
       
   680 
       
   681     // make c upper-case
       
   682     if c > #96 then
       
   683         c:= char(byte(c) - 32);
       
   684 
       
   685     // letters
       
   686     if (c > #64) and (c < #90) then
       
   687         exit(numalpha);
       
   688 
       
   689     // remaining ascii are special chars
       
   690     exit(special);
       
   691 end;
       
   692 
       
   693 // skip from word to word, similar to Qt
       
   694 procedure SkipInputChars(skip: TCharSkip; backwards: boolean);
       
   695 begin
       
   696 if backwards then
       
   697     begin
       
   698     // skip trailing whitespace, similar to Qt
       
   699     while (skip = wspace) and (cursorPos > 0) do
       
   700         begin
       
   701         skip:= GetInputCharSkipClass(cursorPos);
       
   702         if skip = wspace then
       
   703             cursorPos:= InputStrL[cursorPos];
       
   704         end;
       
   705     // skip same-type chars
       
   706     while (cursorPos > 0) and (GetInputCharSkipClass(cursorPos) = skip) do
       
   707         cursorPos:= InputStrL[cursorPos];
       
   708     end
       
   709 else
       
   710     begin
       
   711     // skip same-type chars
       
   712     while cursorPos < Length(InputStr.s) do
       
   713         begin
       
   714         DoCursorStepForward();
       
   715         if (GetInputCharSkipClass(cursorPos) <> skip) then
       
   716             begin
       
   717             // go back 1 char
       
   718             cursorPos:= InputStrL[cursorPos];
       
   719             break;
       
   720             end;
       
   721         end;
       
   722     // skip trailing whitespace, similar to Qt
       
   723     while cursorPos < Length(InputStr.s) do
       
   724         begin
       
   725         DoCursorStepForward();
       
   726         if (GetInputCharSkipClass(cursorPos) <> wspace) then
       
   727             begin
       
   728             // go back 1 char
       
   729             cursorPos:= InputStrL[cursorPos];
       
   730             break;
       
   731             end;
       
   732         end;
       
   733     end;
       
   734 end;
       
   735 
       
   736 procedure CopyToClipboard(var newContent: shortstring);
       
   737 begin
       
   738     SendIPC(_S'y' + copy(newContent, 1, 253) + #0);
       
   739 end;
       
   740 
       
   741 procedure CopySelectionToClipboard();
       
   742 var selection: shortstring;
       
   743 begin
       
   744     if selectedPos >= 0 then
       
   745         begin
       
   746         selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos));
       
   747         CopyToClipboard(selection);
       
   748         end;
       
   749 end;
       
   750 
       
   751 // TODO: honor utf8, don't break utf8 chars when shifting chars beyond limit
       
   752 procedure InsertIntoInputStr(s: shortstring);
       
   753 var i, l, il, lastc: integer;
       
   754 begin
       
   755     // safe length for string
       
   756     l:= min(MaxInputStrLen-cursorPos, Length(s));
       
   757     s:= copy(s,1,l);
       
   758 
       
   759     // if we insert rather than append, shift info in InputStrL accordingly
       
   760     if cursorPos < Length(InputStr.s) then
       
   761         begin
       
   762         for i:= Length(InputStr.s) downto cursorPos + 1 do
       
   763             begin
       
   764             if InputStrL[i] <> InputStrLNoPred then
       
   765                 begin
       
   766                 il:= i + l;
       
   767                 // only shift if not overflowing
       
   768                 if il <= MaxInputStrLen then
       
   769                     InputStrL[il]:= InputStrL[i] + l;
       
   770                 InputStrL[i]:= InputStrLNoPred;
       
   771                 end;
       
   772             end;
       
   773         end;
       
   774 
       
   775     InputStrL[cursorPos + l]:= cursorPos;
       
   776     // insert string truncated to safe length
       
   777     Insert(s, InputStr.s, cursorPos + 1);
       
   778     if Length(InputStr.s) > MaxInputStrLen then
       
   779         InputStr.s[0]:= char(MaxInputStrLen);
       
   780 
       
   781     SetLine(InputStr, InputStr.s, true);
       
   782 
       
   783     // move cursor to end of inserted string
       
   784     lastc:= MaxInputStrLen;
       
   785     cursorPos:= min(lastc, cursorPos + l);
       
   786     UpdateCursorCoords();
       
   787 end;
       
   788 
       
   789 procedure PasteFromClipboard();
       
   790 begin
       
   791     SendIPC(_S'Y');
       
   792 end;
       
   793 
       
   794 procedure CheckPasteBuffer();
       
   795 begin
       
   796     if Length(ChatPasteBuffer) > 0 then
       
   797         begin
       
   798         InsertIntoInputStr(ChatPasteBuffer);
       
   799         ChatPasteBuffer:= '';
       
   800         end;
       
   801 end;
       
   802 
       
   803 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
   414 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
   804 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
   415 var i, btw, index: integer;
   805 var i, btw, index: integer;
   416     utf8: shortstring;
   806     utf8: shortstring;
   417     action: boolean;
   807     action, selMode, ctrl: boolean;
   418 begin
   808     skip: TCharSkip;
       
   809 begin
       
   810     LastKeyPressTick:= RealTicks;
   419     action:= true;
   811     action:= true;
       
   812 
       
   813     CheckPasteBuffer();
       
   814 
       
   815     selMode:= (modifier and (KMOD_LSHIFT or KMOD_RSHIFT)) <> 0;
       
   816     ctrl:= (modifier and (KMOD_LCTRL or KMOD_RCTRL)) <> 0;
       
   817     skip:= none;
       
   818 
   420     case Sym of
   819     case Sym of
   421         SDLK_BACKSPACE:
   820         SDLK_BACKSPACE:
   422             begin
   821             begin
       
   822             if selectedPos < 0 then
       
   823                 begin
       
   824                 if ctrl then
       
   825                     skip:= GetInputCharSkipClass(cursorPos);
       
   826 
       
   827                 // remove char before cursor
       
   828                 dec(cursorPos, DelCharFromInputStr(cursorPos));
       
   829 
       
   830                 // delete more if ctrl is held
       
   831                 if ctrl and (selectedPos < 0) then
       
   832                     begin
       
   833                     HandleSelection(true);
       
   834                     SkipInputChars(skip, true);
       
   835                     DeleteSelected();
       
   836                     end
       
   837                 else
       
   838                     UpdateCursorCoords();
       
   839 
       
   840                 end
       
   841             else
       
   842                 DeleteSelected();
       
   843             end;
       
   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;
       
   872         SDLK_ESCAPE:
       
   873             begin
   423             if Length(InputStr.s) > 0 then
   874             if Length(InputStr.s) > 0 then
   424                 begin
   875                 begin
   425                 InputStr.s[0]:= InputStrL[byte(InputStr.s[0])];
   876                 SetLine(InputStr, '', true);
   426                 SetLine(InputStr, InputStr.s, true)
   877                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   878                 ResetCursor();
   427                 end
   879                 end
   428             end;
   880             else CleanupInput
   429         SDLK_ESCAPE:
   881             end;
       
   882         SDLK_RETURN, SDLK_KP_ENTER:
   430             begin
   883             begin
   431             if Length(InputStr.s) > 0 then
   884             if Length(InputStr.s) > 0 then
   432                 SetLine(InputStr, '', true)
       
   433             else CleanupInput
       
   434             end;
       
   435         SDLK_RETURN, SDLK_KP_ENTER:
       
   436             begin
       
   437             if Length(InputStr.s) > 0 then
       
   438                 begin
   885                 begin
   439                 AcceptChatString(InputStr.s);
   886                 AcceptChatString(InputStr.s);
   440                 SetLine(InputStr, '', false)
   887                 SetLine(InputStr, '', false);
       
   888                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
   889                 ResetCursor();
   441                 end;
   890                 end;
   442             CleanupInput
   891             CleanupInput
   443             end;
   892             end;
   444         SDLK_UP, SDLK_DOWN:
   893         SDLK_UP, SDLK_DOWN:
   445             begin
   894             begin
   446             if (Sym = SDLK_UP) and (history < localLastStr) then inc(history);
   895             if (Sym = SDLK_UP) and (history < localLastStr) then inc(history);
   447             if (Sym = SDLK_DOWN) and (history > 0) then dec(history);
   896             if (Sym = SDLK_DOWN) and (history > 0) then dec(history);
   448             index:= localLastStr - history + 1;
   897             index:= localLastStr - history + 1;
   449             if (index > localLastStr) then
   898             if (index > localLastStr) then
   450                  SetLine(InputStr, '', true)
   899                 begin
   451             else SetLine(InputStr, LocalStrs[index], true)
   900                 SetLine(InputStr, '', true);
   452             end;
   901                 FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
   453         SDLK_RIGHT, SDLK_LEFT, SDLK_DELETE,
   902                 end
   454         SDLK_HOME, SDLK_END,
   903             else
       
   904                 begin
       
   905                 SetLine(InputStr, LocalStrs[index], true);
       
   906                 InputStrL:= LocalStrsL[index];
       
   907                 end;
       
   908             cursorPos:= Length(InputStr.s);
       
   909             ResetSelection();
       
   910             UpdateCursorCoords();
       
   911             end;
       
   912         SDLK_HOME:
       
   913             begin
       
   914             if cursorPos > 0 then
       
   915                 begin
       
   916                 HandleSelection(selMode);
       
   917                 cursorPos:= 0;
       
   918                 end
       
   919             else if (not selMode) then
       
   920                 ResetSelection();
       
   921 
       
   922             UpdateCursorCoords();
       
   923             end;
       
   924         SDLK_END:
       
   925             begin
       
   926             i:= Length(InputStr.s);
       
   927             if cursorPos < i then
       
   928                 begin
       
   929                 HandleSelection(selMode);
       
   930                 cursorPos:= i;
       
   931                 end
       
   932             else if (not selMode) then
       
   933                 ResetSelection();
       
   934 
       
   935             UpdateCursorCoords();
       
   936             end;
       
   937         SDLK_LEFT:
       
   938             begin
       
   939             if cursorPos > 0 then
       
   940                 begin
       
   941 
       
   942                 if ctrl then
       
   943                     skip:= GetInputCharSkipClass(cursorPos);
       
   944 
       
   945                 if selMode or (selectedPos < 0) then
       
   946                     begin
       
   947                     HandleSelection(selMode);
       
   948                     // go to end of previous utf8-char
       
   949                     cursorPos:= InputStrL[cursorPos];
       
   950                     end
       
   951                 else // if we're leaving selection mode, jump to its left end
       
   952                     begin
       
   953                     cursorPos:= min(cursorPos, selectedPos);
       
   954                     ResetSelection();
       
   955                     end;
       
   956 
       
   957                 if ctrl then
       
   958                     SkipInputChars(skip, true);
       
   959 
       
   960                 end
       
   961             else if (not selMode) then
       
   962                 ResetSelection();
       
   963 
       
   964             UpdateCursorCoords();
       
   965             end;
       
   966         SDLK_RIGHT:
       
   967             begin
       
   968             if cursorPos < Length(InputStr.s) then
       
   969                 begin
       
   970 
       
   971                 if selMode or (selectedPos < 0) then
       
   972                     begin
       
   973                     HandleSelection(selMode);
       
   974                     DoCursorStepForward();
       
   975                     end
       
   976                 else // if we're leaving selection mode, jump to its right end
       
   977                     begin
       
   978                     cursorPos:= max(cursorPos, selectedPos);
       
   979                     ResetSelection();
       
   980                     end;
       
   981 
       
   982                 if ctrl then
       
   983                     SkipInputChars(GetInputCharSkipClass(cursorPos), false);
       
   984 
       
   985                 end
       
   986             else if (not selMode) then
       
   987                 ResetSelection();
       
   988 
       
   989             UpdateCursorCoords();
       
   990             end;
   455         SDLK_PAGEUP, SDLK_PAGEDOWN:
   991         SDLK_PAGEUP, SDLK_PAGEDOWN:
   456             begin
   992             begin
   457             // ignore me!!!
   993             // ignore me!!!
       
   994             end;
       
   995         SDLK_a:
       
   996             begin
       
   997             // select all
       
   998             if ctrl then
       
   999                 begin
       
  1000                 ResetSelection();
       
  1001                 cursorPos:= 0;
       
  1002                 HandleSelection(true);
       
  1003                 cursorPos:= Length(InputStr.s);
       
  1004                 UpdateCursorCoords();
       
  1005                 end
       
  1006             else
       
  1007                 action:= false;
       
  1008             end;
       
  1009         SDLK_c:
       
  1010             begin
       
  1011             // copy
       
  1012             if ctrl then
       
  1013                 CopySelectionToClipboard()
       
  1014             else
       
  1015                 action:= false;
       
  1016             end;
       
  1017         SDLK_v:
       
  1018             begin
       
  1019             // paste
       
  1020             if ctrl then
       
  1021                 PasteFromClipboard()
       
  1022             else
       
  1023                 action:= false;
       
  1024             end;
       
  1025         SDLK_x:
       
  1026             begin
       
  1027             // cut
       
  1028             if ctrl then
       
  1029                 begin
       
  1030                 CopySelectionToClipboard();
       
  1031                 DeleteSelected();
       
  1032                 end
       
  1033             else
       
  1034                 action:= false;
   458             end;
  1035             end;
   459         else
  1036         else
   460             action:= false;
  1037             action:= false;
   461         end;
  1038         end;
   462     if not action and (Key <> 0) then
  1039     if not action and (Key <> 0) then
   463         begin
  1040         begin
       
  1041         DeleteSelected();
       
  1042 
   464         if (Key < $80) then
  1043         if (Key < $80) then
   465             btw:= 1
  1044             btw:= 1
   466         else if (Key < $800) then
  1045         else if (Key < $800) then
   467             btw:= 2
  1046             btw:= 2
   468         else if (Key < $10000) then
  1047         else if (Key < $10000) then
   478             Key:= Key shr 6
  1057             Key:= Key shr 6
   479             end;
  1058             end;
   480 
  1059 
   481         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
  1060         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
   482 
  1061 
   483         if byte(InputStr.s[0]) + btw > 240 then
  1062         if Length(InputStr.s) + btw > MaxInputStrLen then
   484             exit;
  1063             exit;
   485 
  1064 
   486         InputStrL[byte(InputStr.s[0]) + btw]:= InputStr.s[0];
  1065         if (Length(InputStr.s) = 0) and (Length(utf8) = 1) and (charIsForHogSpeech(utf8[1])) then
   487         SetLine(InputStr, InputStr.s + utf8, true)
  1066             begin
       
  1067             InsertIntoInputStr(utf8);
       
  1068             InsertIntoInputStr(utf8);
       
  1069             cursorPos:= 1;
       
  1070             UpdateCursorCoords();
       
  1071             end
       
  1072         else
       
  1073             InsertIntoInputStr(utf8);
   488         end
  1074         end
   489 end;
  1075 end;
   490 
  1076 
   491 procedure chChatMessage(var s: shortstring);
  1077 procedure chChatMessage(var s: shortstring);
   492 begin
  1078 begin
   537     SDL_EnableKeyRepeat(200,45);
  1123     SDL_EnableKeyRepeat(200,45);
   538 {$ENDIF}
  1124 {$ENDIF}
   539     if length(s) = 0 then
  1125     if length(s) = 0 then
   540         SetLine(InputStr, '', true)
  1126         SetLine(InputStr, '', true)
   541     else
  1127     else
   542         SetLine(InputStr, '/team ', true)
  1128         begin
       
  1129         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;
       
  1134         UpdateCursorCoords();
       
  1135         end;
   543 end;
  1136 end;
   544 
  1137 
   545 procedure initModule;
  1138 procedure initModule;
   546 var i: ShortInt;
  1139 var i: ShortInt;
   547 begin
  1140 begin
   558     showAll:= false;
  1151     showAll:= false;
   559     ChatReady:= false;
  1152     ChatReady:= false;
   560     missedCount:= 0;
  1153     missedCount:= 0;
   561     liveLua:= false;
  1154     liveLua:= false;
   562     ChatHidden:= false;
  1155     ChatHidden:= false;
   563 
  1156     firstDraw:= true;
       
  1157 
       
  1158     InputLinePrefix.Tex:= nil;
       
  1159     UpdateInputLinePrefix();
       
  1160     inputStr.s:= '';
   564     inputStr.Tex := nil;
  1161     inputStr.Tex := nil;
   565     for i:= 0 to MaxStrIndex do
  1162     for i:= 0 to MaxStrIndex do
   566         Strs[i].Tex := nil;
  1163         Strs[i].Tex := nil;
       
  1164 
       
  1165     FillChar(InputStrL, sizeof(InputStrL), InputStrLNoPred);
       
  1166 
       
  1167     LastKeyPressTick:= 0;
       
  1168     ResetCursor();
   567 end;
  1169 end;
   568 
  1170 
   569 procedure freeModule;
  1171 procedure freeModule;
   570 var i: ShortInt;
  1172 var i: ShortInt;
   571 begin
  1173 begin
       
  1174     FreeAndNilTexture(InputLinePrefix.Tex);
   572     FreeAndNilTexture(InputStr.Tex);
  1175     FreeAndNilTexture(InputStr.Tex);
   573     for i:= 0 to MaxStrIndex do
  1176     for i:= 0 to MaxStrIndex do
   574         FreeAndNilTexture(Strs[i].Tex);
  1177         FreeAndNilTexture(Strs[i].Tex);
   575 end;
  1178 end;
   576 
  1179