hedgewars/uChat.pas
changeset 10855 1af1a54d740b
parent 10853 287634baf7ba
child 10863 9d3e1123bd43
--- a/hedgewars/uChat.pas	Sun Mar 15 16:14:13 2015 -0400
+++ b/hedgewars/uChat.pas	Sun Mar 15 16:14:43 2015 -0400
@@ -30,11 +30,13 @@
 procedure DrawChat;
 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
 procedure SendHogSpeech(s: shortstring);
+procedure CopyToClipboard(var newContent: shortstring);
 
 implementation
 uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils;
 
 const MaxStrIndex = 27;
+      MaxInputStrLen = 240;
 
 type TChatLine = record
     Tex: PTexture;
@@ -63,11 +65,12 @@
     liveLua: boolean;
     ChatHidden: boolean;
     firstDraw: boolean;
-    InputLinePrefix: shortstring;
+    InputLinePrefix: TChatLine;
     // cursor
     cursorPos, cursorX, selectedPos, selectionDx: LongInt;
     LastKeyPressTick: LongWord;
 
+
 const
     InputStrLNoPred: byte = 255;
 
@@ -110,12 +113,12 @@
 
     // calculate cursor offset
 
-    str:= InputLinePrefix + InputStr.s;
+    str:= InputStr.s;
     font:= CheckCJKFont(ansistring(str), fnt16);
 
     // get only substring before cursor to determine length
-    // SetLength(str, Length(InputLinePrefix) + cursorPos); // makes pas2c unhappy
-    str[0]:= char(Length(InputLinePrefix) + cursorPos);
+    // SetLength(str, cursorPos); // makes pas2c unhappy
+    str[0]:= char(cursorPos);
     // get render size of text
     TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @coff, nil);
 
@@ -125,9 +128,9 @@
     if selectedPos >= 0 then
         begin
         if selectedPos > cursorPos then
-            str:= InputLinePrefix + InputStr.s;
-        // SetLength(str, Length(InputLinePrefix) + selectedPos); // makes pas2c unhappy
-        str[0]:= char(Length(InputLinePrefix) + selectedPos);
+            str:= InputStr.s;
+        // SetLength(str, selectedPos); // makes pas2c unhappy
+        str[0]:= char(selectedPos);
         TTF_SizeUTF8(Fontz[font].Handle, Str2PChar(str), @soff, nil);
         selectionDx:= soff - coff;
         end
