hedgewars/uChat.pas
changeset 10855 1af1a54d740b
parent 10853 287634baf7ba
child 10863 9d3e1123bd43
equal deleted inserted replaced
10848:231d3e3d3267 10855:1af1a54d740b
    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; Modifier: Word);
    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;
    61     ChatReady: boolean;
    63     ChatReady: boolean;
    62     showAll: boolean;
    64     showAll: boolean;
    63     liveLua: boolean;
    65     liveLua: boolean;
    64     ChatHidden: boolean;
    66     ChatHidden: boolean;
    65     firstDraw: boolean;
    67     firstDraw: boolean;
    66     InputLinePrefix: shortstring;
    68     InputLinePrefix: TChatLine;
    67     // cursor
    69     // cursor
    68     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
    70     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
    69     LastKeyPressTick: LongWord;
    71     LastKeyPressTick: LongWord;
       
    72 
    70 
    73 
    71 const
    74 const
    72     InputStrLNoPred: byte = 255;
    75     InputStrLNoPred: byte = 255;
    73 
    76 
    74     colors: array[#0..#6] of TSDL_Color = (
    77     colors: array[#0..#6] of TSDL_Color = (
   108     if cursorPos = selectedPos then
   111     if cursorPos = selectedPos then
   109         ResetSelection();
   112         ResetSelection();
   110 
   113 
   111     // calculate cursor offset
   114     // calculate cursor offset
   112 
   115 
   113     str:= InputLinePrefix + InputStr.s;
   116     str:= InputStr.s;
   114     font:= CheckCJKFont(ansistring(str), fnt16);
   117     font:= CheckCJKFont(ansistring(str), fnt16);
   115 
   118 
   116     // get only substring before cursor to determine length
   119     // get only substring before cursor to determine length
   117     // SetLength(str, Length(InputLinePrefix) + cursorPos); // makes pas2c unhappy
   120     // SetLength(str, cursorPos); // makes pas2c unhappy
   118     str[0]:= char(Length(InputLinePrefix) + cursorPos);
   121     str[0]:= char(cursorPos);
   119     // get render size of text
   122     // get render size of text
   120     TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil);
   123     TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil);
   121 
   124 
   122     cursorX:= 2 + coff;
   125     cursorX:= 2 + coff;
   123 
   126 
   124     // calculate selection width on screen
   127     // calculate selection width on screen
   125     if selectedPos >= 0 then
   128     if selectedPos >= 0 then
   126         begin
   129         begin
   127         if selectedPos > cursorPos then
   130         if selectedPos > cursorPos then
   128             str:= InputLinePrefix + InputStr.s;
   131             str:= InputStr.s;
   129         // SetLength(str, Length(InputLinePrefix) + selectedPos); // makes pas2c unhappy
   132         // SetLength(str, selectedPos); // makes pas2c unhappy
   130         str[0]:= char(Length(InputLinePrefix) + selectedPos);
   133         str[0]:= char(selectedPos);
   131         TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @soff, nil);
   134         TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @soff, nil);
   132         selectionDx:= soff - coff;
   135         selectionDx:= soff - coff;
   133         end
   136         end
   134     else
   137     else
   135         selectionDx:= 0;
   138         selectionDx:= 0;
   195 
   198 
   196 if isInput then
   199 if isInput then
   197     begin
   200     begin
   198     cl.s:= str;
   201     cl.s:= str;
   199     color:= colors[#6];
   202     color:= colors[#6];
   200     str:= InputLinePrefix + str + ' ';
   203     str:= str + ' ';
   201     end
   204     end
   202 else
   205 else
   203     begin
   206     begin
   204     color:= colors[str[1]];
   207     color:= colors[str[1]];
   205     delete(str, 1, 1);
   208     delete(str, 1, 1);
   246 SetLine(Strs[lastStr], s, false);
   249 SetLine(Strs[lastStr], s, false);
   247 
   250 
   248 inc(visibleCount)
   251 inc(visibleCount)
   249 end;
   252 end;
   250 
   253 
       
   254 procedure CheckPasteBuffer(); forward;
       
   255 
       
   256 procedure UpdateInputLinePrefix();
       
   257 begin
       
   258 if liveLua then
       
   259     begin
       
   260     InputLinePrefix.color:= colors[#1];
       
   261     InputLinePrefix.s:= '[Lua] >';
       
   262     end
       
   263 else
       
   264     begin
       
   265     InputLinePrefix.color:= colors[#6];
       
   266     InputLinePrefix.s:= UserNick + '>';
       
   267     end;
       
   268 
       
   269 FreeAndNilTexture(InputLinePrefix.Tex);
       
   270 end;
       
   271 
   251 procedure DrawChat;
   272 procedure DrawChat;
   252 var i, t, left, top, cnt: LongInt;
   273 var i, t, left, top, cnt: LongInt;
   253     selRect: TSDL_Rect;
   274     selRect: TSDL_Rect;
   254 begin
   275 begin
   255 ChatReady:= true; // maybe move to somewhere else?
   276 ChatReady:= true; // maybe move to somewhere else?
   262 top := 10 + visibleCount * ClHeight; // we start with input line (if any)
   283 top := 10 + visibleCount * ClHeight; // we start with input line (if any)
   263 
   284 
   264 // draw chat input line first and under all other lines
   285 // draw chat input line first and under all other lines
   265 if (GameState = gsChat) and (InputStr.Tex <> nil) then
   286 if (GameState = gsChat) and (InputStr.Tex <> nil) then
   266     begin
   287     begin
       
   288     CheckPasteBuffer();
       
   289 
       
   290     if InputLinePrefix.Tex = nil then
       
   291         RenderChatLineTex(InputLinePrefix, InputLinePrefix.s);
       
   292 
       
   293     DrawTexture(left, top, InputLinePrefix.Tex);
       
   294     inc(left, InputLinePrefix.Width);
       
   295     DrawTexture(left, top, InputStr.Tex);
       
   296 
   267     if firstDraw then
   297     if firstDraw then
   268         begin
   298         begin
   269         UpdateCursorCoords();
   299         UpdateCursorCoords();
   270         firstDraw:= false;
   300         firstDraw:= false;
   271         end;
   301         end;
   272 
   302 
   273     DrawTexture(left, top, InputStr.Tex);
       
   274     if selectedPos < 0 then
   303     if selectedPos < 0 then
   275         begin
   304         begin
   276         // draw cursor
   305         // draw cursor
   277         if ((RealTicks - LastKeyPressTick) and 512) < 256 then
   306         if ((RealTicks - LastKeyPressTick) and 512) < 256 then
   278             DrawLineOnScreen(left + cursorX, top + 2, left + cursorX, top + ClHeight - 2, 2.0, $00, $FF, $FF, $FF);
   307             DrawLineOnScreen(left + cursorX, top + 2, left + cursorX, top + ClHeight - 2, 2.0, $00, $FF, $FF, $FF);
   292             selRect.w:= selectionDx;
   321             selRect.w:= selectionDx;
   293             end;
   322             end;
   294 
   323 
   295         DrawRect(selRect, $FF, $FF, $FF, $40, true);
   324         DrawRect(selRect, $FF, $FF, $FF, $40, true);
   296         end;
   325         end;
       
   326 
       
   327     dec(left, InputLinePrefix.Width);
   297     end;
   328     end;
   298 
       
   299 
   329 
   300 // draw chat lines
   330 // draw chat lines
   301 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then
   331 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then
   302     begin
   332     begin
   303     if MissedCount <> 0 then // there are chat strings we missed, so print them now
   333     if MissedCount <> 0 then // there are chat strings we missed, so print them now
   456             else
   486             else
   457                 begin
   487                 begin
   458                 AddFileLog('[Lua] chat input string parsing disabled');
   488                 AddFileLog('[Lua] chat input string parsing disabled');
   459                 AddChatString(#3 + 'Lua parsing: OFF');
   489                 AddChatString(#3 + 'Lua parsing: OFF');
   460                 end;
   490                 end;
       
   491             UpdateInputLinePrefix();
   461             end;
   492             end;
   462         exit
   493         exit
   463         end;
   494         end;
   464 
   495 
   465     // hedghog animations/taunts and engine commands
   496     // hedghog animations/taunts and engine commands
   563         begin
   594         begin
   564         DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
   595         DelBytesFromInputStrBack(max(cursorPos, selectedPos), abs(selectedPos-cursorPos));
   565         cursorPos:= min(cursorPos, selectedPos);
   596         cursorPos:= min(cursorPos, selectedPos);
   566         ResetSelection();
   597         ResetSelection();
   567         end;
   598         end;
       
   599     UpdateCursorCoords();
   568 end;
   600 end;
   569 
   601 
   570 procedure HandleSelection(enabled: boolean);
   602 procedure HandleSelection(enabled: boolean);
   571 begin
   603 begin
   572 if enabled then
   604 if enabled then
   656             cursorPos:= InputStrL[cursorPos];
   688             cursorPos:= InputStrL[cursorPos];
   657             break;
   689             break;
   658             end;
   690             end;
   659         end;
   691         end;
   660     end;
   692     end;
       
   693 end;
       
   694 
       
   695 procedure CopyToClipboard(var newContent: shortstring);
       
   696 begin
       
   697     SendIPC(_S'y' + copy(newContent, 1, 253) + #0);
       
   698 end;
       
   699 
       
   700 procedure CopySelectionToClipboard();
       
   701 var selection: shortstring;
       
   702 begin
       
   703     if selectedPos >= 0 then
       
   704         begin
       
   705         selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos));
       
   706         CopyToClipboard(selection);
       
   707         end;
       
   708 end;
       
   709 
       
   710 // TODO: honor utf8, don't break utf8 chars when shifting chars beyond limit
       
   711 procedure InsertIntoInputStr(s: shortstring);
       
   712 var i, l, il, lastc: integer;
       
   713 begin
       
   714     // safe length for string
       
   715     l:= min(MaxInputStrLen-cursorPos, Length(s));
       
   716     s:= copy(s,1,l);
       
   717 
       
   718     // if we insert rather than append, shift info in InputStrL accordingly
       
   719     if cursorPos < Length(InputStr.s) then
       
   720         begin
       
   721         for i:= Length(InputStr.s) downto cursorPos + 1 do
       
   722             begin
       
   723             if InputStrL[i] <> InputStrLNoPred then
       
   724                 begin
       
   725                 il:= i + l;
       
   726                 // only shift if not overflowing
       
   727                 if il <= MaxInputStrLen then
       
   728                     InputStrL[il]:= InputStrL[i] + l;
       
   729                 InputStrL[i]:= InputStrLNoPred;
       
   730                 end;
       
   731             end;
       
   732         end;
       
   733 
       
   734     InputStrL[cursorPos + l]:= cursorPos;
       
   735     // insert string truncated to safe length
       
   736     Insert(s, InputStr.s, cursorPos + 1);
       
   737     if Length(InputStr.s) > MaxInputStrLen then
       
   738         InputStr.s[0]:= char(MaxInputStrLen);
       
   739 
       
   740     SetLine(InputStr, InputStr.s, true);
       
   741 
       
   742     // move cursor to end of inserted string
       
   743     lastc:= MaxInputStrLen;
       
   744     cursorPos:= min(lastc, cursorPos + l);
       
   745     UpdateCursorCoords();
       
   746 end;
       
   747 
       
   748 procedure PasteFromClipboard();
       
   749 begin
       
   750     SendIPC(_S'Y');
       
   751 end;
       
   752 
       
   753 procedure CheckPasteBuffer();
       
   754 begin
       
   755     if Length(ChatPasteBuffer) > 0 then
       
   756         begin
       
   757         InsertIntoInputStr(ChatPasteBuffer);
       
   758         ChatPasteBuffer:= '';
       
   759         end;
   661 end;
   760 end;
   662 
   761 
   663 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
   762 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
   664 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
   763 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
   665 var i, btw, index: integer;
   764 var i, btw, index: integer;
   668     skip: TCharSkip;
   767     skip: TCharSkip;
   669 begin
   768 begin
   670     LastKeyPressTick:= RealTicks;
   769     LastKeyPressTick:= RealTicks;
   671     action:= true;
   770     action:= true;
   672 
   771 
       
   772     CheckPasteBuffer();
       
   773 
   673     selMode:= (modifier and (KMOD_LSHIFT or KMOD_RSHIFT)) <> 0;
   774     selMode:= (modifier and (KMOD_LSHIFT or KMOD_RSHIFT)) <> 0;
   674     ctrl:= (modifier and (KMOD_LCTRL or KMOD_RCTRL)) <> 0;
   775     ctrl:= (modifier and (KMOD_LCTRL or KMOD_RCTRL)) <> 0;
   675     skip:= none;
   776     skip:= none;
   676 
   777 
   677     case Sym of
   778     case Sym of
   689                 if ctrl and (selectedPos < 0) then
   790                 if ctrl and (selectedPos < 0) then
   690                     begin
   791                     begin
   691                     HandleSelection(true);
   792                     HandleSelection(true);
   692                     SkipInputChars(skip, true);
   793                     SkipInputChars(skip, true);
   693                     DeleteSelected();
   794                     DeleteSelected();
   694                     end;
   795                     end
       
   796                 else
       
   797                     UpdateCursorCoords();
       
   798 
   695                 end
   799                 end
   696             else
   800             else
   697                 DeleteSelected();
   801                 DeleteSelected();
   698             UpdateCursorCoords();
       
   699             end;
   802             end;
   700         SDLK_DELETE:
   803         SDLK_DELETE:
   701             begin
   804             begin
   702             if selectedPos < 0 then
   805             if selectedPos < 0 then
   703                 begin
   806                 begin
   716                         begin
   819                         begin
   717                         HandleSelection(true);
   820                         HandleSelection(true);
   718                         SkipInputChars(skip, false);
   821                         SkipInputChars(skip, false);
   719                         DeleteSelected();
   822                         DeleteSelected();
   720                         end;
   823                         end;
   721                     end;
   824                     end
       
   825                 else
       
   826                     UpdateCursorCoords();
   722                 end
   827                 end
   723             else
   828             else
   724                 DeleteSelected();
   829                 DeleteSelected();
   725 
       
   726             UpdateCursorCoords();
       
   727             end;
   830             end;
   728         SDLK_ESCAPE:
   831         SDLK_ESCAPE:
   729             begin
   832             begin
   730             if Length(InputStr.s) > 0 then
   833             if Length(InputStr.s) > 0 then
   731                 begin
   834                 begin
   860                 UpdateCursorCoords();
   963                 UpdateCursorCoords();
   861                 end
   964                 end
   862             else
   965             else
   863                 action:= false;
   966                 action:= false;
   864             end;
   967             end;
       
   968         SDLK_c:
       
   969             begin
       
   970             // copy
       
   971             if ctrl then
       
   972                 CopySelectionToClipboard()
       
   973             else
       
   974                 action:= false;
       
   975             end;
       
   976         SDLK_v:
       
   977             begin
       
   978             // paste
       
   979             if ctrl then
       
   980                 PasteFromClipboard()
       
   981             else
       
   982                 action:= false;
       
   983             end;
       
   984         SDLK_x:
       
   985             begin
       
   986             // cut
       
   987             if ctrl then
       
   988                 begin
       
   989                 CopySelectionToClipboard();
       
   990                 DeleteSelected();
       
   991                 end
       
   992             else
       
   993                 action:= false;
       
   994             end;
   865         else
   995         else
   866             action:= false;
   996             action:= false;
   867         end;
   997         end;
   868     if not action and (Key <> 0) then
   998     if not action and (Key <> 0) then
   869         begin
   999         begin
   886             Key:= Key shr 6
  1016             Key:= Key shr 6
   887             end;
  1017             end;
   888 
  1018 
   889         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
  1019         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
   890 
  1020 
   891         if Length(InputStr.s) + btw > 240 then
  1021         if Length(InputStr.s) + btw > MaxInputStrLen then
   892             exit;
  1022             exit;
   893 
  1023 
   894         // if we insert rather than append, shift info in InputStrL accordingly
  1024         InsertIntoInputStr(utf8);
   895         if cursorPos < Length(InputStr.s) then
       
   896             begin
       
   897             for i:= Length(InputStr.s) downto cursorPos + 1 do
       
   898                 begin
       
   899                 if InputStrL[i] <> InputStrLNoPred then
       
   900                     begin
       
   901                     InputStrL[i+btw]:= InputStrL[i] + btw;
       
   902                     InputStrL[i]:= InputStrLNoPred;
       
   903                     end;
       
   904                 end;
       
   905             end;
       
   906 
       
   907         InputStrL[cursorPos + btw]:= cursorPos;
       
   908         Insert(utf8, InputStr.s, cursorPos + 1);
       
   909         SetLine(InputStr, InputStr.s, true);
       
   910 
       
   911         cursorPos:= cursorPos + btw;
       
   912         UpdateCursorCoords();
       
   913         end
  1025         end
   914 end;
  1026 end;
   915 
  1027 
   916 procedure chChatMessage(var s: shortstring);
  1028 procedure chChatMessage(var s: shortstring);
   917 begin
  1029 begin
   992     missedCount:= 0;
  1104     missedCount:= 0;
   993     liveLua:= false;
  1105     liveLua:= false;
   994     ChatHidden:= false;
  1106     ChatHidden:= false;
   995     firstDraw:= true;
  1107     firstDraw:= true;
   996 
  1108 
   997     InputLinePrefix:= UserNick + '> ';
  1109     InputLinePrefix.Tex:= nil;
       
  1110     UpdateInputLinePrefix();
   998     inputStr.s:= '';
  1111     inputStr.s:= '';
   999     inputStr.Tex := nil;
  1112     inputStr.Tex := nil;
  1000     for i:= 0 to MaxStrIndex do
  1113     for i:= 0 to MaxStrIndex do
  1001         Strs[i].Tex := nil;
  1114         Strs[i].Tex := nil;
  1002 
  1115 
  1007 end;
  1120 end;
  1008 
  1121 
  1009 procedure freeModule;
  1122 procedure freeModule;
  1010 var i: ShortInt;
  1123 var i: ShortInt;
  1011 begin
  1124 begin
       
  1125     FreeAndNilTexture(InputLinePrefix.Tex);
  1012     FreeAndNilTexture(InputStr.Tex);
  1126     FreeAndNilTexture(InputStr.Tex);
  1013     for i:= 0 to MaxStrIndex do
  1127     for i:= 0 to MaxStrIndex do
  1014         FreeAndNilTexture(Strs[i].Tex);
  1128         FreeAndNilTexture(Strs[i].Tex);
  1015 end;
  1129 end;
  1016 
  1130