4cf8dce08b2dd6357a178f17ba7f8e015ff06074
[xboard.git] / winboard / wchat.c
1 /*\r
2  * Chat window (PV)\r
3  *\r
4  * Author: H.G.Muller (August 2009)\r
5  *\r
6  * Copyright 2009, 2010 Free Software Foundation, Inc.\r
7  *\r
8  * ------------------------------------------------------------------------\r
9  *\r
10  * GNU XBoard is free software: you can redistribute it and/or modify\r
11  * it under the terms of the GNU General Public License as published by\r
12  * the Free Software Foundation, either version 3 of the License, or (at\r
13  * your option) any later version.\r
14  *\r
15  * GNU XBoard is distributed in the hope that it will be useful, but\r
16  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
18  * General Public License for more details.\r
19  *\r
20  * You should have received a copy of the GNU General Public License\r
21  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
22  *\r
23  *------------------------------------------------------------------------\r
24  ** See the file ChangeLog for a revision history.  */\r
25 \r
26 #include "config.h"\r
27 \r
28 #include <windows.h> /* required for all Windows applications */\r
29 #include <richedit.h>\r
30 #include <stdio.h>\r
31 #include <stdlib.h>\r
32 #include <malloc.h>\r
33 #include <commdlg.h>\r
34 #include <dlgs.h>\r
35 \r
36 #include "common.h"\r
37 #include "frontend.h"\r
38 #include "winboard.h"\r
39 #include "backend.h"\r
40 \r
41 #include "wsnap.h"\r
42 \r
43 int chatCount;\r
44 extern char chatPartner[MAX_CHAT][MSG_SIZ];\r
45 HANDLE chatHandle[MAX_CHAT];\r
46 \r
47 void SendToICS P((char *s));\r
48 void ChatPopUp();\r
49 void ChatPopDown();\r
50 \r
51 /* Imports from backend.c */\r
52 char * SavePart(char *str);\r
53 extern int opponentKibitzes;\r
54 \r
55 /* Imports from winboard.c */\r
56 extern HWND ChatDialog;\r
57 \r
58 extern HINSTANCE hInst;\r
59 extern HWND hwndMain;\r
60 \r
61 extern WindowPlacement wpChat[MAX_CHAT];\r
62 \r
63 extern BoardSize boardSize;\r
64 \r
65 /* Module variables */\r
66 #define H_MARGIN            5\r
67 #define V_MARGIN            5\r
68 \r
69 // front end, although we might make GetWindowRect front end instead\r
70 static int GetControlWidth( HWND hDlg, int id )\r
71 {\r
72     RECT rc;\r
73 \r
74     GetWindowRect( GetDlgItem( hDlg, id ), &rc );\r
75 \r
76     return rc.right - rc.left;\r
77 }\r
78 \r
79 // front end?\r
80 static int GetControlHeight( HWND hDlg, int id )\r
81 {\r
82     RECT rc;\r
83 \r
84     GetWindowRect( GetDlgItem( hDlg, id ), &rc );\r
85 \r
86     return rc.bottom - rc.top;\r
87 }\r
88 \r
89 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )\r
90 {\r
91     HWND hControl = GetDlgItem( hDlg, id );\r
92 \r
93     SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );\r
94 }\r
95 \r
96 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine\r
97 static void ResizeWindowControls( HWND hDlg )\r
98 {\r
99     RECT rc;\r
100     int clientWidth;\r
101     int clientHeight;\r
102     int maxControlWidth;\r
103     int buttonWidth, buttonHeight;\r
104 \r
105     /* Initialize variables */\r
106     GetClientRect( hDlg, &rc );\r
107 \r
108     clientWidth = rc.right - rc.left;\r
109     clientHeight = rc.bottom - rc.top;\r
110 \r
111     maxControlWidth = clientWidth - 2*H_MARGIN;\r
112     buttonWidth  = GetControlWidth(hDlg, IDC_Send);\r
113     buttonHeight = GetControlHeight(hDlg, IDC_Send);\r
114 \r
115     /* Resize controls */\r
116     SetControlPos( hDlg, IDC_Clear, maxControlWidth+H_MARGIN-2*buttonWidth-5, V_MARGIN, buttonWidth, buttonHeight );\r
117     SetControlPos( hDlg, IDC_Send, maxControlWidth+H_MARGIN-buttonWidth, V_MARGIN, buttonWidth, buttonHeight );\r
118     SetControlPos( hDlg, IDC_ChatMemo, H_MARGIN, 2*V_MARGIN+buttonHeight, maxControlWidth, clientHeight-3*V_MARGIN-2*buttonHeight );\r
119     SetControlPos( hDlg, OPT_ChatInput, H_MARGIN, clientHeight-V_MARGIN-buttonHeight, maxControlWidth, buttonHeight );\r
120 \r
121 //    InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );\r
122 //    InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );\r
123 }\r
124 \r
125 // front end. Actual printing of PV lines into the output field\r
126 static void InsertIntoMemo( HANDLE hDlg, char * text )\r
127 {\r
128     HANDLE hMemo = GetDlgItem(hDlg, IDC_ChatMemo);\r
129 \r
130     SendMessage( hMemo, EM_SETSEL, 1000000, 1000000 );\r
131 \r
132     SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );\r
133     SendMessage( hMemo, EM_SCROLLCARET, 0, 0);\r
134 }\r
135 \r
136 // This seems pure front end\r
137 LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )\r
138 {\r
139     static SnapData sd;\r
140     char buf[MSG_SIZ], mess[MSG_SIZ];\r
141     int partner = -1, i;\r
142     static BOOL filterHasFocus[MAX_CHAT];\r
143 \r
144     for(i=0; i<MAX_CHAT; i++) if(hDlg == chatHandle[i]) { partner = i; break; }\r
145 \r
146     switch (message) {\r
147     case WM_INITDIALOG:\r
148         if(partner<0) {\r
149                 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] == NULL) { partner = i; break; }\r
150                 chatHandle[partner] = hDlg;\r
151                 sprintf(buf, "Chat Window %s", first.tidy);\r
152                 SetWindowText(hDlg, buf);\r
153         }\r
154         chatPartner[partner][0] = 0;\r
155         filterHasFocus[partner] = FALSE;\r
156 \r
157         return FALSE;\r
158 \r
159     case WM_COMMAND:\r
160       /* \r
161         [AS]\r
162         If <Enter> is pressed while editing the filter, it's better to apply\r
163         the filter rather than selecting the current game.\r
164       */\r
165       if( LOWORD(wParam) == IDC_ChatPartner ) {\r
166           switch( HIWORD(wParam) ) {\r
167           case EN_SETFOCUS:\r
168               filterHasFocus[partner] = TRUE;\r
169               break;\r
170           case EN_KILLFOCUS:\r
171               filterHasFocus[partner] = FALSE;\r
172               break;\r
173           }\r
174       }\r
175 \r
176       if( filterHasFocus[partner] && (LOWORD(wParam) == IDC_Send) ) {\r
177           SetFocus(GetDlgItem(hDlg, OPT_ChatInput));\r
178           wParam = IDC_Change;\r
179       }\r
180       /* [AS] End command replacement */\r
181 \r
182         switch (LOWORD(wParam)) {\r
183 \r
184         case IDCANCEL:\r
185             chatHandle[partner] = 0;\r
186             chatPartner[partner][0] = 0;\r
187             ChatPopDown();\r
188             EndDialog(hDlg, TRUE);\r
189             break;\r
190 \r
191         case IDC_Clear:\r
192             SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), WM_SETTEXT, 0, (LPARAM) "" );\r
193             break;\r
194 \r
195         case IDC_Change:\r
196             GetDlgItemText(hDlg, IDC_ChatPartner, chatPartner[partner], MSG_SIZ);\r
197             break;\r
198 \r
199         case IDC_Send:\r
200             GetDlgItemText(hDlg, OPT_ChatInput, mess, MSG_SIZ);\r
201             SetDlgItemText(hDlg, OPT_ChatInput, "");\r
202             // from here on it could be back-end\r
203             if(!strcmp("WHISPER", chatPartner[partner]))\r
204                 sprintf(buf, "whisper %s\n", mess); // WHISPER box uses "whisper" to send\r
205             else {\r
206                 if(!atoi(chatPartner[partner])) {\r
207                     sprintf(buf, "> %s\n", mess); // echo only tells to handle, not channel\r
208                 InsertIntoMemo(hDlg, buf);\r
209                 sprintf(buf, "xtell %s %s\n", chatPartner[partner], mess);\r
210                 } else\r
211                 sprintf(buf, "tell %s %s\n", chatPartner[partner], mess);\r
212             }\r
213             SendToICS(buf);\r
214             break;\r
215 \r
216         default:\r
217           break;\r
218         }\r
219 \r
220         break;\r
221 \r
222     case WM_CLOSE:\r
223         chatHandle[partner] = 0;\r
224         chatPartner[partner][0] = 0;\r
225         ChatPopDown();\r
226         EndDialog(hDlg, TRUE);\r
227         break;\r
228 \r
229     case WM_SIZE:\r
230         ResizeWindowControls( hDlg );\r
231         break;\r
232 \r
233     case WM_ENTERSIZEMOVE:\r
234         return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
235 \r
236     case WM_SIZING:\r
237         return OnSizing( &sd, hDlg, wParam, lParam );\r
238 \r
239     case WM_MOVING:\r
240         return OnMoving( &sd, hDlg, wParam, lParam );\r
241 \r
242     case WM_EXITSIZEMOVE:\r
243         return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
244     }\r
245 \r
246     return FALSE;\r
247 }\r
248 \r
249 // front end\r
250 void ChatPopUp()\r
251 {\r
252   FARPROC lpProc;\r
253   \r
254   if(chatCount >= MAX_CHAT) return;\r
255 \r
256   CheckMenuItem(GetMenu(hwndMain), IDM_NewChat, MF_CHECKED);\r
257   chatCount++;\r
258 \r
259     lpProc = MakeProcInstance( (FARPROC) ChatProc, hInst );\r
260 \r
261     /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */\r
262     CreateDialog( hInst, MAKEINTRESOURCE(DLG_Chat), hwndMain, (DLGPROC)lpProc );\r
263 \r
264     FreeProcInstance(lpProc);\r
265 \r
266 }\r
267 \r
268 // front end\r
269 void ChatPopDown()\r
270 {\r
271   if(--chatCount <= 0)\r
272         CheckMenuItem(GetMenu(hwndMain), IDM_NewChat, MF_UNCHECKED);\r
273 }\r
274 \r
275 \r
276 //------------------------ pure back-end routines -------------------------------\r
277 \r
278 void OutputChatMessage(int partner, char *text)\r
279 {\r
280         if(!chatHandle[partner]) return;\r
281 \r
282         int n = strlen(text);\r
283         text[n+1] = 0; text[n] = '\n'; text[n-1] = '\r'; // Needs CR to not lose line breaks on copy-paste\r
284         InsertIntoMemo(chatHandle[partner], text);\r
285 }\r