@@ -197,7 +200,7 @@
     begin
     cl.s:= str;
     color:= colors[#6];
-    str:= InputLinePrefix + str + ' ';
+    str:= str + ' ';
     end
 else
     begin
@@ -248,6 +251,24 @@
 inc(visibleCount)
 end;
 
+procedure CheckPasteBuffer(); forward;
+
+procedure UpdateInputLinePrefix();
+begin
+if liveLua then
+    begin
+    InputLinePrefix.color:= colors[#1];
+    InputLinePrefix.s:= '[Lua] >';
+    end
+else
+    begin
+    InputLinePrefix.color:= colors[#6];
+    InputLinePrefix.s:= UserNick + '>';
+    end;
+
+FreeAndNilTexture(InputLinePrefix.Tex);
+end;
+
 procedure DrawChat;
 var i, t, left, top, cnt: LongInt;
     selRect: TSDL_Rect;
@@ -264,13 +285,21 @@
 // draw chat input line first and under all other lines
 if (GameState = gsChat) and (InputStr.Tex <> nil) then
     begin
+    CheckPasteBuffer();
+
+    if InputLinePrefix.Tex = nil then
+        RenderChatLineTex(InputLinePrefix, InputLinePrefix.s);
+
+    DrawTexture(left, top, InputLinePrefix.Tex);
+    inc(left, InputLinePrefix.Width);
+    DrawTexture(left, top, InputStr.Tex);
+
     if firstDraw then
         begin
         UpdateCursorCoords();
         firstDraw:= false;
         end;
 
-    DrawTexture(left, top, InputStr.Tex);
     if selectedPos < 0 then
         begin
         // draw cursor
@@ -294,9 +323,10 @@
 
         DrawRect(selRect, $FF, $FF, $FF, $40, true);
         end;
+
+    dec(left, InputLinePrefix.Width);
     end;
 
-
 // draw chat lines
 if ((not ChatHidden) or showAll) and (UIDisplay <> uiNone) then
     begin
@@ -458,6 +488,7 @@
                 AddFileLog('[Lua] chat input string parsing disabled');
                 AddChatString(#3 + 'Lua parsing: OFF');
                 end;
+            UpdateInputLinePrefix();
             end;
         exit
         end;
@@ -565,6 +596,7 @@
         cursorPos:= min(cursorPos, selectedPos);
         ResetSelection();
         end;
+    UpdateCursorCoords();
 end;
 
 procedure HandleSelection(enabled: boolean);
@@ -660,6 +692,73 @@
     end;
 end;
 
+procedure CopyToClipboard(var newContent: shortstring);
+begin
+    SendIPC(_S'y' + copy(newContent, 1, 253) + #0);
+end;
+
+procedure CopySelectionToClipboard();
+var selection: shortstring;
+begin
+    if selectedPos >= 0 then
+        begin
+        selection:= copy(InputStr.s, min(CursorPos, selectedPos) + 1, abs(CursorPos - selectedPos));
+        CopyToClipboard(selection);
+        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;
+begin
+    // safe length for string
+    l:= min(MaxInputStrLen-cursorPos, Length(s));
+    s:= copy(s,1,l);
+
+    // if we insert rather than append, shift info in InputStrL accordingly
+    if cursorPos < Length(InputStr.s) then
+        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;
+        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);
+
+    SetLine(InputStr, InputStr.s, true);
+
+    // move cursor to end of inserted string
+    lastc:= MaxInputStrLen;
+    cursorPos:= min(lastc, cursorPos + l);
+    UpdateCursorCoords();
+end;
+
+procedure PasteFromClipboard();
+begin
+    SendIPC(_S'Y');
+end;
+
+procedure CheckPasteBuffer();
+begin
+    if Length(ChatPasteBuffer) > 0 then
+        begin
+        InsertIntoInputStr(ChatPasteBuffer);
+        ChatPasteBuffer:= '';
+        end;
+end;
+
 procedure KeyPressChat(Key, Sym: Longword; Modifier: Word);
 const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
 var i, btw, index: integer;
@@ -670,6 +769,8 @@
     LastKeyPressTick:= RealTicks;
     action:= true;
 
+    CheckPasteBuffer();
+
     selMode:= (modifier and (KMOD_LSHIFT or KMOD_RSHIFT)) <> 0;
     ctrl:= (modifier and (KMOD_LCTRL or KMOD_RCTRL)) <> 0;
     skip:= none;
@@ -691,11 +792,13 @@
                     HandleSelection(true);
                     SkipInputChars(skip, true);
                     DeleteSelected();
-                    end;
+                    end
+                else
+                    UpdateCursorCoords();
+
                 end
             else
                 DeleteSelected();
-            UpdateCursorCoords();
             end;
         SDLK_DELETE:
             begin
@@ -718,12 +821,12 @@
                         SkipInputChars(skip, false);
                         DeleteSelected();
                         end;
-                    end;
+                    end
+                else
+                    UpdateCursorCoords();
                 end
             else
                 DeleteSelected();
-
-            UpdateCursorCoords();
             end;
         SDLK_ESCAPE:
             begin
@@ -862,6 +965,33 @@
             else
                 action:= false;
             end;
+        SDLK_c:
+            begin
+            // copy
+            if ctrl then
+                CopySelectionToClipboard()
+            else
+                action:= false;
+            end;
+        SDLK_v:
+            begin
+            // paste
+            if ctrl then
+                PasteFromClipboard()
+            else
+                action:= false;
+            end;
+        SDLK_x:
+            begin
+            // cut
+            if ctrl then
+                begin
+                CopySelectionToClipboard();
+                DeleteSelected();
+                end
+            else
+                action:= false;
+            end;
         else
             action:= false;
         end;
@@ -888,28 +1018,10 @@
 
         utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
 
-        if Length(InputStr.s) + btw > 240 then
+        if Length(InputStr.s) + btw > MaxInputStrLen then
             exit;
 
-        // if we insert rather than append, shift info in InputStrL accordingly
-        if cursorPos < Length(InputStr.s) then
-            begin
-            for i:= Length(InputStr.s) downto cursorPos + 1 do
-                begin
-                if InputStrL[i] <> InputStrLNoPred then
-                    begin
-                    InputStrL[i+btw]:= InputStrL[i] + btw;
-                    InputStrL[i]:= InputStrLNoPred;
-                    end;
-                end;
-            end;
-
-        InputStrL[cursorPos + btw]:= cursorPos;
-        Insert(utf8, InputStr.s, cursorPos + 1);
-        SetLine(InputStr, InputStr.s, true);
-
-        cursorPos:= cursorPos + btw;
-        UpdateCursorCoords();
+        InsertIntoInputStr(utf8);
         end
 end;
 
@@ -994,7 +1106,8 @@
     ChatHidden:= false;
     firstDraw:= true;
 
-    InputLinePrefix:= UserNick + '> ';
+    InputLinePrefix.Tex:= nil;
+    UpdateInputLinePrefix();
     inputStr.s:= '';
     inputStr.Tex := nil;
     for i:= 0 to MaxStrIndex do
@@ -1009,6 +1122,7 @@
 procedure freeModule;
 var i: ShortInt;
 begin
+    FreeAndNilTexture(InputLinePrefix.Tex);
     FreeAndNilTexture(InputStr.Tex);
     for i:= 0 to MaxStrIndex do
         FreeAndNilTexture(Strs[i].Tex);