mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 13:33:42 +00:00
ebe3d5273e
- Improve the scrolling support for bitmaps, DIBs and text formats. This completes the work started in CORE-10679 by Ricardo Hanke. Includes scrolling with the keyboard and the mouse wheel. - Add support for the CF_DSP* clipboard formats, as well as CF_TEXT and CF_OEMTEXT. - Add support for owner-display clipboard format CF_OWNERDISPLAY. - Realize any palette found in the clipboard (CF_PALETTE) before displaying the clipboard data format we want. - Remove dead code. - Update the file headers.
355 lines
10 KiB
C
355 lines
10 KiB
C
/*
|
|
* PROJECT: ReactOS Clipboard Viewer
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Display helper functions.
|
|
* COPYRIGHT: Copyright 2015-2018 Ricardo Hanke
|
|
* Copyright 2015-2018 Hermes Belusca-Maito
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
void ShowLastWin32Error(HWND hwndParent)
|
|
{
|
|
DWORD dwError;
|
|
LPWSTR lpMsgBuf = NULL;
|
|
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_SUCCESS)
|
|
return;
|
|
|
|
if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
dwError,
|
|
LANG_USER_DEFAULT,
|
|
(LPWSTR)&lpMsgBuf,
|
|
0, NULL))
|
|
{
|
|
return;
|
|
}
|
|
|
|
MessageBoxW(hwndParent, lpMsgBuf, NULL, MB_OK | MB_ICONERROR);
|
|
LocalFree(lpMsgBuf);
|
|
}
|
|
|
|
void BringWindowToFront(HWND hWnd)
|
|
{
|
|
if (IsIconic(hWnd))
|
|
{
|
|
ShowWindow(hWnd, SW_RESTORE);
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
else
|
|
{
|
|
SetForegroundWindow(hWnd);
|
|
}
|
|
}
|
|
|
|
int MessageBoxRes(HWND hWnd, HINSTANCE hInstance, UINT uText, UINT uCaption, UINT uType)
|
|
{
|
|
MSGBOXPARAMSW mb;
|
|
|
|
ZeroMemory(&mb, sizeof(mb));
|
|
mb.cbSize = sizeof(mb);
|
|
mb.hwndOwner = hWnd;
|
|
mb.hInstance = hInstance;
|
|
mb.lpszText = MAKEINTRESOURCEW(uText);
|
|
mb.lpszCaption = MAKEINTRESOURCEW(uCaption);
|
|
mb.dwStyle = uType;
|
|
mb.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
|
|
|
|
return MessageBoxIndirectW(&mb);
|
|
}
|
|
|
|
void DrawTextFromResource(HINSTANCE hInstance, UINT uID, HDC hDC, LPRECT lpRect, UINT uFormat)
|
|
{
|
|
LPWSTR lpBuffer;
|
|
int nCount;
|
|
|
|
nCount = LoadStringW(hInstance, uID, (LPWSTR)&lpBuffer, 0);
|
|
if (nCount)
|
|
DrawTextW(hDC, lpBuffer, nCount, lpRect, uFormat);
|
|
}
|
|
|
|
void DrawTextFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state)
|
|
{
|
|
POINT ptOrg;
|
|
HGLOBAL hGlobal;
|
|
PVOID lpText, ptr;
|
|
SIZE_T lineSize;
|
|
INT FirstLine, LastLine;
|
|
|
|
hGlobal = GetClipboardData(uFormat);
|
|
if (!hGlobal)
|
|
return;
|
|
|
|
lpText = GlobalLock(hGlobal);
|
|
if (!lpText)
|
|
return;
|
|
|
|
/* Find the first and last line indices to display (Note that CurrentX/Y are in pixels!) */
|
|
FirstLine = max(0, (state.CurrentY + ps.rcPaint.top) / Globals.CharHeight);
|
|
// LastLine = min(LINES - 1, (state.CurrentY + ps.rcPaint.bottom) / Globals.CharHeight);
|
|
// NOTE: Can be less or greater than the actual number of lines in the text.
|
|
LastLine = (state.CurrentY + ps.rcPaint.bottom) / Globals.CharHeight;
|
|
|
|
/* Find the first text line to display */
|
|
while (FirstLine > 0)
|
|
{
|
|
if (uFormat == CF_UNICODETEXT)
|
|
{
|
|
if (*(LPCWSTR)lpText == UNICODE_NULL)
|
|
break;
|
|
GetLineExtentW(lpText, (LPCWSTR*)&ptr);
|
|
}
|
|
else
|
|
{
|
|
if (*(LPCSTR)lpText == ANSI_NULL)
|
|
break;
|
|
GetLineExtentA(lpText, (LPCSTR*)&ptr);
|
|
}
|
|
|
|
--FirstLine;
|
|
--LastLine;
|
|
|
|
lpText = ptr;
|
|
}
|
|
|
|
ptOrg.x = ps.rcPaint.left;
|
|
ptOrg.y = /* FirstLine */ max(0, (state.CurrentY + ps.rcPaint.top) / Globals.CharHeight)
|
|
* Globals.CharHeight - state.CurrentY;
|
|
|
|
/* Display each line from the current one up to the last one */
|
|
++LastLine;
|
|
while (LastLine >= 0)
|
|
{
|
|
if (uFormat == CF_UNICODETEXT)
|
|
{
|
|
if (*(LPCWSTR)lpText == UNICODE_NULL)
|
|
break;
|
|
lineSize = GetLineExtentW(lpText, (LPCWSTR*)&ptr);
|
|
TabbedTextOutW(ps.hdc, /*ptOrg.x*/0 - state.CurrentX, ptOrg.y,
|
|
lpText, lineSize, 0, NULL,
|
|
/*ptOrg.x*/0 - state.CurrentX);
|
|
}
|
|
else
|
|
{
|
|
if (*(LPCSTR)lpText == ANSI_NULL)
|
|
break;
|
|
lineSize = GetLineExtentA(lpText, (LPCSTR*)&ptr);
|
|
TabbedTextOutA(ps.hdc, /*ptOrg.x*/0 - state.CurrentX, ptOrg.y,
|
|
lpText, lineSize, 0, NULL,
|
|
/*ptOrg.x*/0 - state.CurrentX);
|
|
}
|
|
|
|
--LastLine;
|
|
|
|
ptOrg.y += Globals.CharHeight;
|
|
lpText = ptr;
|
|
}
|
|
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
|
|
void BitBltFromClipboard(PAINTSTRUCT ps, SCROLLSTATE state, DWORD dwRop)
|
|
{
|
|
HDC hdcMem;
|
|
HBITMAP hBitmap;
|
|
BITMAP bmp;
|
|
LONG bmWidth, bmHeight;
|
|
|
|
hdcMem = CreateCompatibleDC(ps.hdc);
|
|
if (!hdcMem)
|
|
return;
|
|
|
|
hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
|
|
GetObjectW(hBitmap, sizeof(bmp), &bmp);
|
|
|
|
SelectObject(hdcMem, hBitmap);
|
|
|
|
bmWidth = min(ps.rcPaint.right - ps.rcPaint.left, bmp.bmWidth - ps.rcPaint.left - state.CurrentX);
|
|
bmHeight = min(ps.rcPaint.bottom - ps.rcPaint.top , bmp.bmHeight - ps.rcPaint.top - state.CurrentY);
|
|
|
|
BitBlt(ps.hdc,
|
|
ps.rcPaint.left,
|
|
ps.rcPaint.top,
|
|
bmWidth,
|
|
bmHeight,
|
|
hdcMem,
|
|
ps.rcPaint.left + state.CurrentX,
|
|
ps.rcPaint.top + state.CurrentY,
|
|
dwRop);
|
|
|
|
DeleteDC(hdcMem);
|
|
}
|
|
|
|
void SetDIBitsToDeviceFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state, UINT fuColorUse)
|
|
{
|
|
HGLOBAL hGlobal;
|
|
LPBITMAPINFOHEADER lpInfoHeader;
|
|
LPBYTE lpBits;
|
|
LONG bmWidth, bmHeight;
|
|
DWORD dwPalSize = 0;
|
|
|
|
hGlobal = GetClipboardData(uFormat);
|
|
if (!hGlobal)
|
|
return;
|
|
|
|
lpInfoHeader = GlobalLock(hGlobal);
|
|
if (!lpInfoHeader)
|
|
return;
|
|
|
|
if (lpInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
|
|
{
|
|
LPBITMAPCOREHEADER lpCoreHeader = (LPBITMAPCOREHEADER)lpInfoHeader;
|
|
|
|
dwPalSize = 0;
|
|
|
|
if (lpCoreHeader->bcBitCount <= 8)
|
|
{
|
|
dwPalSize = (1 << lpCoreHeader->bcBitCount);
|
|
|
|
if (fuColorUse == DIB_RGB_COLORS)
|
|
dwPalSize *= sizeof(RGBTRIPLE);
|
|
else
|
|
dwPalSize *= sizeof(WORD);
|
|
}
|
|
|
|
bmWidth = lpCoreHeader->bcWidth;
|
|
bmHeight = lpCoreHeader->bcHeight;
|
|
}
|
|
else if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) ||
|
|
(lpInfoHeader->biSize == sizeof(BITMAPV4HEADER)) ||
|
|
(lpInfoHeader->biSize == sizeof(BITMAPV5HEADER)))
|
|
{
|
|
dwPalSize = lpInfoHeader->biClrUsed;
|
|
|
|
if ((dwPalSize == 0) && (lpInfoHeader->biBitCount <= 8))
|
|
dwPalSize = (1 << lpInfoHeader->biBitCount);
|
|
|
|
if (fuColorUse == DIB_RGB_COLORS)
|
|
dwPalSize *= sizeof(RGBQUAD);
|
|
else
|
|
dwPalSize *= sizeof(WORD);
|
|
|
|
if (/*(lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) &&*/
|
|
(lpInfoHeader->biCompression == BI_BITFIELDS))
|
|
{
|
|
dwPalSize += 3 * sizeof(DWORD);
|
|
}
|
|
|
|
/*
|
|
* This is a (disabled) hack for Windows, when uFormat == CF_DIB
|
|
* it needs yet another extra 3 DWORDs, in addition to the
|
|
* ones already taken into account in via the compression.
|
|
* This problem doesn't happen when uFormat == CF_DIBV5
|
|
* (in that case, when compression is taken into account,
|
|
* everything is nice).
|
|
*
|
|
* NOTE 1: This fix is only for us, because when one pastes DIBs
|
|
* directly in apps, the bitmap offset problem is still present.
|
|
*
|
|
* NOTE 2: The problem can be seen with Windows' clipbrd.exe if
|
|
* one copies a CF_DIB image in the clipboard. By default Windows'
|
|
* clipbrd.exe works with CF_DIBV5 and CF_BITMAP, so the problem
|
|
* is unseen, and the clipboard doesn't have to convert to CF_DIB.
|
|
*
|
|
* FIXME: investigate!!
|
|
* ANSWER: this is a Windows bug; part of the answer is there:
|
|
* https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/ac7ab3b5-8609-4478-b86a-976dab44c271/bug-clipboard-format-conversions-cfdib-cfdibv5-cfdib
|
|
* May be related:
|
|
* https://blog.talosintelligence.com/2015/10/dangerous-clipboard.html
|
|
*/
|
|
#if 0
|
|
if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) &&
|
|
(lpInfoHeader->biCompression == BI_BITFIELDS))
|
|
{
|
|
dwPalSize += 3 * sizeof(DWORD);
|
|
}
|
|
#endif
|
|
|
|
bmWidth = lpInfoHeader->biWidth;
|
|
/* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */
|
|
bmHeight = lpInfoHeader->biHeight;
|
|
}
|
|
else
|
|
{
|
|
/* Invalid format */
|
|
GlobalUnlock(hGlobal);
|
|
return;
|
|
}
|
|
|
|
lpBits = (LPBYTE)lpInfoHeader + lpInfoHeader->biSize + dwPalSize;
|
|
|
|
/*
|
|
* The seventh parameter (YSrc) of SetDIBitsToDevice always designates
|
|
* the Y-coordinate of the "lower-left corner" of the image, be the DIB
|
|
* in bottom-up or top-down mode.
|
|
*/
|
|
SetDIBitsToDevice(ps.hdc,
|
|
-state.CurrentX, // ps.rcPaint.left,
|
|
-state.CurrentY, // ps.rcPaint.top,
|
|
bmWidth,
|
|
bmHeight,
|
|
0, // ps.rcPaint.left + state.CurrentX,
|
|
0, // -(ps.rcPaint.top + state.CurrentY),
|
|
0, // uStartScan,
|
|
bmHeight,
|
|
lpBits,
|
|
(LPBITMAPINFO)lpInfoHeader,
|
|
fuColorUse);
|
|
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
|
|
void PlayMetaFileFromClipboard(HDC hdc, const RECT *lpRect)
|
|
{
|
|
LPMETAFILEPICT mp;
|
|
HGLOBAL hGlobal;
|
|
|
|
hGlobal = GetClipboardData(CF_METAFILEPICT);
|
|
if (!hGlobal)
|
|
return;
|
|
|
|
mp = (LPMETAFILEPICT)GlobalLock(hGlobal);
|
|
if (!mp)
|
|
return;
|
|
|
|
SetMapMode(hdc, mp->mm);
|
|
SetViewportExtEx(hdc, lpRect->right, lpRect->bottom, NULL);
|
|
SetViewportOrgEx(hdc, lpRect->left, lpRect->top, NULL);
|
|
PlayMetaFile(hdc, mp->hMF);
|
|
GlobalUnlock(hGlobal);
|
|
}
|
|
|
|
void PlayEnhMetaFileFromClipboard(HDC hdc, const RECT *lpRect)
|
|
{
|
|
HENHMETAFILE hEmf;
|
|
|
|
hEmf = GetClipboardData(CF_ENHMETAFILE);
|
|
PlayEnhMetaFile(hdc, hEmf, lpRect);
|
|
}
|
|
|
|
BOOL RealizeClipboardPalette(HDC hdc)
|
|
{
|
|
BOOL Success;
|
|
HPALETTE hPalette, hOldPalette;
|
|
|
|
if (!IsClipboardFormatAvailable(CF_PALETTE))
|
|
return FALSE;
|
|
|
|
hPalette = GetClipboardData(CF_PALETTE);
|
|
if (!hPalette)
|
|
return FALSE;
|
|
|
|
hOldPalette = SelectPalette(hdc, hPalette, FALSE);
|
|
if (!hOldPalette)
|
|
return FALSE;
|
|
|
|
Success = (RealizePalette(hdc) != GDI_ERROR);
|
|
|
|
SelectPalette(hdc, hOldPalette, FALSE);
|
|
|
|
return Success;
|
|
}
|