Make Chat Windows pseudo-tabbed
[xboard.git] / winboard / wchat.c
index eb5179f..3b90b2c 100644 (file)
@@ -32,6 +32,7 @@
 #include <malloc.h>\r
 #include <commdlg.h>\r
 #include <dlgs.h>\r
+#include <Windowsx.h>\r
 \r
 #include "common.h"\r
 #include "frontend.h"\r
 #include "wsnap.h"\r
 \r
 int chatCount;\r
+static int onTop;\r
 extern char chatPartner[MAX_CHAT][MSG_SIZ];\r
 HANDLE chatHandle[MAX_CHAT];\r
+static WNDPROC chatInputWindowProc;\r
 \r
 void SendToICS P((char *s));\r
 void ChatPopUp P((char *s));\r
@@ -52,12 +55,16 @@ void ChatPopDown();
 extern int opponentKibitzes;\r
 \r
 /* Imports from winboard.c */\r
+VOID SaveInHistory(char *cmd);\r
+char *PrevInHistory(char *cmd);\r
+char *NextInHistory();\r
 extern HWND ChatDialog;\r
 \r
 extern HINSTANCE hInst;\r
 extern HWND hwndMain;\r
 \r
 extern WindowPlacement wpChat[MAX_CHAT];\r
+extern WindowPlacement wpConsole;\r
 \r
 extern BoardSize boardSize;\r
 \r
@@ -132,6 +139,42 @@ static void InsertIntoMemo( HANDLE hDlg, char * text )
     SendMessage( hMemo, EM_SCROLLCARET, 0, 0);\r
 }\r
 \r
+LRESULT CALLBACK\r
+InterceptArrowKeys(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
+{\r
+  char buf[MSG_SIZ];\r
+  char *p;\r
+  CHARRANGE sel;\r
+\r
+  switch (message) {\r
+  case WM_KEYDOWN: // cloned from ConsoleInputSubClass()\r
+    switch (wParam) {\r
+    case VK_UP:\r
+      GetWindowText(hwnd, buf, MSG_SIZ);\r
+      p = PrevInHistory(buf);\r
+      if (p != NULL) {\r
+       SetWindowText(hwnd, p);\r
+       sel.cpMin = 999999;\r
+       sel.cpMax = 999999;\r
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
+        return 0;\r
+      }\r
+      break;\r
+    case VK_DOWN:\r
+      p = NextInHistory();\r
+      if (p != NULL) {\r
+       SetWindowText(hwnd, p);\r
+       sel.cpMin = 999999;\r
+       sel.cpMax = 999999;\r
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
+        return 0;\r
+      }\r
+      break;\r
+    }\r
+  }\r
+  return (*chatInputWindowProc)(hwnd, message, wParam, lParam);\r
+}\r
+\r
 // This seems pure front end\r
 LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
 {\r
@@ -139,6 +182,8 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
     char buf[MSG_SIZ], mess[MSG_SIZ];\r
     int partner = -1, i;\r
     static BOOL filterHasFocus[MAX_CHAT];\r
+    WORD wMask;\r
+    HWND hMemo;\r
 \r
     for(i=0; i<MAX_CHAT; i++) if(hDlg == chatHandle[i]) { partner = i; break; }\r
 \r
@@ -150,7 +195,15 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
                sprintf(buf, "Chat Window %s", first.tidy);\r
                SetWindowText(hDlg, buf);\r
         }\r
-//     chatPartner[partner][0] = 0;\r
+       for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) {\r
+           // set our button in other open chats\r
+           SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]);\r
+           EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 1 );\r
+           // and buttons for other chats in ours\r
+           SetDlgItemText(hDlg, IDC_Focus1+i-(i>partner), chatPartner[i]);\r
+       } else EnableWindow( GetDlgItem(hDlg, IDC_Focus1+i-(i>partner)), 1 );\r
+       for(i=0; i<MAX_CHAT-1; i++) { Button_SetStyle(GetDlgItem(hDlg, IDC_Focus1+i), BS_PUSHBUTTON|BS_LEFT, TRUE); }\r
+        SetWindowPos(hDlg, NULL, wpConsole.x, wpConsole.y, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
        SendMessage( GetDlgItem(hDlg, IDC_ChatPartner), // [HGM] clickbox: initialize with requested handle\r
                        WM_SETTEXT, 0, (LPARAM) chatPartner[partner] );\r
        filterHasFocus[partner] = TRUE;\r
@@ -158,8 +211,31 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
            filterHasFocus[partner] = FALSE;\r
            SetFocus( GetDlgItem(hDlg, OPT_ChatInput) );\r
        }\r
