4 * Author: H.G.Muller (August 2009)
\r
6 * Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
\r
7 * Software Foundation, Inc.
\r
9 * ------------------------------------------------------------------------
\r
11 * GNU XBoard is free software: you can redistribute it and/or modify
\r
12 * it under the terms of the GNU General Public License as published by
\r
13 * the Free Software Foundation, either version 3 of the License, or (at
\r
14 * your option) any later version.
\r
16 * GNU XBoard is distributed in the hope that it will be useful, but
\r
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
\r
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
19 * General Public License for more details.
\r
21 * You should have received a copy of the GNU General Public License
\r
22 * along with this program. If not, see http://www.gnu.org/licenses/. *
\r
24 *------------------------------------------------------------------------
\r
25 ** See the file ChangeLog for a revision history. */
\r
29 #include <windows.h> /* required for all Windows applications */
\r
30 #include <richedit.h>
\r
34 #include <commdlg.h>
\r
36 #include <Windowsx.h>
\r
39 #include "frontend.h"
\r
40 #include "winboard.h"
\r
41 #include "backend.h"
\r
47 extern char chatPartner[MAX_CHAT][MSG_SIZ];
\r
48 HANDLE chatHandle[MAX_CHAT];
\r
49 static WNDPROC chatInputWindowProc;
\r
51 void SendToICS P((char *s));
\r
52 void ChatPopUp P((char *s));
\r
55 /* Imports from backend.c */
\r
56 extern int opponentKibitzes;
\r
58 /* Imports from winboard.c */
\r
59 VOID SaveInHistory(char *cmd);
\r
60 char *PrevInHistory(char *cmd);
\r
61 char *NextInHistory();
\r
62 extern HWND ChatDialog;
\r
64 extern HINSTANCE hInst;
\r
65 extern HWND hwndConsole;
\r
66 extern char ics_handle[];
\r
68 extern WindowPlacement wpChat[MAX_CHAT];
\r
69 extern WindowPlacement wpConsole;
\r
71 extern BoardSize boardSize;
\r
73 /* Module variables */
\r
77 // front end, although we might make GetWindowRect front end instead
\r
78 static int GetControlWidth( HWND hDlg, int id )
\r
82 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
84 return rc.right - rc.left;
\r
88 static int GetControlHeight( HWND hDlg, int id )
\r
92 GetWindowRect( GetDlgItem( hDlg, id ), &rc );
\r
94 return rc.bottom - rc.top;
\r
97 static void SetControlPos( HWND hDlg, int id, int x, int y, int width, int height )
\r
99 HWND hControl = GetDlgItem( hDlg, id );
\r
101 SetWindowPos( hControl, HWND_TOP, x, y, width, height, SWP_NOZORDER );
\r
104 // Also here some of the size calculations should go to the back end, and their actual application to a front-end routine
\r
105 static void ResizeWindowControls( HWND hDlg )
\r
110 int maxControlWidth;
\r
111 int buttonWidth, buttonHeight;
\r
113 /* Initialize variables */
\r
114 GetClientRect( hDlg, &rc );
\r
116 clientWidth = rc.right - rc.left;
\r
117 clientHeight = rc.bottom - rc.top;
\r
119 maxControlWidth = clientWidth - 2*H_MARGIN;
\r
120 buttonWidth = GetControlWidth(hDlg, IDC_Send);
\r
121 buttonHeight = GetControlHeight(hDlg, IDC_Send);
\r
123 /* Resize controls */
\r
124 SetControlPos( hDlg, IDC_Clear, maxControlWidth+H_MARGIN-2*buttonWidth-5, V_MARGIN, buttonWidth, buttonHeight );
\r
125 SetControlPos( hDlg, IDC_Send, maxControlWidth+H_MARGIN-buttonWidth, V_MARGIN, buttonWidth, buttonHeight );
\r
126 SetControlPos( hDlg, IDC_ChatMemo, H_MARGIN, 2*V_MARGIN+buttonHeight, maxControlWidth, clientHeight-3*V_MARGIN-2*buttonHeight );
\r
127 SetControlPos( hDlg, OPT_ChatInput, H_MARGIN, clientHeight-V_MARGIN-buttonHeight, maxControlWidth, buttonHeight );
\r
129 // InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo1), NULL, FALSE );
\r
130 // InvalidateRect( GetDlgItem(hDlg,IDC_EngineMemo2), NULL, FALSE );
\r
133 // front end. Actual printing of PV lines into the output field
\r
134 static void InsertIntoMemo( HANDLE hDlg, char * text )
\r
136 HANDLE hMemo = GetDlgItem(hDlg, IDC_ChatMemo);
\r
138 SendMessage( hMemo, EM_SETSEL, 1000000, 1000000 );
\r
140 SendMessage( hMemo, EM_REPLACESEL, (WPARAM) FALSE, (LPARAM) text );
\r
141 SendMessage( hMemo, EM_SCROLLCARET, 0, 0);
\r
145 InterceptArrowKeys(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
\r
152 case WM_KEYDOWN: // cloned from ConsoleInputSubClass()
\r
155 GetWindowText(hwnd, buf, MSG_SIZ);
\r
156 p = PrevInHistory(buf);
\r
158 SetWindowText(hwnd, p);
\r
159 sel.cpMin = 999999;
\r
160 sel.cpMax = 999999;
\r
161 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
166 p = NextInHistory();
\r
168 SetWindowText(hwnd, p);
\r
169 sel.cpMin = 999999;
\r
170 sel.cpMax = 999999;
\r
171 SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);
\r
177 return (*chatInputWindowProc)(hwnd, message, wParam, lParam);
\r
180 // This seems pure front end
\r
181 LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
\r
183 static SnapData sd;
\r
184 char buf[MSG_SIZ], mess[MSG_SIZ];
\r
185 int partner = -1, i, x, y;
\r
186 static BOOL filterHasFocus[MAX_CHAT];
\r
190 for(i=0; i<MAX_CHAT; i++) if(hDlg == chatHandle[i]) { partner = i; break; }
\r
193 case WM_INITDIALOG:
\r
194 Translate(hDlg, DLG_Chat);
\r
196 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] == NULL) { partner = i; break; }
\r
197 chatHandle[partner] = hDlg;
\r
198 snprintf(buf, MSG_SIZ, T_("Chat Window %s"), ics_handle[0] ? ics_handle : first.tidy);
\r
199 SetWindowText(hDlg, buf);
\r
201 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i]) {
\r
202 if(i == partner) continue;
\r
203 // set our button in other open chats
\r
204 SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]);
\r
205 EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 1 );
\r
206 // and buttons for other chats in ours
\r
207 SetDlgItemText(hDlg, IDC_Focus1+i-(i>partner), chatPartner[i]);
\r
208 } else EnableWindow( GetDlgItem(hDlg, IDC_Focus1+i-(i>partner)), 0 );
\r
209 for(i=0; i<MAX_CHAT-1; i++) { Button_SetStyle(GetDlgItem(hDlg, IDC_Focus1+i), BS_PUSHBUTTON|BS_LEFT, TRUE); }
\r
210 x = wpConsole.x; y = wpConsole.y; EnsureOnScreen(&x, &y, 0, 0);
\r
211 SetWindowPos(hDlg, NULL, x, y, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
\r
212 SendMessage( GetDlgItem(hDlg, IDC_ChatPartner), // [HGM] clickbox: initialize with requested handle
\r
213 WM_SETTEXT, 0, (LPARAM) chatPartner[partner] );
\r
214 filterHasFocus[partner] = TRUE;
\r
215 onTop = partner; // a newly opened box becomes top one
\r
216 if(chatPartner[partner][0]) {
\r
217 filterHasFocus[partner] = FALSE;
\r
218 SetFocus( GetDlgItem(hDlg, OPT_ChatInput) );
\r
220 hMemo = GetDlgItem(hDlg, IDC_ChatMemo);
\r
221 wMask = (WORD) SendMessage(hMemo, EM_GETEVENTMASK, 0, 0L);
\r
222 SendMessage(hMemo, EM_SETEVENTMASK, 0, wMask | ENM_LINK);
\r
223 SendMessage(hMemo, EM_AUTOURLDETECT, TRUE, 0L);
\r
224 chatInputWindowProc = (WNDPROC) // cloned from ConsoleWndProc(). Assume they all share same proc.
\r
225 SetWindowLongPtr(GetDlgItem(hDlg, OPT_ChatInput), GWLP_WNDPROC, (LONG_PTR) InterceptArrowKeys);
\r
229 if (((NMHDR*)lParam)->code == EN_LINK)
\r
231 ENLINK *pLink = (ENLINK*)lParam;
\r
232 if (pLink->msg == WM_LBUTTONUP)
\r
236 tr.chrg = pLink->chrg;
\r
237 tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);
\r
238 SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
\r
239 ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);
\r
240 free(tr.lpstrText);
\r
248 If <Enter> is pressed while editing the filter, it's better to apply
\r
249 the filter rather than selecting the current game.
\r
251 if( LOWORD(wParam) == IDC_ChatPartner ) {
\r
252 switch( HIWORD(wParam) ) {
\r
254 filterHasFocus[partner] = TRUE;
\r
257 filterHasFocus[partner] = FALSE;
\r
262 if( filterHasFocus[partner] && (LOWORD(wParam) == IDC_Send) ) {
\r
263 SetFocus(GetDlgItem(hDlg, OPT_ChatInput));
\r
264 wParam = IDC_Change;
\r
266 /* [AS] End command replacement */
\r
268 switch (LOWORD(wParam)) {
\r
270 case IDCANCEL: /* let Esc key switch focus back to console */
\r
271 SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));
\r
275 SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), WM_SETTEXT, 0, (LPARAM) "" );
\r
279 GetDlgItemText(hDlg, IDC_ChatPartner, chatPartner[partner], MSG_SIZ);
\r
280 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) {
\r
281 // set our button in other open chats
\r
282 SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]);
\r
287 GetDlgItemText(hDlg, OPT_ChatInput, mess, MSG_SIZ);
\r
288 SetDlgItemText(hDlg, OPT_ChatInput, "");
\r
289 // from here on it could be back-end
\r
290 SaveInHistory(mess);
\r
291 if(!strcmp("whispers", chatPartner[partner]))
\r
292 snprintf(buf, MSG_SIZ, "whisper %s\n", mess); // WHISPER box uses "whisper" to send
\r
293 else if(!strcmp("shouts", chatPartner[partner]))
\r
294 snprintf(buf, MSG_SIZ, "shout %s\n", mess); // SHOUT box uses "shout" to send
\r
296 if(!atoi(chatPartner[partner])) {
\r
297 snprintf(buf, MSG_SIZ, "> %s\r\n", mess); // echo only tells to handle, not channel
\r
298 InsertIntoMemo(hDlg, buf);
\r
299 snprintf(buf, MSG_SIZ, "xtell %s %s\n", chatPartner[partner], mess);
\r
301 snprintf(buf, MSG_SIZ, "tell %s %s\n", chatPartner[partner], mess);
\r
310 i = LOWORD(wParam) - IDC_Focus1;
\r
311 if(i >= partner) i++;
\r
313 SetFocus(GetDlgItem(hDlg, IDC_Send));
\r
314 if(chatHandle[i]) {
\r
316 for(j=0; j<MAX_CHAT; j++) if(i != j && chatHandle[j])
\r
317 Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+i-(j<i)), FALSE);
\r
318 SetFocus(GetDlgItem(chatHandle[i], OPT_ChatInput));
\r
329 chatHandle[partner] = 0;
\r
330 chatPartner[partner][0] = 0;
\r
332 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) {
\r
333 // set our button in other open chats
\r
334 SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), "");
\r
335 EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 0 );
\r
337 EndDialog(hDlg, TRUE);
\r
341 ResizeWindowControls( hDlg );
\r
344 case WM_ENTERSIZEMOVE:
\r
345 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
\r
348 return OnSizing( &sd, hDlg, wParam, lParam );
\r
351 return OnMoving( &sd, hDlg, wParam, lParam );
\r
353 case WM_EXITSIZEMOVE:
\r
354 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
\r
361 void ChatPopUp(char *icsHandle)
\r
364 int i, partner = -1;
\r
366 static int first = 1;
\r
368 CheckMenuItem(GetMenu(hwndMain), IDM_NewChat, MF_CHECKED);
\r
369 for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] == NULL) { partner = i; break; }
\r
370 if(partner == -1) { DisplayError("You first have to close a Chat Box\nbefore you can open a new one", 0); return; }
\r
371 if(icsHandle) { // [HGM] clickbox set handle in advance
\r
372 safeStrCpy(chatPartner[partner], icsHandle,
\r
373 sizeof(chatPartner[partner])/sizeof(chatPartner[partner][0]) );
\r
374 if(sscanf(icsHandle, "%d", &i) == 1) { // make sure channel is on
\r
375 snprintf(buf, MSG_SIZ, "addlist ch %d\n", i);
\r
377 if(first) first=0, SendToICS(buf); // work-around for weirdness: On public FICS code first attempt on login is completely ignored
\r
379 } else chatPartner[partner][0] = NULLCHAR;
\r
382 lpProc = MakeProcInstance( (FARPROC) ChatProc, hInst );
\r
384 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
\r
385 CreateDialog( hInst, MAKEINTRESOURCE(DLG_Chat), hwndConsole, (DLGPROC)lpProc );
\r
387 FreeProcInstance(lpProc);
\r
394 if(--chatCount <= 0)
\r
395 CheckMenuItem(GetMenu(hwndMain), IDM_NewChat, MF_UNCHECKED);
\r
399 //------------------------ pure back-end routines -------------------------------
\r
401 void OutputChatMessage(int partner, char *text)
\r
403 int j, n = strlen(text);
\r
405 if(!chatHandle[partner]) return;
\r
406 text[n+1] = 0; text[n] = '\n'; text[n-1] = '\r'; // Needs CR to not lose line breaks on copy-paste
\r
407 InsertIntoMemo(chatHandle[partner], text);
\r
408 if(partner != onTop) for(j=0; j<MAX_CHAT; j++) if(j != partner && chatHandle[j])
\r
409 Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+partner-(j<partner)), TRUE);
\r