[CLIPBRD] Improvements for the Clipboard Viewer.
[reactos.git] / base / applications / clipbrd / winutils.c
1 /*
2 * PROJECT: ReactOS Clipboard Viewer
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Display helper functions.
5 * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke
6 * Copyright 2015-2018 Hermes Belusca-Maito
7 */
8
9 #include "precomp.h"
10
11 void ShowLastWin32Error(HWND hwndParent)
12 {
13 DWORD dwError;
14 LPWSTR lpMsgBuf = NULL;
15
16 dwError = GetLastError();
17 if (dwError == ERROR_SUCCESS)
18 return;
19
20 if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
21 FORMAT_MESSAGE_FROM_SYSTEM |
22 FORMAT_MESSAGE_IGNORE_INSERTS,
23 NULL,
24 dwError,
25 LANG_USER_DEFAULT,
26 (LPWSTR)&lpMsgBuf,
27 0, NULL))
28 {
29 return;
30 }
31
32 MessageBoxW(hwndParent, lpMsgBuf, NULL, MB_OK | MB_ICONERROR);
33 LocalFree(lpMsgBuf);
34 }
35
36 void BringWindowToFront(HWND hWnd)
37 {
38 if (IsIconic(hWnd))
39 {
40 ShowWindow(hWnd, SW_RESTORE);
41 SetForegroundWindow(hWnd);
42 }
43 else
44 {
45 SetForegroundWindow(hWnd);
46 }
47 }
48
49 int MessageBoxRes(HWND hWnd, HINSTANCE hInstance, UINT uText, UINT uCaption, UINT uType)
50 {
51 MSGBOXPARAMSW mb;
52
53 ZeroMemory(&mb, sizeof(mb));
54 mb.cbSize = sizeof(mb);
55 mb.hwndOwner = hWnd;
56 mb.hInstance = hInstance;
57 mb.lpszText = MAKEINTRESOURCEW(uText);
58 mb.lpszCaption = MAKEINTRESOURCEW(uCaption);
59 mb.dwStyle = uType;
60 mb.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
61
62 return MessageBoxIndirectW(&mb);
63 }
64
65 void DrawTextFromResource(HINSTANCE hInstance, UINT uID, HDC hDC, LPRECT lpRect, UINT uFormat)
66 {
67 LPWSTR lpBuffer;
68 int nCount;
69
70 nCount = LoadStringW(hInstance, uID, (LPWSTR)&lpBuffer, 0);
71 if (nCount)
72 DrawTextW(hDC, lpBuffer, nCount, lpRect, uFormat);
73 }
74
75 void DrawTextFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state)
76 {
77 POINT ptOrg;
78 HGLOBAL hGlobal;
79 PVOID lpText, ptr;
80 SIZE_T lineSize;
81 INT FirstLine, LastLine;
82
83 hGlobal = GetClipboardData(uFormat);
84 if (!hGlobal)
85 return;
86
87 lpText = GlobalLock(hGlobal);
88 if (!lpText)
89 return;
90
91 /* Find the first and last line indices to display (Note that CurrentX/Y are in pixels!) */
92 FirstLine = max(0, (state.CurrentY + ps.rcPaint.top) / Globals.CharHeight);
93 // LastLine = min(LINES - 1, (state.CurrentY + ps.rcPaint.bottom) / Globals.CharHeight);
94 // NOTE: Can be less or greater than the actual number of lines in the text.
95 LastLine = (state.CurrentY + ps.rcPaint.bottom) / Globals.CharHeight;
96
97 /* Find the first text line to display */
98 while (FirstLine > 0)
99 {
100 if (uFormat == CF_UNICODETEXT)
101 {
102 if (*(LPCWSTR)lpText == UNICODE_NULL)
103 break;
104 GetLineExtentW(lpText, (LPCWSTR*)&ptr);
105 }
106 else
107 {
108 if (*(LPCSTR)lpText == ANSI_NULL)
109 break;
110 GetLineExtentA(lpText, (LPCSTR*)&ptr);
111 }
112
113 --FirstLine;
114 --LastLine;
115
116 lpText = ptr;
117 }
118
119 ptOrg.x = ps.rcPaint.left;
120 ptOrg.y = /* FirstLine */ max(0, (state.CurrentY + ps.rcPaint.top) / Globals.CharHeight)
121 * Globals.CharHeight - state.CurrentY;
122
123 /* Display each line from the current one up to the last one */
124 ++LastLine;
125 while (LastLine >= 0)
126 {
127 if (uFormat == CF_UNICODETEXT)
128 {
129 if (*(LPCWSTR)lpText == UNICODE_NULL)
130 break;
131 lineSize = GetLineExtentW(lpText, (LPCWSTR*)&ptr);
132 TabbedTextOutW(ps.hdc, /*ptOrg.x*/0 - state.CurrentX, ptOrg.y,
133 lpText, lineSize, 0, NULL,
134 /*ptOrg.x*/0 - state.CurrentX);
135 }
136 else
137 {
138 if (*(LPCSTR)lpText == ANSI_NULL)
139 break;
140 lineSize = GetLineExtentA(lpText, (LPCSTR*)&ptr);
141 TabbedTextOutA(ps.hdc, /*ptOrg.x*/0 - state.CurrentX, ptOrg.y,
142 lpText, lineSize, 0, NULL,
143 /*ptOrg.x*/0 - state.CurrentX);
144 }
145
146 --LastLine;
147
148 ptOrg.y += Globals.CharHeight;
149 lpText = ptr;
150 }
151
152 GlobalUnlock(hGlobal);
153 }
154
155 void BitBltFromClipboard(PAINTSTRUCT ps, SCROLLSTATE state, DWORD dwRop)
156 {
157 HDC hdcMem;
158 HBITMAP hBitmap;
159 BITMAP bmp;
160 LONG bmWidth, bmHeight;
161
162 hdcMem = CreateCompatibleDC(ps.hdc);
163 if (!hdcMem)
164 return;
165
166 hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
167 GetObjectW(hBitmap, sizeof(bmp), &bmp);
168
169 SelectObject(hdcMem, hBitmap);
170
171 bmWidth = min(ps.rcPaint.right - ps.rcPaint.left, bmp.bmWidth - ps.rcPaint.left - state.CurrentX);
172 bmHeight = min(ps.rcPaint.bottom - ps.rcPaint.top , bmp.bmHeight - ps.rcPaint.top - state.CurrentY);
173
174 BitBlt(ps.hdc,
175 ps.rcPaint.left,
176 ps.rcPaint.top,
177 bmWidth,
178 bmHeight,
179 hdcMem,
180 ps.rcPaint.left + state.CurrentX,
181 ps.rcPaint.top + state.CurrentY,
182 dwRop);
183
184 DeleteDC(hdcMem);
185 }
186
187 void SetDIBitsToDeviceFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state, UINT fuColorUse)
188 {
189 HGLOBAL hGlobal;
190 LPBITMAPINFOHEADER lpInfoHeader;
191 LPBYTE lpBits;
192 LONG bmWidth, bmHeight;
193 DWORD dwPalSize = 0;
194
195 hGlobal = GetClipboardData(uFormat);
196 if (!hGlobal)
197 return;
198
199 lpInfoHeader = GlobalLock(hGlobal);
200 if (!lpInfoHeader)
201 return;
202
203 if (lpInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
204 {
205 LPBITMAPCOREHEADER lpCoreHeader = (LPBITMAPCOREHEADER)lpInfoHeader;
206
207 dwPalSize = 0;
208
209 if (lpCoreHeader->bcBitCount <= 8)
210 {
211 dwPalSize = (1 << lpCoreHeader->bcBitCount);
212
213 if (fuColorUse == DIB_RGB_COLORS)
214 dwPalSize *= sizeof(RGBTRIPLE);
215 else
216 dwPalSize *= sizeof(WORD);
217 }
218
219 bmWidth = lpCoreHeader->bcWidth;
220 bmHeight = lpCoreHeader->bcHeight;
221 }
222 else if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) ||
223 (lpInfoHeader->biSize == sizeof(BITMAPV4HEADER)) ||
224 (lpInfoHeader->biSize == sizeof(BITMAPV5HEADER)))
225 {
226 dwPalSize = lpInfoHeader->biClrUsed;
227
228 if ((dwPalSize == 0) && (lpInfoHeader->biBitCount <= 8))
229 dwPalSize = (1 << lpInfoHeader->biBitCount);
230
231 if (fuColorUse == DIB_RGB_COLORS)
232 dwPalSize *= sizeof(RGBQUAD);
233 else
234 dwPalSize *= sizeof(WORD);
235
236 if (/*(lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) &&*/
237 (lpInfoHeader->biCompression == BI_BITFIELDS))
238 {
239 dwPalSize += 3 * sizeof(DWORD);
240 }
241
242 /*
243 * This is a (disabled) hack for Windows, when uFormat == CF_DIB
244 * it needs yet another extra 3 DWORDs, in addition to the
245 * ones already taken into account in via the compression.
246 * This problem doesn't happen when uFormat == CF_DIBV5
247 * (in that case, when compression is taken into account,
248 * everything is nice).
249 *
250 * NOTE 1: This fix is only for us, because when one pastes DIBs
251 * directly in apps, the bitmap offset problem is still present.
252 *
253 * NOTE 2: The problem can be seen with Windows' clipbrd.exe if
254 * one copies a CF_DIB image in the clipboard. By default Windows'
255 * clipbrd.exe works with CF_DIBV5 and CF_BITMAP, so the problem
256 * is unseen, and the clipboard doesn't have to convert to CF_DIB.
257 *
258 * FIXME: investigate!!
259 * ANSWER: this is a Windows bug; part of the answer is there:
260 * https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/ac7ab3b5-8609-4478-b86a-976dab44c271/bug-clipboard-format-conversions-cfdib-cfdibv5-cfdib
261 * May be related:
262 * https://blog.talosintelligence.com/2015/10/dangerous-clipboard.html
263 */
264 #if 0
265 if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) &&
266 (lpInfoHeader->biCompression == BI_BITFIELDS))
267 {
268 dwPalSize += 3 * sizeof(DWORD);
269 }
270 #endif
271
272 bmWidth = lpInfoHeader->biWidth;
273 /* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */
274 bmHeight = lpInfoHeader->biHeight;
275 }
276 else
277 {
278 /* Invalid format */
279 GlobalUnlock(hGlobal);
280 return;
281 }
282
283 lpBits = (LPBYTE)lpInfoHeader + lpInfoHeader->biSize + dwPalSize;
284
285 /*
286 * The seventh parameter (YSrc) of SetDIBitsToDevice always designates
287 * the Y-coordinate of the "lower-left corner" of the image, be the DIB
288 * in bottom-up or top-down mode.
289 */
290 SetDIBitsToDevice(ps.hdc,
291 -state.CurrentX, // ps.rcPaint.left,
292 -state.CurrentY, // ps.rcPaint.top,
293 bmWidth,
294 bmHeight,
295 0, // ps.rcPaint.left + state.CurrentX,
296 0, // -(ps.rcPaint.top + state.CurrentY),
297 0, // uStartScan,
298 bmHeight,
299 lpBits,
300 (LPBITMAPINFO)lpInfoHeader,
301 fuColorUse);
302
303 GlobalUnlock(hGlobal);
304 }
305
306 void PlayMetaFileFromClipboard(HDC hdc, const RECT *lpRect)
307 {
308 LPMETAFILEPICT mp;
309 HGLOBAL hGlobal;
310
311 hGlobal = GetClipboardData(CF_METAFILEPICT);
312 if (!hGlobal)
313 return;
314
315 mp = (LPMETAFILEPICT)GlobalLock(hGlobal);
316 if (!mp)
317 return;
318
319 SetMapMode(hdc, mp->mm);
320 SetViewportExtEx(hdc, lpRect->right, lpRect->bottom, NULL);
321 SetViewportOrgEx(hdc, lpRect->left, lpRect->top, NULL);
322 PlayMetaFile(hdc, mp->hMF);
323 GlobalUnlock(hGlobal);
324 }
325
326 void PlayEnhMetaFileFromClipboard(HDC hdc, const RECT *lpRect)
327 {
328 HENHMETAFILE hEmf;
329
330 hEmf = GetClipboardData(CF_ENHMETAFILE);
331 PlayEnhMetaFile(hdc, hEmf, lpRect);
332 }
333
334 BOOL RealizeClipboardPalette(HDC hdc)
335 {
336 BOOL Success;
337 HPALETTE hPalette, hOldPalette;
338
339 if (!IsClipboardFormatAvailable(CF_PALETTE))
340 return FALSE;
341
342 hPalette = GetClipboardData(CF_PALETTE);
343 if (!hPalette)
344 return FALSE;
345
346 hOldPalette = SelectPalette(hdc, hPalette, FALSE);
347 if (!hOldPalette)
348 return FALSE;
349
350 Success = (RealizePalette(hdc) != GDI_ERROR);
351
352 SelectPalette(hdc, hOldPalette, FALSE);
353
354 return Success;
355 }