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
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
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
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 
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 
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 
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
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
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 
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