[CLIPBRD] Improvements for the Clipboard Viewer.
[reactos.git] / base / applications / clipbrd / cliputils.c
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 };
169
170 return GetPriorityClipboardFormat(uFormatList, ARRAYSIZE(uFormatList));
171 }
172
173 BOOL IsClipboardFormatSupported(UINT uFormat)
174 {
175 switch (uFormat)
176 {
177 case CF_OWNERDISPLAY:
178 case CF_UNICODETEXT:
179 case CF_TEXT:
180 case CF_OEMTEXT:
181 case CF_BITMAP:
182 case CF_ENHMETAFILE:
183 case CF_METAFILEPICT:
184 case CF_DIB:
185 case CF_DIBV5:
186 case CF_HDROP:
187 {
188 return TRUE;
189 }
190
191 default:
192 {
193 return FALSE;
194 }
195 }
196 }
197
198 SIZE_T
199 GetLineExtentW(
200 IN LPCWSTR lpText,
201 OUT LPCWSTR* lpNextLine)
202 {
203 LPCWSTR ptr;
204
205 /* Find the next line of text (lpText is NULL-terminated) */
206 /* For newlines, focus only on '\n', not on '\r' */
207 ptr = wcschr(lpText, L'\n'); // Find the end of this line.
208 if (ptr)
209 {
210 /* We have the end of this line, go to the next line (ignore the endline in the count) */
211 *lpNextLine = ptr + 1;
212 }
213 else
214 {
215 /* This line was the last one, go pointing to the terminating NULL */
216 ptr = lpText + wcslen(lpText);
217 *lpNextLine = ptr;
218 }
219
220 return (ptr - lpText);
221 }
222
223 SIZE_T
224 GetLineExtentA(
225 IN LPCSTR lpText,
226 OUT LPCSTR* lpNextLine)
227 {
228 LPCSTR ptr;
229
230 /* Find the next line of text (lpText is NULL-terminated) */
231 /* For newlines, focus only on '\n', not on '\r' */
232 ptr = strchr(lpText, '\n'); // Find the end of this line.
233 if (ptr)
234 {
235 /* We have the end of this line, go to the next line (ignore the endline in the count) */
236 *lpNextLine = ptr + 1;
237 }
238 else
239 {
240 /* This line was the last one, go pointing to the terminating NULL */
241 ptr = lpText + strlen(lpText);
242 *lpNextLine = ptr;
243 }
244
245 return (ptr - lpText);
246 }
247
248 BOOL GetClipboardDataDimensions(UINT uFormat, PRECT pRc)
249 {
250 SetRectEmpty(pRc);
251
252 if (!OpenClipboard(Globals.hMainWnd))
253 {
254 return FALSE;
255 }
256
257 switch (uFormat)
258 {
259 case CF_DSPBITMAP:
260 case CF_BITMAP:
261 {
262 HBITMAP hBitmap;
263 BITMAP bmp;
264
265 hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
266 GetObjectW(hBitmap, sizeof(bmp), &bmp);
267 SetRect(pRc, 0, 0, bmp.bmWidth, bmp.bmHeight);
268 break;
269 }
270
271 case CF_DIB:
272 case CF_DIBV5:
273 {
274 HGLOBAL hGlobal;
275 LPBITMAPINFOHEADER lpInfoHeader;
276
277 hGlobal = GetClipboardData(uFormat);
278 if (!hGlobal)
279 break;
280
281 lpInfoHeader = GlobalLock(hGlobal);
282 if (!lpInfoHeader)
283 break;
284
285 if (lpInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
286 {
287 LPBITMAPCOREHEADER lpCoreHeader = (LPBITMAPCOREHEADER)lpInfoHeader;
288 SetRect(pRc, 0, 0,
289 lpCoreHeader->bcWidth,
290 lpCoreHeader->bcHeight);
291 }
292 else if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) ||
293 (lpInfoHeader->biSize == sizeof(BITMAPV4HEADER)) ||
294 (lpInfoHeader->biSize == sizeof(BITMAPV5HEADER)))
295 {
296 SetRect(pRc, 0, 0,
297 lpInfoHeader->biWidth,
298 /* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */
299 (lpInfoHeader->biHeight > 0) ? lpInfoHeader->biHeight
300 : -lpInfoHeader->biHeight);
301 }
302 else
303 {
304 /* Invalid format */
305 }
306
307 GlobalUnlock(hGlobal);
308 break;
309 }
310
311 case CF_DSPTEXT:
312 case CF_TEXT:
313 case CF_OEMTEXT:
314 case CF_UNICODETEXT:
315 {
316 HDC hDC;
317 HGLOBAL hGlobal;
318 PVOID lpText, ptr;
319 DWORD dwSize;
320 SIZE txtSize = {0, 0};
321 SIZE_T lineSize;
322
323 hGlobal = GetClipboardData(uFormat);
324 if (!hGlobal)
325 break;
326
327 lpText = GlobalLock(hGlobal);
328 if (!lpText)
329 break;
330
331 hDC = GetDC(Globals.hMainWnd);
332
333 /* Find the size of the rectangle enclosing the text */
334 for (;;)
335 {
336 if (uFormat == CF_UNICODETEXT)
337 {
338 if (*(LPCWSTR)lpText == UNICODE_NULL)
339 break;
340 lineSize = GetLineExtentW(lpText, (LPCWSTR*)&ptr);
341 dwSize = GetTabbedTextExtentW(hDC, lpText, lineSize, 0, NULL);
342 }
343 else
344 {
345 if (*(LPCSTR)lpText == ANSI_NULL)
346 break;
347 lineSize = GetLineExtentA(lpText, (LPCSTR*)&ptr);
348 dwSize = GetTabbedTextExtentA(hDC, lpText, lineSize, 0, NULL);
349 }
350 txtSize.cx = max(txtSize.cx, LOWORD(dwSize));
351 txtSize.cy += HIWORD(dwSize);
352 lpText = ptr;
353 }
354
355 ReleaseDC(Globals.hMainWnd, hDC);
356
357 GlobalUnlock(hGlobal);
358
359 SetRect(pRc, 0, 0, txtSize.cx, txtSize.cy);
360 break;
361 }
362
363 default:
364 {
365 break;
366 }
367 }
368
369 CloseClipboard();
370
371 return TRUE;
372 }