1 /*
2  * PROJECT:     ReactOS Clipboard Viewer
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Clipboard helper functions.
5  * COPYRIGHT:   Copyright 2015-2018 Ricardo Hanke
6  *              Copyright 2015-2018 Hermes Belusca-Maito
7  */
8 
9 #include "precomp.h"
10 
11 LRESULT
SendClipboardOwnerMessage(IN BOOL bUnicode,IN UINT uMsg,IN WPARAM wParam,IN LPARAM lParam)12 SendClipboardOwnerMessage(
13     IN BOOL bUnicode,
14     IN UINT uMsg,
15     IN WPARAM wParam,
16     IN LPARAM lParam)
17 {
18     HWND hwndOwner;
19 
20     hwndOwner = GetClipboardOwner();
21     if (!hwndOwner)
22         return GetLastError();
23 
24     if (bUnicode)
25         return SendMessageW(hwndOwner, uMsg, wParam, lParam);
26     else
27         return SendMessageA(hwndOwner, uMsg, wParam, lParam);
28 }
29 
30 static int
GetPredefinedClipboardFormatName(HINSTANCE hInstance,UINT uFormat,BOOL Unicode,PVOID lpszFormat,UINT cch)31 GetPredefinedClipboardFormatName(HINSTANCE hInstance,
32                                  UINT uFormat,
33                                  BOOL Unicode,
34                                  PVOID lpszFormat,
35                                  UINT cch)
36 {
37     static
38     struct FORMAT_NAME
39     {
40         UINT uFormat;
41         UINT uResID;
42     } uFormatList[] = {
43     /* Table sorted in increasing order of CF_xxx values, please keep it this way! */
44         {CF_TEXT        , STRING_CF_TEXT        },          // 1
45         {CF_BITMAP      , STRING_CF_BITMAP      },          // 2
46         {CF_METAFILEPICT, STRING_CF_METAFILEPICT},          // 3
47         {CF_SYLK        , STRING_CF_SYLK        },          // 4
48         {CF_DIF         , STRING_CF_DIF         },          // 5
49         {CF_TIFF        , 0/*STRING_CF_TIFF*/        },          // 6
50         {CF_OEMTEXT     , STRING_CF_OEMTEXT     },          // 7
51         {CF_DIB         , STRING_CF_DIB         },          // 8
52         {CF_PALETTE     , STRING_CF_PALETTE     },          // 9
53         {CF_PENDATA     , 0/*STRING_CF_PENDATA*/     },          // 10
54         {CF_RIFF        , 0/*STRING_CF_RIFF*/        },          // 11
55         {CF_WAVE        , 0/*STRING_CF_WAVE*/        },          // 12
56         {CF_UNICODETEXT , STRING_CF_UNICODETEXT },          // 13
57         {CF_ENHMETAFILE , STRING_CF_ENHMETAFILE },          // 14
58 #if(WINVER >= 0x0400)
59         {CF_HDROP       , STRING_CF_HDROP       },          // 15
60         {CF_LOCALE      , STRING_CF_LOCALE      },          // 16
61 #endif
62 #if(WINVER >= 0x0500)
63         {CF_DIBV5       , STRING_CF_DIBV5       },          // 17
64 #endif
65     };
66 
67     switch (uFormat)
68     {
69         case CF_TEXT: case CF_BITMAP: case CF_METAFILEPICT:
70         case CF_SYLK: case CF_DIF: // case CF_TIFF:
71         case CF_OEMTEXT: case CF_DIB: case CF_PALETTE:
72         // case CF_PENDATA: // case CF_RIFF: // case CF_WAVE:
73         case CF_UNICODETEXT: case CF_ENHMETAFILE:
74 #if(WINVER >= 0x0400)
75         case CF_HDROP: case CF_LOCALE:
76 #endif
77 #if(WINVER >= 0x0500)
78         case CF_DIBV5:
79 #endif
80         {
81             if (Unicode)
82                 return LoadStringW(hInstance, uFormatList[uFormat-1].uResID, (LPWSTR)lpszFormat, cch);
83             else
84                 return LoadStringA(hInstance, uFormatList[uFormat-1].uResID, (LPSTR)lpszFormat, cch);
85         }
86 
87         default:
88         {
89             return 0;
90         }
91     }
92 }
93 
94 void
RetrieveClipboardFormatName(HINSTANCE hInstance,UINT uFormat,BOOL Unicode,PVOID lpszFormat,UINT cch)95 RetrieveClipboardFormatName(HINSTANCE hInstance,
96                             UINT uFormat,
97                             BOOL Unicode,
98                             PVOID lpszFormat,
99                             UINT cch)
100 {
101     ZeroMemory(lpszFormat, cch * (Unicode ? sizeof(WCHAR) : sizeof(CHAR)));
102 
103     /* Check for predefined clipboard format */
104     if (GetPredefinedClipboardFormatName(hInstance, uFormat, Unicode, lpszFormat, cch) != 0)
105         return;
106 
107     /* Check for owner-display format */
108     if (uFormat == CF_OWNERDISPLAY)
109     {
110         if (SendClipboardOwnerMessage(Unicode, WM_ASKCBFORMATNAME,
111                                       (WPARAM)cch, (LPARAM)lpszFormat) != 0)
112         {
113             if (Unicode)
114                 LoadStringW(hInstance, STRING_CF_UNKNOWN, (LPWSTR)lpszFormat, cch);
115             else
116                 LoadStringA(hInstance, STRING_CF_UNKNOWN, (LPSTR)lpszFormat, cch);
117         }
118         return;
119     }
120 
121     /* Fallback to registered clipboard format */
122     if (Unicode)
123     {
124         if (!GetClipboardFormatNameW(uFormat, (LPWSTR)lpszFormat, cch))
125             LoadStringW(hInstance, STRING_CF_UNKNOWN, (LPWSTR)lpszFormat, cch);
126     }
127     else
128     {
129         if (!GetClipboardFormatNameA(uFormat, (LPSTR)lpszFormat, cch))
130             LoadStringA(hInstance, STRING_CF_UNKNOWN, (LPSTR)lpszFormat, cch);
131     }
132 }
133 
DeleteClipboardContent(void)134 void DeleteClipboardContent(void)
135 {
136     if (!OpenClipboard(Globals.hMainWnd))
137     {
138         ShowLastWin32Error(Globals.hMainWnd);
139         return;
140     }
141 
142     if (!EmptyClipboard())
143     {
144         ShowLastWin32Error(Globals.hMainWnd);
145     }
146 
147     CloseClipboard();
148 }
149 
GetAutomaticClipboardFormat(void)150 UINT GetAutomaticClipboardFormat(void)
151 {
152     static UINT uFormatList[] =
153     {
154         CF_OWNERDISPLAY,
155         CF_UNICODETEXT,
156         CF_TEXT,
157         CF_OEMTEXT,
158         CF_ENHMETAFILE,
159         CF_METAFILEPICT,
160         CF_DIBV5,
161         CF_DIB,
162         CF_BITMAP,
163         CF_DSPTEXT,
164         CF_DSPBITMAP,
165         CF_DSPMETAFILEPICT,
166         CF_DSPENHMETAFILE,
167         CF_PALETTE,
168         CF_HDROP
169     };
170 
171     return GetPriorityClipboardFormat(uFormatList, ARRAYSIZE(uFormatList));
172 }
173 
IsClipboardFormatSupported(UINT uFormat)174 BOOL IsClipboardFormatSupported(UINT uFormat)
175 {
176     switch (uFormat)
177     {
178         case CF_OWNERDISPLAY:
179         case CF_UNICODETEXT:
180         case CF_TEXT:
181         case CF_OEMTEXT:
182         case CF_BITMAP:
183         case CF_ENHMETAFILE:
184         case CF_METAFILEPICT:
185         case CF_DIB:
186         case CF_DIBV5:
187         case CF_HDROP:
188         {
189             return TRUE;
190         }
191 
192         default:
193         {
194             return FALSE;
195         }
196     }
197 }
198 
199 SIZE_T
GetLineExtentW(IN LPCWSTR lpText,OUT LPCWSTR * lpNextLine)200 GetLineExtentW(
201     IN LPCWSTR lpText,
202     OUT LPCWSTR* lpNextLine)
203 {
204     LPCWSTR ptr;
205 
206     /* Find the next line of text (lpText is NULL-terminated) */
207     /* For newlines, focus only on '\n', not on '\r' */
208     ptr = wcschr(lpText, L'\n'); // Find the end of this line.
209     if (ptr)
210     {
211         /* We have the end of this line, go to the next line (ignore the endline in the count) */
212         *lpNextLine = ptr + 1;
213     }
214     else
215     {
216         /* This line was the last one, go pointing to the terminating NULL */
217         ptr = lpText + wcslen(lpText);
218         *lpNextLine = ptr;
219     }
220 
221     return (ptr - lpText);
222 }
223 
224 SIZE_T
GetLineExtentA(IN LPCSTR lpText,OUT LPCSTR * lpNextLine)225 GetLineExtentA(
226     IN LPCSTR lpText,
227     OUT LPCSTR* lpNextLine)
228 {
229     LPCSTR ptr;
230 
231     /* Find the next line of text (lpText is NULL-terminated) */
232     /* For newlines, focus only on '\n', not on '\r' */
233     ptr = strchr(lpText, '\n'); // Find the end of this line.
234     if (ptr)
235     {
236         /* We have the end of this line, go to the next line (ignore the endline in the count) */
237         *lpNextLine = ptr + 1;
238     }
239     else
240     {
241         /* This line was the last one, go pointing to the terminating NULL */
242         ptr = lpText + strlen(lpText);
243         *lpNextLine = ptr;
244     }
245 
246     return (ptr - lpText);
247 }
248 
GetClipboardDataDimensions(UINT uFormat,PRECT pRc)249 BOOL GetClipboardDataDimensions(UINT uFormat, PRECT pRc)
250 {
251     SetRectEmpty(pRc);
252 
253     if (!OpenClipboard(Globals.hMainWnd))
254     {
255         return FALSE;
256     }
257 
258     switch (uFormat)
259     {
260         case CF_DSPBITMAP:
261         case CF_BITMAP:
262         {
263             HBITMAP hBitmap;
264             BITMAP bmp;
265 
266             hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
267             GetObjectW(hBitmap, sizeof(bmp), &bmp);
268             SetRect(pRc, 0, 0, bmp.bmWidth, bmp.bmHeight);
269             break;
270         }
271 
272         case CF_DIB:
273         case CF_DIBV5:
274         {
275             HGLOBAL hGlobal;
276             LPBITMAPINFOHEADER lpInfoHeader;
277 
278             hGlobal = GetClipboardData(uFormat);
279             if (!hGlobal)
280                 break;
281 
282             lpInfoHeader = GlobalLock(hGlobal);
283             if (!lpInfoHeader)
284                 break;
285 
286             if (lpInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
287             {
288                 LPBITMAPCOREHEADER lpCoreHeader = (LPBITMAPCOREHEADER)lpInfoHeader;
289                 SetRect(pRc, 0, 0,
290                         lpCoreHeader->bcWidth,
291                         lpCoreHeader->bcHeight);
292             }
293             else if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) ||
294                      (lpInfoHeader->biSize == sizeof(BITMAPV4HEADER))   ||
295                      (lpInfoHeader->biSize == sizeof(BITMAPV5HEADER)))
296             {
297                 SetRect(pRc, 0, 0,
298                         lpInfoHeader->biWidth,
299                         /* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */
300                         (lpInfoHeader->biHeight > 0) ?  lpInfoHeader->biHeight
301                                                      : -lpInfoHeader->biHeight);
302             }
303             else
304             {
305                 /* Invalid format */
306             }
307 
308             GlobalUnlock(hGlobal);
309             break;
310         }
311 
312         case CF_DSPTEXT:
313         case CF_TEXT:
314         case CF_OEMTEXT:
315         case CF_UNICODETEXT:
316         {
317             HDC hDC;
318             HGLOBAL hGlobal;
319             PVOID lpText, ptr;
320             DWORD dwSize;
321             SIZE txtSize = {0, 0};
322             SIZE_T lineSize;
323 
324             hGlobal = GetClipboardData(uFormat);
325             if (!hGlobal)
326                 break;
327 
328             lpText = GlobalLock(hGlobal);
329             if (!lpText)
330                 break;
331 
332             hDC = GetDC(Globals.hMainWnd);
333 
334             /* Find the size of the rectangle enclosing the text */
335             for (;;)
336             {
337                 if (uFormat == CF_UNICODETEXT)
338                 {
339                     if (*(LPCWSTR)lpText == UNICODE_NULL)
340                         break;
341                     lineSize = GetLineExtentW(lpText, (LPCWSTR*)&ptr);
342                     dwSize = GetTabbedTextExtentW(hDC, lpText, lineSize, 0, NULL);
343                 }
344                 else
345                 {
346                     if (*(LPCSTR)lpText == ANSI_NULL)
347                         break;
348                     lineSize = GetLineExtentA(lpText, (LPCSTR*)&ptr);
349                     dwSize = GetTabbedTextExtentA(hDC, lpText, lineSize, 0, NULL);
350                 }
351                 txtSize.cx = max(txtSize.cx, LOWORD(dwSize));
352                 txtSize.cy += HIWORD(dwSize);
353                 lpText = ptr;
354             }
355 
356             ReleaseDC(Globals.hMainWnd, hDC);
357 
358             GlobalUnlock(hGlobal);
359 
360             SetRect(pRc, 0, 0, txtSize.cx, txtSize.cy);
361             break;
362         }
363 
364         default:
365         {
366             break;
367         }
368     }
369 
370     CloseClipboard();
371 
372     return TRUE;
373 }
374