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