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 static LPWSTR AllocStrCat(LPWSTR psz, LPCWSTR cat)
335 {
336     INT cch;
337     LPWSTR pszNew;
338 
339     if (psz == NULL)
340         return _wcsdup(cat);
341 
342     cch = lstrlenW(psz) + lstrlenW(cat) + 1;
343     pszNew = realloc(psz, cch * sizeof(WCHAR));
344     if (!pszNew)
345         return psz;
346 
347     lstrcatW(pszNew, cat);
348     return pszNew;
349 }
350 
351 void HDropFromClipboard(HDC hdc, const RECT *lpRect)
352 {
353     LPWSTR pszAlloc = NULL;
354     WCHAR szFile[MAX_PATH + 2];
355     HDROP hDrop = (HDROP)GetClipboardData(CF_HDROP);
356     UINT iFile, cFiles = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);
357     RECT rc = *lpRect;
358 
359     FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
360 
361     for (iFile = 0; iFile < cFiles; ++iFile)
362     {
363         DragQueryFileW(hDrop, iFile, szFile, _countof(szFile));
364         lstrcatW(szFile, L"\r\n");
365         pszAlloc = AllocStrCat(pszAlloc, szFile);
366     }
367 
368     DrawTextW(hdc, pszAlloc, -1, &rc,
369               DT_LEFT | DT_NOPREFIX | DT_EXTERNALLEADING | DT_WORD_ELLIPSIS);
370     free(pszAlloc);
371 }
372 
373 BOOL RealizeClipboardPalette(HDC hdc)
374 {
375     BOOL Success;
376     HPALETTE hPalette, hOldPalette;
377 
378     if (!IsClipboardFormatAvailable(CF_PALETTE))
379         return FALSE;
380 
381     hPalette = GetClipboardData(CF_PALETTE);
382     if (!hPalette)
383         return FALSE;
384 
385     hOldPalette = SelectPalette(hdc, hPalette, FALSE);
386     if (!hOldPalette)
387         return FALSE;
388 
389     Success = (RealizePalette(hdc) != GDI_ERROR);
390 
391     SelectPalette(hdc, hOldPalette, FALSE);
392 
393     return Success;
394 }
395