+       hMemo = GetDlgItem(hDlg, IDC_ChatMemo);\r
+       wMask = (WORD) SendMessage(hMemo, EM_GETEVENTMASK, 0, 0L);\r
+       SendMessage(hMemo, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
+       SendMessage(hMemo, EM_AUTOURLDETECT, TRUE, 0L);\r
+       chatInputWindowProc = (WNDPROC) // cloned from ConsoleWndProc(). Assume they all share same proc.\r
+             SetWindowLong(GetDlgItem(hDlg, OPT_ChatInput), GWL_WNDPROC, (LONG) InterceptArrowKeys);\r
         return FALSE;\r
 \r
+    case WM_NOTIFY:\r
+      if (((NMHDR*)lParam)->code == EN_LINK)\r
+      {\r
+       ENLINK *pLink = (ENLINK*)lParam;\r
+       if (pLink->msg == WM_LBUTTONUP)\r
+       {\r
+         TEXTRANGE tr;\r
+\r
+         tr.chrg = pLink->chrg;\r
+         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
+         SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
+         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
+         free(tr.lpstrText);\r
+       }\r
+      }\r
+    break;\r
+\r
     case WM_COMMAND:\r
       /* \r
         [AS]\r
@@ -198,17 +274,22 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
 \r
        case IDC_Change:\r
            GetDlgItemText(hDlg, IDC_ChatPartner, chatPartner[partner], MSG_SIZ);\r
+           for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) {\r
+             // set our button in other open chats\r
+             SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]);\r
+           }\r
            break;\r
 \r
        case IDC_Send:\r
            GetDlgItemText(hDlg, OPT_ChatInput, mess, MSG_SIZ);\r
            SetDlgItemText(hDlg, OPT_ChatInput, "");\r
            // from here on it could be back-end\r
+           SaveInHistory(mess);\r
            if(!strcmp("WHISPER", chatPartner[partner]))\r
                sprintf(buf, "whisper %s\n", mess); // WHISPER box uses "whisper" to send\r
            else {\r
                if(!atoi(chatPartner[partner])) {\r
-                   sprintf(buf, "> %s\n", mess); // echo only tells to handle, not channel\r
+                   sprintf(buf, "> %s\r\n", mess); // echo only tells to handle, not channel\r
                InsertIntoMemo(hDlg, buf);\r
                sprintf(buf, "xtell %s %s\n", chatPartner[partner], mess);\r
                } else\r
@@ -217,6 +298,22 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
            SendToICS(buf);\r
            break;\r
 \r
+       case IDC_Focus1:\r
+       case IDC_Focus2:\r
+       case IDC_Focus3:\r
+       case IDC_Focus4:\r
+           i = LOWORD(wParam) - IDC_Focus1;\r
+           if(i >= partner) i++;\r
+           onTop = i;\r
+           SetFocus(GetDlgItem(hDlg, IDC_Send));\r
+           if(chatHandle[i]) {\r
+               int j;\r
+               for(j=0; j<MAX_CHAT; j++) if(i != j && chatHandle[j])\r
+                   Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+i-(j<i)), FALSE);\r
+               SetFocus(GetDlgItem(chatHandle[i], OPT_ChatInput));\r
+           }\r
+           break;\r
+\r
         default:\r
           break;\r
         }\r
@@ -227,6 +324,11 @@ LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam
        chatHandle[partner] = 0;\r
        chatPartner[partner][0] = 0;\r
         ChatPopDown();\r
+       for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) {\r
+           // set our button in other open chats\r
+           SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), "");\r
+           EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 0 );\r
+       }\r
        EndDialog(hDlg, TRUE);\r
         break;\r
 \r
@@ -285,7 +387,12 @@ void ChatPopDown()
 \r
 void OutputChatMessage(int partner, char *text)\r
 {\r
+       int j;\r
        if(!chatHandle[partner]) return;\r
 \r
+       int n = strlen(text);\r
+       text[n+1] = 0; text[n] = '\n'; text[n-1] = '\r'; // Needs CR to not lose line breaks on copy-paste\r
        InsertIntoMemo(chatHandle[partner], text);\r
+       if(partner != onTop) for(j=0; j<MAX_CHAT; j++) if(j != partner && chatHandle[j])\r
+           Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+partner-(j<partner)), TRUE);\r
 }\r