Fix multi-leg promotions
[xboard.git] / winboard / wclipbrd.c
1 /*\r
2  * wclipbrd.c -- Clipboard routines for WinBoard\r
3  *\r
4  * Copyright 2000, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
5  * Software Foundation, Inc.\r
6  *\r
7  * Enhancements Copyright 2005 Alessandro Scotti\r
8  *\r
9  * ------------------------------------------------------------------------\r
10  *\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
15  *\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
20  *\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
23  *\r
24  *------------------------------------------------------------------------\r
25  ** See the file ChangeLog for a revision history.  */\r
26 \r
27 #include "config.h"\r
28 \r
29 #include <windows.h>   /* required for all Windows applications */\r
30 #include <stdio.h>\r
31 #include <stdlib.h>\r
32 #include <malloc.h>\r
33 #include <sys/stat.h>\r
34 \r
35 #include "common.h"\r
36 #include "frontend.h"\r
37 #include "backend.h"\r
38 #include "winboard.h"\r
39 #include "wclipbrd.h"\r
40 \r
41 #define _(s) T_(s)\r
42 #define N_(s) s\r
43 \r
44 /* Imports from winboard.c */\r
45 extern HWND hwndMain;\r
46 \r
47 /* File globals */\r
48 static char *copyTemp;\r
49 static char *pasteTemp;\r
50 \r
51 VOID \r
52 CopyFENToClipboard()\r
53 {\r
54   char *fen = NULL;\r
55 \r
56   if(gameMode == EditPosition) EditPositionDone(TRUE); // [HGM] mak sure castling rights are set consistently\r
57   fen = PositionToFEN(currentMove, NULL, 1);\r
58   if (!fen) {\r
59     DisplayError(_("Unable to convert position to FEN."), 0);\r
60     return;\r
61   }\r
62   if (!CopyTextToClipboard(fen))\r
63       DisplayError(_("Unable to copy FEN to clipboard."), 0);\r
64   free(fen);\r
65 }\r
66 \r
67 /* [AS] */\r
68 HGLOBAL ExportGameListAsText();\r
69 \r
70 VOID CopyGameListToClipboard()\r
71 {\r
72     HGLOBAL hMem = ExportGameListAsText();\r
73     \r
74     if( hMem != NULL ) {\r
75         /* Assign memory block to clipboard */\r
76         BOOL ok = OpenClipboard( hwndMain );\r
77 \r
78         if( ok ) {\r
79             ok = EmptyClipboard();\r
80 \r
81             if( ok ) {\r
82                 if( hMem != SetClipboardData( CF_TEXT, hMem ) ) {\r
83                     ok = FALSE;\r
84                 }\r
85             }\r
86 \r
87             CloseClipboard();\r
88 \r
89             if( ! ok ) {\r
90                 GlobalFree( hMem );\r
91             }\r
92         }\r
93 \r
94         if( ! ok ) {\r
95             DisplayError( "Cannot copy list to clipboard.", 0 );\r
96         }\r
97     }\r
98 }\r
99 \r
100 VOID\r
101 CopyGameToClipboard()\r
102 {\r
103   /* A rather cheesy hack here. Write the game to a file, then read from the\r
104    * file into the clipboard.\r
105    */\r
106   char *buf = NULL;\r
107   FILE *f;\r
108   unsigned long size;\r
109   size_t len;\r
110   struct stat st;\r
111 \r
112   if (!copyTemp) {\r
113     copyTemp = tempnam(NULL, "wbcp");\r
114   }\r
115   if (!copyTemp) {\r
116       DisplayError(_("Cannot create temporary file name."),0);\r
117       return;\r
118   }\r
119   f = fopen(copyTemp, "w");\r
120   if (!f) {\r
121     DisplayError(_("Cannot open temporary file."), 0);\r
122     return;\r
123   }\r
124   if (!SaveGame(f,0,"")) {                      /* call into backend */\r
125     DisplayError(_("Cannot write to temporary file."), 0);\r
126     goto copy_game_to_clipboard_cleanup;\r
127   }\r
128   f = fopen(copyTemp, "rb");\r
129   if (!f) {\r
130     DisplayError(_("Cannot reopen temporary file."), 0);\r
131     goto copy_game_to_clipboard_cleanup;\r
132   }\r
133   if (fstat(fileno(f), &st) < 0) {\r
134     DisplayError(_(_("Cannot determine size of file.")), 0);\r
135     goto copy_game_to_clipboard_cleanup;\r
136   }\r
137   size = st.st_size;\r
138   if (size == -1) {\r
139     DisplayError(_(_("Cannot determine size of file.")), 0);\r
140     goto copy_game_to_clipboard_cleanup;\r
141   }\r
142   rewind(f);\r
143   buf = (char*)malloc(size+1);\r
144   if (!buf) {\r
145     DisplayError(_("Cannot allocate clipboard buffer."), 0);\r
146     goto copy_game_to_clipboard_cleanup;\r
147   }\r
148   len = fread(buf, sizeof(char), size, f);\r
149   if (len == -1) {\r
150     DisplayError(_("Cannot read from temporary file."), 0);\r
151     goto copy_game_to_clipboard_cleanup;\r
152   }\r
153   if ((unsigned long)size != (unsigned long)len) { /* sigh */ \r
154       DisplayError(_("Error reading from temporary file."), 0);\r
155       goto copy_game_to_clipboard_cleanup;\r
156   }\r
157   buf[size] = 0;\r
158   if (!CopyTextToClipboard(buf)) {\r
159       DisplayError(_("Cannot copy text to clipboard"), 0);\r
160   }\r
161 \r
162 copy_game_to_clipboard_cleanup:\r
163   if (buf) free(buf);\r
164   if (f) fclose(f);\r
165 }\r
166 \r
167 \r
168 int \r
169 CopyTextToClipboard(char *text)\r
170 {\r
171   /* some (most?) of the error checking may be overkill, \r
172    * but hey, this is Windows \r
173    */\r
174   HGLOBAL hGlobalMem;\r
175   LPVOID lpGlobalMem;\r
176   BOOL locked;\r
177   UINT lockCount;\r
178   DWORD err;\r
179 \r
180   hGlobalMem = GlobalAlloc(GHND, (DWORD)lstrlen(text)+1);\r
181   if (hGlobalMem == NULL) {\r
182     DisplayError(_("Unable to allocate memory for clipboard."), 0);\r
183     return FALSE;\r
184   }\r
185   lpGlobalMem = GlobalLock(hGlobalMem);\r
186   if (lpGlobalMem == NULL) {\r
187     DisplayError(_(_("Unable to lock clipboard memory.")), 0);\r
188     GlobalFree(hGlobalMem);\r
189     return FALSE;\r
190   }\r
191   safeStrCpy(lpGlobalMem, text, 1<<20);\r
192   if (appData.debugMode) {\r
193     lockCount = GlobalFlags(hGlobalMem) & GMEM_LOCKCOUNT;\r
194     fprintf(debugFP, "CopyTextToClipboard(): lock count %d\n", lockCount);\r
195   }\r
196   SetLastError(NO_ERROR);\r
197   locked = GlobalUnlock(hGlobalMem);\r
198   err = GetLastError();\r
199   if (appData.debugMode) {\r
200     lockCount = GlobalFlags(hGlobalMem) & GMEM_LOCKCOUNT;\r
201     fprintf(debugFP, "CopyTextToClipboard(): lock count %d\n", lockCount);\r
202   }\r
203   if (!locked) {\r
204     locked = !((err == NO_ERROR) || (err == ERROR_NOT_LOCKED));\r
205     if (appData.debugMode) {\r
206       fprintf(debugFP, \r
207               "CopyTextToClipboard(): err %d locked %d\n", (int)err, locked);\r
208     }\r
209   }\r
210   if (locked) {\r
211     DisplayError(_("Cannot unlock clipboard memory."), 0);\r
212     GlobalFree(hGlobalMem);\r
213     return FALSE;\r
214   }\r
215   if (!OpenClipboard(hwndMain)) {\r
216     DisplayError(_("Cannot open clipboard."), 0);\r
217     GlobalFree(hGlobalMem);\r
218     return FALSE;\r
219   }\r
220   if (!EmptyClipboard()) {\r
221     DisplayError(_("Cannot empty clipboard."), 0);\r
222     return FALSE;\r
223   }\r
224   if (hGlobalMem != SetClipboardData(CF_TEXT, hGlobalMem)) {\r
225     DisplayError(_("Cannot copy text to clipboard."), 0);\r
226     CloseClipboard();\r
227     GlobalFree(hGlobalMem);\r
228     return FALSE;\r
229   }\r
230   if (!CloseClipboard())\r
231     DisplayError(_("Cannot close clipboard."), 0);\r
232   \r
233   return TRUE;\r
234 }\r
235 \r
236 /* [AS] Reworked paste functions so they can work with strings too */\r
237 \r
238 VOID PasteFENFromString( char * fen )\r
239 {\r
240   if (appData.debugMode) {\r
241     fprintf(debugFP, "PasteFenFromString(): fen '%s'\n", fen);\r
242   }\r
243   EditPositionPasteFEN(fen); /* call into backend */\r
244   free(fen);\r
245 }\r
246 \r
247 \r
248 VOID\r
249 PasteFENFromClipboard()\r
250 {\r
251   char *fen = NULL;\r
252   if (!PasteTextFromClipboard(&fen)) {\r
253       DisplayError(_("Unable to paste FEN from clipboard."), 0);\r
254       return;\r
255   }\r
256   PasteFENFromString( fen );\r
257 }\r
258 \r
259 VOID PasteGameFromString( char * buf )\r
260 {\r
261   FILE *f;\r
262   size_t len;\r
263   int flip = appData.flipView;\r
264   if (!pasteTemp) {\r
265     pasteTemp = tempnam(NULL, "wbpt");\r
266   }\r
267   f = fopen(pasteTemp, "w");\r
268   if (!f) {\r
269     DisplayError(_("Unable to create temporary file."), 0);\r
270     free(buf); /* [AS] */\r
271     return;\r
272   }\r
273   len = fwrite(buf, sizeof(char), strlen(buf), f);\r
274   fclose(f);\r
275   if (len != strlen(buf)) {\r
276     DisplayError(_("Error writing to temporary file."), 0);\r
277     free(buf); /* [AS] */\r
278     return;\r
279   }\r
280   if(!appData.autoFlipView) appData.flipView = flipView;\r
281   LoadGameFromFile(pasteTemp, 0, "Clipboard", TRUE);\r
282   appData.flipView = flip;\r
283   free( buf ); /* [AS] */\r
284 }\r
285 \r
286 \r
287 VOID\r
288 PasteGameFromClipboard()\r
289 {\r
290   /* Write the clipboard to a temp file, then let LoadGameFromFile()\r
291    * do all the work.  */\r
292   char *buf;\r
293   if (!PasteTextFromClipboard(&buf)) {\r
294     return;\r
295   }\r
296   PasteGameFromString( buf );\r
297 }\r
298 \r
299 /* [AS] Try to detect whether the clipboard contains FEN or PGN data */\r
300 VOID PasteGameOrFENFromClipboard()\r
301 {\r
302   char *buf;\r
303 //  char *tmp;\r
304   Board dummyBoard; int dummy; // [HGM] paste any\r
305 \r
306   if (!PasteTextFromClipboard(&buf)) {\r
307 \r
308     return;\r
309   }\r
310 \r
311   // [HGM] paste any: make still smarter, to allow pasting of games without tags, recognize FEN in stead\r
312   if(!ParseFEN(dummyBoard, &dummy, buf, 0) ) {\r
313       PasteGameFromString( buf );\r
314   }\r
315   else {\r
316       PasteFENFromString( buf );\r
317   }\r
318 }\r
319 \r
320 int \r
321 PasteTextFromClipboard(char **text)\r
322 {\r
323   /* some (most?) of the error checking may be overkill, \r
324    * but hey, this is Windows \r
325    */\r
326   HANDLE hClipMem;\r
327   LPVOID lpClipMem;\r
328   BOOL locked = FALSE;\r
329   DWORD err;\r
330   UINT lockCount;\r
331 \r
332   if (!OpenClipboard(hwndMain)) {\r
333     DisplayError(_("Unable to open clipboard."), 0);\r
334     return FALSE;\r
335   }\r
336   hClipMem = GetClipboardData(CF_TEXT);\r
337   if (hClipMem == NULL) {\r
338     CloseClipboard();\r
339     DisplayError(_("No text in clipboard."), 0);\r
340     return FALSE;\r
341   }\r
342   lpClipMem = GlobalLock(hClipMem);\r
343   if (lpClipMem == NULL) {\r
344     CloseClipboard();\r
345     DisplayError(_(_("Unable to lock clipboard memory.")), 0);\r
346     return FALSE;\r
347   }\r
348   *text = (char *) malloc(GlobalSize(hClipMem)+1);\r
349   if (!*text) {\r
350     DisplayError(_("Unable to allocate memory for text string."), 0);\r
351     CloseClipboard();\r
352     return FALSE;\r
353   }\r
354   safeStrCpy(*text, lpClipMem, 1<<20 );\r
355   if (appData.debugMode) {\r
356     lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;\r
357     fprintf(debugFP, "PasteTextFromClipboard(): lock count %d\n", lockCount);\r
358   }\r
359   SetLastError(NO_ERROR);\r
360   /*suggested by Wilkin Ng*/\r
361   lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;\r
362   if (lockCount) {\r
363     locked = GlobalUnlock(hClipMem);\r
364   }\r
365   err = GetLastError();\r
366   if (appData.debugMode) {\r
367     lockCount = GlobalFlags(hClipMem) & GMEM_LOCKCOUNT;\r
368     fprintf(debugFP, "PasteTextFromClipboard(): lock count %d\n", lockCount);\r
369   }\r
370   if (!locked) {\r
371     locked = !((err == NO_ERROR) || (err == ERROR_NOT_LOCKED));\r
372     if (appData.debugMode) {\r
373       fprintf(debugFP, \r
374               "PasteTextFromClipboard(): err %d locked %d\n", (int)err, locked);\r
375     }\r
376   }\r
377   if (locked) \r
378     DisplayError(_("Unable to unlock clipboard memory."), 0);\r
379   \r
380   if (!CloseClipboard())\r
381     DisplayError(_("Unable to close clipboard."), 0);\r
382   \r
383   return TRUE;\r
384 }\r
385 \r
386 VOID\r
387 DeleteClipboardTempFiles()\r
388 {\r
389   if (copyTemp) remove(copyTemp);\r
390   if (pasteTemp) remove(pasteTemp);\r
391 }\r