From: Hermès Bélusca-Maïto Date: Sun, 13 May 2018 20:15:41 +0000 (+0200) Subject: [CLIPBRD] Improvements for the Clipboard Viewer. X-Git-Tag: 0.4.9-RC~46 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=ebe3d5273e47f382f44657ebf29c7632b23bbe5e [CLIPBRD] Improvements for the Clipboard Viewer. - 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. --- diff --git a/base/applications/clipbrd/clipbrd.c b/base/applications/clipbrd/clipbrd.c index f46daeb6e40..280cc6f78ea 100644 --- a/base/applications/clipbrd/clipbrd.c +++ b/base/applications/clipbrd/clipbrd.c @@ -1,9 +1,9 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/clipbrd.c - * PURPOSE: Provides a view of the contents of the ReactOS clipboard. - * PROGRAMMERS: Ricardo Hanke + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Provides a view of the contents of the ReactOS clipboard. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ #include "precomp.h" @@ -13,26 +13,12 @@ static const WCHAR szClassName[] = L"ClipBookWClass"; CLIPBOARD_GLOBALS Globals; SCROLLSTATE Scrollstate; -static void UpdateLinesToScroll(void) -{ - UINT uLinesToScroll; - - if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uLinesToScroll, 0)) - { - Globals.uLinesToScroll = 3; - } - else - { - Globals.uLinesToScroll = uLinesToScroll; - } -} - static void SaveClipboardToFile(void) { OPENFILENAMEW sfn; + LPWSTR c; WCHAR szFileName[MAX_PATH]; WCHAR szFilterMask[MAX_STRING_LEN + 10]; - LPWSTR c; ZeroMemory(&szFilterMask, sizeof(szFilterMask)); c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_NT, szFilterMask, MAX_STRING_LEN) + 1; @@ -87,9 +73,9 @@ static void LoadClipboardDataFromFile(LPWSTR lpszFileName) static void LoadClipboardFromFile(void) { OPENFILENAMEW ofn; + LPWSTR c; WCHAR szFileName[MAX_PATH]; WCHAR szFilterMask[MAX_STRING_LEN + 10]; - LPWSTR c; ZeroMemory(&szFilterMask, sizeof(szFilterMask)); c = szFilterMask + LoadStringW(Globals.hInstance, STRING_FORMAT_GEN, szFilterMask, MAX_STRING_LEN) + 1; @@ -123,6 +109,8 @@ static void LoadClipboardFromDrop(HDROP hDrop) static void SetDisplayFormat(UINT uFormat) { + RECT rc; + CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_UNCHECKED); Globals.uCheckedItem = uFormat + CMD_AUTOMATIC; CheckMenuItem(Globals.hMenu, Globals.uCheckedItem, MF_BYCOMMAND | MF_CHECKED); @@ -136,13 +124,10 @@ static void SetDisplayFormat(UINT uFormat) Globals.uDisplayFormat = uFormat; } - if (Globals.hDspBmp) - { - DeleteObject(Globals.hDspBmp); - } - - ZeroMemory(&Scrollstate, sizeof(Scrollstate)); - UpdateWindowScrollState(Globals.hMainWnd, Globals.hDspBmp, &Scrollstate); + GetClipboardDataDimensions(Globals.uDisplayFormat, &rc); + Scrollstate.CurrentX = Scrollstate.CurrentY = 0; + Scrollstate.iWheelCarryoverX = Scrollstate.iWheelCarryoverY = 0; + UpdateWindowScrollState(Globals.hMainWnd, rc.right, rc.bottom, &Scrollstate); InvalidateRect(Globals.hMainWnd, NULL, TRUE); } @@ -169,8 +154,8 @@ static void InitMenuPopup(HMENU hMenu, LPARAM index) static void UpdateDisplayMenu(void) { UINT uFormat; - WCHAR szFormatName[MAX_FMT_NAME_LEN + 1]; HMENU hMenu; + WCHAR szFormatName[MAX_FMT_NAME_LEN + 1]; hMenu = GetSubMenu(Globals.hMenu, DISPLAY_MENU_POS); @@ -187,27 +172,34 @@ static void UpdateDisplayMenu(void) AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL); - uFormat = EnumClipboardFormats(0); - while (uFormat) + /* Display the supported clipboard formats first */ + for (uFormat = EnumClipboardFormats(0); uFormat; + uFormat = EnumClipboardFormats(uFormat)) { - RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, szFormatName, ARRAYSIZE(szFormatName)); + if (IsClipboardFormatSupported(uFormat)) + { + RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, + szFormatName, ARRAYSIZE(szFormatName)); + AppendMenuW(hMenu, MF_STRING, CMD_AUTOMATIC + uFormat, szFormatName); + } + } + /* Now display the unsupported clipboard formats */ + for (uFormat = EnumClipboardFormats(0); uFormat; + uFormat = EnumClipboardFormats(uFormat)) + { if (!IsClipboardFormatSupported(uFormat)) { + RetrieveClipboardFormatName(Globals.hInstance, uFormat, TRUE, + szFormatName, ARRAYSIZE(szFormatName)); AppendMenuW(hMenu, MF_STRING | MF_GRAYED, 0, szFormatName); } - else - { - AppendMenuW(hMenu, MF_STRING, CMD_AUTOMATIC + uFormat, szFormatName); - } - - uFormat = EnumClipboardFormats(uFormat); } CloseClipboard(); } -static int ClipboardCommandHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +static int OnCommand(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (LOWORD(wParam)) { @@ -274,68 +266,113 @@ static int ClipboardCommandHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return 0; } -static void ClipboardPaintHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +static void OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; + COLORREF crOldBkColor, crOldTextColor; RECT rc; if (!OpenClipboard(Globals.hMainWnd)) return; hdc = BeginPaint(hWnd, &ps); - GetClientRect(hWnd, &rc); + + /* Erase the background if needed */ + if (ps.fErase) + FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1)); + + /* Set the correct background and text colors */ + crOldBkColor = SetBkColor(ps.hdc, GetSysColor(COLOR_WINDOW)); + crOldTextColor = SetTextColor(ps.hdc, GetSysColor(COLOR_WINDOWTEXT)); + + /* Realize the clipboard palette if there is one */ + RealizeClipboardPalette(ps.hdc); switch (Globals.uDisplayFormat) { case CF_NONE: { + /* The clipboard is empty */ break; } + case CF_DSPTEXT: + case CF_TEXT: + case CF_OEMTEXT: case CF_UNICODETEXT: { - DrawTextFromClipboard(hdc, &rc, DT_LEFT | DT_NOPREFIX); + DrawTextFromClipboard(Globals.uDisplayFormat, ps, Scrollstate); break; } + case CF_DSPBITMAP: case CF_BITMAP: { - BitBltFromClipboard(hdc, rc.left, rc.top, rc.right, rc.bottom, 0, 0, SRCCOPY); + BitBltFromClipboard(ps, Scrollstate, SRCCOPY); break; } case CF_DIB: - { - SetDIBitsToDeviceFromClipboard(CF_DIB, hdc, rc.left, rc.top, 0, 0, 0, DIB_RGB_COLORS); - break; - } - case CF_DIBV5: { - SetDIBitsToDeviceFromClipboard(CF_DIBV5, hdc, rc.left, rc.top, 0, 0, 0, DIB_RGB_COLORS); + SetDIBitsToDeviceFromClipboard(Globals.uDisplayFormat, ps, Scrollstate, DIB_RGB_COLORS); break; } + case CF_DSPMETAFILEPICT: case CF_METAFILEPICT: { + GetClientRect(hWnd, &rc); PlayMetaFileFromClipboard(hdc, &rc); break; } + case CF_DSPENHMETAFILE: case CF_ENHMETAFILE: { + GetClientRect(hWnd, &rc); PlayEnhMetaFileFromClipboard(hdc, &rc); break; } + // case CF_PALETTE: + // TODO: Draw a palette with squares filled with colors. + // break; + + case CF_OWNERDISPLAY: + { + HGLOBAL hglb; + PPAINTSTRUCT pps; + + hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(ps)); + if (hglb) + { + pps = GlobalLock(hglb); + CopyMemory(pps, &ps, sizeof(ps)); + GlobalUnlock(hglb); + + SendClipboardOwnerMessage(TRUE, WM_PAINTCLIPBOARD, + (WPARAM)hWnd, (LPARAM)hglb); + + GlobalFree(hglb); + } + break; + } + default: { - DrawTextFromResource(Globals.hInstance, ERROR_UNSUPPORTED_FORMAT, hdc, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX); + GetClientRect(hWnd, &rc); + DrawTextFromResource(Globals.hInstance, ERROR_UNSUPPORTED_FORMAT, + hdc, &rc, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX); break; } } + /* Restore the original colors */ + SetTextColor(ps.hdc, crOldTextColor); + SetBkColor(ps.hdc, crOldBkColor); + EndPaint(hWnd, &ps); CloseClipboard(); @@ -347,12 +384,29 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP { case WM_CREATE: { + TEXTMETRICW tm; + HDC hDC = GetDC(hWnd); + + /* + * Note that the method with GetObjectW just returns + * the original parameters with which the font was created. + */ + if (GetTextMetricsW(hDC, &tm)) + { + Globals.CharWidth = tm.tmMaxCharWidth; // tm.tmAveCharWidth; + Globals.CharHeight = tm.tmHeight + tm.tmExternalLeading; + } + ReleaseDC(hWnd, hDC); + + Globals.hMenu = GetMenu(hWnd); Globals.hWndNext = SetClipboardViewer(hWnd); - + // For now, the Help dialog item is disabled because of lacking of HTML support EnableMenuItem(Globals.hMenu, CMD_HELP, MF_BYCOMMAND | MF_GRAYED); - + + UpdateLinesToScroll(&Scrollstate); + UpdateDisplayMenu(); SetDisplayFormat(0); break; @@ -367,61 +421,114 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP case WM_DESTROY: { ChangeClipboardChain(hWnd, Globals.hWndNext); + + if (Globals.uDisplayFormat == CF_OWNERDISPLAY) + { + HGLOBAL hglb; + PRECT prc; + + hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc)); + if (hglb) + { + prc = GlobalLock(hglb); + SetRectEmpty(prc); + GlobalUnlock(hglb); + + SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD, + (WPARAM)hWnd, (LPARAM)hglb); + + GlobalFree(hglb); + } + } + PostQuitMessage(0); break; } case WM_PAINT: { - ClipboardPaintHandler(hWnd, uMsg, wParam, lParam); + OnPaint(hWnd, wParam, lParam); break; } case WM_KEYDOWN: { - HandleKeyboardScrollEvents(hWnd, uMsg, wParam, lParam); + OnKeyScroll(hWnd, wParam, lParam, &Scrollstate); break; } case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: { - HandleMouseScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); + OnMouseScroll(hWnd, uMsg, wParam, lParam, &Scrollstate); break; } case WM_HSCROLL: { - HandleHorizontalScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); + // NOTE: Windows uses an offset of 16 pixels + OnScroll(hWnd, SB_HORZ, wParam, 5, &Scrollstate); break; } case WM_VSCROLL: { - HandleVerticalScrollEvents(hWnd, uMsg, wParam, lParam, &Scrollstate); + // NOTE: Windows uses an offset of 16 pixels + OnScroll(hWnd, SB_VERT, wParam, 5, &Scrollstate); break; } case WM_SIZE: { - UpdateWindowScrollState(hWnd, Globals.hDspBmp, &Scrollstate); + RECT rc; - if ((Globals.uDisplayFormat == CF_METAFILEPICT) || - (Globals.uDisplayFormat == CF_ENHMETAFILE) || - (Globals.uDisplayFormat == CF_DSPENHMETAFILE) || - (Globals.uDisplayFormat == CF_DSPMETAFILEPICT)) + if (Globals.uDisplayFormat == CF_OWNERDISPLAY) { - InvalidateRect(Globals.hMainWnd, NULL, FALSE); + HGLOBAL hglb; + PRECT prc; + + hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(*prc)); + if (hglb) + { + prc = GlobalLock(hglb); + if (wParam == SIZE_MINIMIZED) + SetRectEmpty(prc); + else + GetClientRect(hWnd, prc); + GlobalUnlock(hglb); + + SendClipboardOwnerMessage(TRUE, WM_SIZECLIPBOARD, + (WPARAM)hWnd, (LPARAM)hglb); + + GlobalFree(hglb); + } + break; } - else if (!IsClipboardFormatSupported(Globals.uDisplayFormat)) + + GetClipboardDataDimensions(Globals.uDisplayFormat, &rc); + UpdateWindowScrollState(hWnd, rc.right, rc.bottom, &Scrollstate); + + // NOTE: There still are little problems drawing + // the background when displaying clipboard text. + if (!IsClipboardFormatSupported(Globals.uDisplayFormat) || + Globals.uDisplayFormat == CF_DSPTEXT || + Globals.uDisplayFormat == CF_TEXT || + Globals.uDisplayFormat == CF_OEMTEXT || + Globals.uDisplayFormat == CF_UNICODETEXT) { InvalidateRect(Globals.hMainWnd, NULL, TRUE); } + else + { + InvalidateRect(Globals.hMainWnd, NULL, FALSE); + } break; } case WM_CHANGECBCHAIN: { + /* Transmit through the clipboard viewer chain */ if ((HWND)wParam == Globals.hWndNext) { Globals.hWndNext = (HWND)lParam; @@ -434,11 +541,32 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP break; } + case WM_DESTROYCLIPBOARD: + break; + + case WM_RENDERALLFORMATS: + { + /* + * When the user has cleared the clipboard via the DELETE command, + * we (clipboard viewer) become the clipboard owner. When we are + * subsequently closed, this message is then sent to us so that + * we get a chance to render everything we can. Since we don't have + * anything to render, just empty the clipboard. + */ + DeleteClipboardContent(); + break; + } + + case WM_RENDERFORMAT: + // TODO! + break; + case WM_DRAWCLIPBOARD: { UpdateDisplayMenu(); SetDisplayFormat(0); + /* Pass the message to the next window in clipboard viewer chain */ SendMessageW(Globals.hWndNext, uMsg, wParam, lParam); break; } @@ -451,7 +579,7 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP } else { - ClipboardCommandHandler(hWnd, uMsg, wParam, lParam); + OnCommand(hWnd, uMsg, wParam, lParam); } break; } @@ -468,9 +596,36 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP break; } + case WM_PALETTECHANGED: + { + /* Ignore if this comes from ourselves */ + if ((HWND)wParam == hWnd) + break; + + /* Fall back to WM_QUERYNEWPALETTE */ + } + case WM_QUERYNEWPALETTE: { - if (RealizeClipboardPalette(hWnd) != GDI_ERROR) + BOOL Success; + HDC hDC; + + if (!OpenClipboard(Globals.hMainWnd)) + return FALSE; + + hDC = GetDC(hWnd); + if (!hDC) + { + CloseClipboard(); + return FALSE; + } + + Success = RealizeClipboardPalette(hDC); + + ReleaseDC(hWnd, hDC); + CloseClipboard(); + + if (Success) { InvalidateRect(hWnd, NULL, TRUE); UpdateWindow(hWnd); @@ -479,19 +634,6 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP return FALSE; } - case WM_PALETTECHANGED: - { - if ((HWND)wParam != hWnd) - { - if (RealizeClipboardPalette(hWnd) != GDI_ERROR) - { - InvalidateRect(hWnd, NULL, TRUE); - UpdateWindow(hWnd); - } - } - break; - } - case WM_SYSCOLORCHANGE: { SetDisplayFormat(Globals.uDisplayFormat); @@ -502,7 +644,7 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP { if (wParam == SPI_SETWHEELSCROLLLINES) { - UpdateLinesToScroll(); + UpdateLinesToScroll(&Scrollstate); } break; } @@ -512,6 +654,7 @@ static LRESULT WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP return DefWindowProc(hWnd, uMsg, wParam, lParam); } } + return 0; } @@ -530,6 +673,16 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi return 0; } + switch (GetUserDefaultUILanguage()) + { + case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): + SetProcessDefaultLayout(LAYOUT_RTL); + break; + + default: + break; + } + ZeroMemory(&Globals, sizeof(Globals)); Globals.hInstance = hInstance; @@ -542,16 +695,6 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndclass.lpszMenuName = MAKEINTRESOURCEW(MAIN_MENU); wndclass.lpszClassName = szClassName; - - switch (GetUserDefaultUILanguage()) - { - case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): - SetProcessDefaultLayout(LAYOUT_RTL); - break; - - default: - break; - } if (!RegisterClassExW(&wndclass)) { @@ -559,6 +702,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi return 0; } + ZeroMemory(&Scrollstate, sizeof(Scrollstate)); + LoadStringW(hInstance, STRING_CLIPBOARD, szBuffer, ARRAYSIZE(szBuffer)); Globals.hMainWnd = CreateWindowExW(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES, szClassName, @@ -591,8 +736,6 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi if (lpCmdLine != NULL && *lpCmdLine) LoadClipboardDataFromFile(lpCmdLine); - UpdateLinesToScroll(); - while (GetMessageW(&msg, 0, 0, 0)) { if (!TranslateAcceleratorW(Globals.hMainWnd, hAccel, &msg)) @@ -602,10 +745,5 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi } } - if (Globals.hDspBmp) - { - DeleteObject(Globals.hDspBmp); - } - return (int)msg.wParam; } diff --git a/base/applications/clipbrd/clipbrd.rc b/base/applications/clipbrd/clipbrd.rc index 5f3e293bc49..178c6ce697a 100644 --- a/base/applications/clipbrd/clipbrd.rc +++ b/base/applications/clipbrd/clipbrd.rc @@ -1,11 +1,18 @@ +/* + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Resources file. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + */ + #include #include #include "resources.h" -#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Clipboard Viewer" -#define REACTOS_STR_INTERNAL_NAME "clipbrd" -#define REACTOS_STR_ORIGINAL_FILENAME "clipbrd.exe" +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Clipboard Viewer" +#define REACTOS_STR_INTERNAL_NAME "clipbrd" +#define REACTOS_STR_ORIGINAL_FILENAME "clipbrd.exe" #include CLIPBRD_ICON ICON "res/clipbrd.ico" diff --git a/base/applications/clipbrd/cliputils.c b/base/applications/clipbrd/cliputils.c index e13348a9ebe..daee8439a8f 100644 --- a/base/applications/clipbrd/cliputils.c +++ b/base/applications/clipbrd/cliputils.c @@ -1,13 +1,32 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/cliputils.c - * PURPOSE: Clipboard helper functions. - * PROGRAMMERS: Ricardo Hanke + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Clipboard helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ #include "precomp.h" +LRESULT +SendClipboardOwnerMessage( + IN BOOL bUnicode, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + HWND hwndOwner; + + hwndOwner = GetClipboardOwner(); + if (!hwndOwner) + return GetLastError(); + + if (bUnicode) + return SendMessageW(hwndOwner, uMsg, wParam, lParam); + else + return SendMessageA(hwndOwner, uMsg, wParam, lParam); +} + static int GetPredefinedClipboardFormatName(HINSTANCE hInstance, UINT uFormat, @@ -79,18 +98,36 @@ RetrieveClipboardFormatName(HINSTANCE hInstance, PVOID lpszFormat, UINT cch) { - if (!GetPredefinedClipboardFormatName(hInstance, uFormat, Unicode, lpszFormat, cch)) + ZeroMemory(lpszFormat, cch * (Unicode ? sizeof(WCHAR) : sizeof(CHAR))); + + /* Check for predefined clipboard format */ + if (GetPredefinedClipboardFormatName(hInstance, uFormat, Unicode, lpszFormat, cch) != 0) + return; + + /* Check for owner-display format */ + if (uFormat == CF_OWNERDISPLAY) { - if (Unicode) + if (SendClipboardOwnerMessage(Unicode, WM_ASKCBFORMATNAME, + (WPARAM)cch, (LPARAM)lpszFormat) != 0) { - if (!GetClipboardFormatNameW(uFormat, (LPWSTR)lpszFormat, cch)) + if (Unicode) LoadStringW(hInstance, STRING_CF_UNKNOWN, (LPWSTR)lpszFormat, cch); - } - else - { - if (!GetClipboardFormatNameA(uFormat, (LPSTR)lpszFormat, cch)) + else LoadStringA(hInstance, STRING_CF_UNKNOWN, (LPSTR)lpszFormat, cch); } + return; + } + + /* Fallback to registered clipboard format */ + if (Unicode) + { + if (!GetClipboardFormatNameW(uFormat, (LPWSTR)lpszFormat, cch)) + LoadStringW(hInstance, STRING_CF_UNKNOWN, (LPWSTR)lpszFormat, cch); + } + else + { + if (!GetClipboardFormatNameA(uFormat, (LPSTR)lpszFormat, cch)) + LoadStringA(hInstance, STRING_CF_UNKNOWN, (LPSTR)lpszFormat, cch); } } @@ -112,13 +149,22 @@ void DeleteClipboardContent(void) UINT GetAutomaticClipboardFormat(void) { - static UINT uFormatList[] = { + static UINT uFormatList[] = + { + CF_OWNERDISPLAY, CF_UNICODETEXT, + CF_TEXT, + CF_OEMTEXT, CF_ENHMETAFILE, CF_METAFILEPICT, CF_DIBV5, CF_DIB, - CF_BITMAP + CF_BITMAP, + CF_DSPTEXT, + CF_DSPBITMAP, + CF_DSPMETAFILEPICT, + CF_DSPENHMETAFILE, + CF_PALETTE }; return GetPriorityClipboardFormat(uFormatList, ARRAYSIZE(uFormatList)); @@ -128,12 +174,16 @@ BOOL IsClipboardFormatSupported(UINT uFormat) { switch (uFormat) { + case CF_OWNERDISPLAY: case CF_UNICODETEXT: + case CF_TEXT: + case CF_OEMTEXT: case CF_BITMAP: case CF_ENHMETAFILE: case CF_METAFILEPICT: case CF_DIB: case CF_DIBV5: + case CF_HDROP: { return TRUE; } @@ -144,3 +194,179 @@ BOOL IsClipboardFormatSupported(UINT uFormat) } } } + +SIZE_T +GetLineExtentW( + IN LPCWSTR lpText, + OUT LPCWSTR* lpNextLine) +{ + LPCWSTR ptr; + + /* Find the next line of text (lpText is NULL-terminated) */ + /* For newlines, focus only on '\n', not on '\r' */ + ptr = wcschr(lpText, L'\n'); // Find the end of this line. + if (ptr) + { + /* We have the end of this line, go to the next line (ignore the endline in the count) */ + *lpNextLine = ptr + 1; + } + else + { + /* This line was the last one, go pointing to the terminating NULL */ + ptr = lpText + wcslen(lpText); + *lpNextLine = ptr; + } + + return (ptr - lpText); +} + +SIZE_T +GetLineExtentA( + IN LPCSTR lpText, + OUT LPCSTR* lpNextLine) +{ + LPCSTR ptr; + + /* Find the next line of text (lpText is NULL-terminated) */ + /* For newlines, focus only on '\n', not on '\r' */ + ptr = strchr(lpText, '\n'); // Find the end of this line. + if (ptr) + { + /* We have the end of this line, go to the next line (ignore the endline in the count) */ + *lpNextLine = ptr + 1; + } + else + { + /* This line was the last one, go pointing to the terminating NULL */ + ptr = lpText + strlen(lpText); + *lpNextLine = ptr; + } + + return (ptr - lpText); +} + +BOOL GetClipboardDataDimensions(UINT uFormat, PRECT pRc) +{ + SetRectEmpty(pRc); + + if (!OpenClipboard(Globals.hMainWnd)) + { + return FALSE; + } + + switch (uFormat) + { + case CF_DSPBITMAP: + case CF_BITMAP: + { + HBITMAP hBitmap; + BITMAP bmp; + + hBitmap = (HBITMAP)GetClipboardData(CF_BITMAP); + GetObjectW(hBitmap, sizeof(bmp), &bmp); + SetRect(pRc, 0, 0, bmp.bmWidth, bmp.bmHeight); + break; + } + + case CF_DIB: + case CF_DIBV5: + { + HGLOBAL hGlobal; + LPBITMAPINFOHEADER lpInfoHeader; + + hGlobal = GetClipboardData(uFormat); + if (!hGlobal) + break; + + lpInfoHeader = GlobalLock(hGlobal); + if (!lpInfoHeader) + break; + + if (lpInfoHeader->biSize == sizeof(BITMAPCOREHEADER)) + { + LPBITMAPCOREHEADER lpCoreHeader = (LPBITMAPCOREHEADER)lpInfoHeader; + SetRect(pRc, 0, 0, + lpCoreHeader->bcWidth, + lpCoreHeader->bcHeight); + } + else if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) || + (lpInfoHeader->biSize == sizeof(BITMAPV4HEADER)) || + (lpInfoHeader->biSize == sizeof(BITMAPV5HEADER))) + { + SetRect(pRc, 0, 0, + lpInfoHeader->biWidth, + /* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */ + (lpInfoHeader->biHeight > 0) ? lpInfoHeader->biHeight + : -lpInfoHeader->biHeight); + } + else + { + /* Invalid format */ + } + + GlobalUnlock(hGlobal); + break; + } + + case CF_DSPTEXT: + case CF_TEXT: + case CF_OEMTEXT: + case CF_UNICODETEXT: + { + HDC hDC; + HGLOBAL hGlobal; + PVOID lpText, ptr; + DWORD dwSize; + SIZE txtSize = {0, 0}; + SIZE_T lineSize; + + hGlobal = GetClipboardData(uFormat); + if (!hGlobal) + break; + + lpText = GlobalLock(hGlobal); + if (!lpText) + break; + + hDC = GetDC(Globals.hMainWnd); + + /* Find the size of the rectangle enclosing the text */ + for (;;) + { + if (uFormat == CF_UNICODETEXT) + { + if (*(LPCWSTR)lpText == UNICODE_NULL) + break; + lineSize = GetLineExtentW(lpText, (LPCWSTR*)&ptr); + dwSize = GetTabbedTextExtentW(hDC, lpText, lineSize, 0, NULL); + } + else + { + if (*(LPCSTR)lpText == ANSI_NULL) + break; + lineSize = GetLineExtentA(lpText, (LPCSTR*)&ptr); + dwSize = GetTabbedTextExtentA(hDC, lpText, lineSize, 0, NULL); + } + txtSize.cx = max(txtSize.cx, LOWORD(dwSize)); + txtSize.cy += HIWORD(dwSize); + lpText = ptr; + } + + ReleaseDC(Globals.hMainWnd, hDC); + + GlobalUnlock(hGlobal); + + SetRect(pRc, 0, 0, txtSize.cx, txtSize.cy); + break; + } + + default: + { + break; + } + } + + CloseClipboard(); + + return TRUE; +} diff --git a/base/applications/clipbrd/cliputils.h b/base/applications/clipbrd/cliputils.h index 28d64b8b3e7..f126108649f 100644 --- a/base/applications/clipbrd/cliputils.h +++ b/base/applications/clipbrd/cliputils.h @@ -1,11 +1,20 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/cliputils.h - * PURPOSE: Clipboard helper functions. - * PROGRAMMERS: Ricardo Hanke + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Clipboard helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ +#pragma once + +LRESULT +SendClipboardOwnerMessage( + IN BOOL bUnicode, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam); + void RetrieveClipboardFormatName(HINSTANCE hInstance, UINT uFormat, @@ -16,3 +25,15 @@ RetrieveClipboardFormatName(HINSTANCE hInstance, void DeleteClipboardContent(void); UINT GetAutomaticClipboardFormat(void); BOOL IsClipboardFormatSupported(UINT uFormat); + +SIZE_T +GetLineExtentW( + IN LPCWSTR lpText, + OUT LPCWSTR* lpNextLine); + +SIZE_T +GetLineExtentA( + IN LPCSTR lpText, + OUT LPCSTR* lpNextLine); + +BOOL GetClipboardDataDimensions(UINT uFormat, PRECT pRc); diff --git a/base/applications/clipbrd/fileutils.c b/base/applications/clipbrd/fileutils.c index 3be9acb2754..d4b3ba72f07 100644 --- a/base/applications/clipbrd/fileutils.c +++ b/base/applications/clipbrd/fileutils.c @@ -1,10 +1,9 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/fileutils.c - * PURPOSE: Clipboard file format helper functions. - * PROGRAMMERS: Ricardo Hanke - * Hermes Belusca-Maito + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Clipboard file format helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ #include "precomp.h" @@ -399,22 +398,22 @@ void ReadClipboardFile(LPCWSTR lpFileName) break; } - case CF_BITMAP: case CF_DSPBITMAP: + case CF_BITMAP: { bResult = ClipboardReadBitmap(hFile, dwOffData, dwLenData); break; } - case CF_METAFILEPICT: case CF_DSPMETAFILEPICT: + case CF_METAFILEPICT: { bResult = ClipboardReadMetafile(hFile, dwOffData, dwLenData); break; } - case CF_ENHMETAFILE: case CF_DSPENHMETAFILE: + case CF_ENHMETAFILE: { bResult = ClipboardReadEnhMetafile(hFile, dwOffData, dwLenData); break; diff --git a/base/applications/clipbrd/fileutils.h b/base/applications/clipbrd/fileutils.h index e103d8ee843..b17c45066a9 100644 --- a/base/applications/clipbrd/fileutils.h +++ b/base/applications/clipbrd/fileutils.h @@ -1,15 +1,18 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/fileutils.h - * PURPOSE: Clipboard file format helper functions. - * PROGRAMMERS: Ricardo Hanke - * Hermes Belusca-Maito + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Clipboard file format helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ +#pragma once + +/* Clipboard file format signatures */ #define CLIP_FMT_31 0xC350 #define CLIP_FMT_NT 0xC351 #define CLIP_FMT_BK 0xC352 + #define MAX_FMT_NAME_LEN 79 /* diff --git a/base/applications/clipbrd/precomp.h b/base/applications/clipbrd/precomp.h index 5159ec2e5d0..7a04b962c40 100644 --- a/base/applications/clipbrd/precomp.h +++ b/base/applications/clipbrd/precomp.h @@ -1,8 +1,18 @@ +/* + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Precompiled header. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + */ + #ifndef _CLIPBRD_PCH_ #define _CLIPBRD_PCH_ // #pragma once +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 + #include #include @@ -19,11 +29,12 @@ #include "resources.h" #include "cliputils.h" #include "fileutils.h" -#include "winutils.h" #include "scrollutils.h" +#include "winutils.h" #define MAX_STRING_LEN 255 #define DISPLAY_MENU_POS 2 + #define CF_NONE 0 typedef struct _CLIPBOARD_GLOBALS @@ -34,8 +45,10 @@ typedef struct _CLIPBOARD_GLOBALS HMENU hMenu; UINT uDisplayFormat; UINT uCheckedItem; - UINT uLinesToScroll; - HBITMAP hDspBmp; + + /* Metrics of the current font */ + LONG CharWidth; + LONG CharHeight; } CLIPBOARD_GLOBALS; extern CLIPBOARD_GLOBALS Globals; diff --git a/base/applications/clipbrd/resources.h b/base/applications/clipbrd/resources.h index ac958ef6258..afc9ae8fd96 100644 --- a/base/applications/clipbrd/resources.h +++ b/base/applications/clipbrd/resources.h @@ -1,3 +1,10 @@ +/* + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Resources header. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + */ + #pragma once #define CLIPBRD_ICON 100 diff --git a/base/applications/clipbrd/scrollutils.c b/base/applications/clipbrd/scrollutils.c index 424921f3215..87c6ce8334f 100644 --- a/base/applications/clipbrd/scrollutils.c +++ b/base/applications/clipbrd/scrollutils.c @@ -1,202 +1,166 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/scrollutils.c - * PURPOSE: Scrolling related helper functions. - * PROGRAMMERS: Ricardo Hanke + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Scrolling related helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ #include "precomp.h" -static int InternalSetScrollInfo(HWND hWnd, int nMin, int nMax, UINT nPage, int nPos, int fnBar) -{ - SCROLLINFO si; - - ZeroMemory(&si, sizeof(si)); - si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; - si.nMin = nMin; - si.nMax = nMax; - si.nPage = nPage; - si.nPos = nPos; - - return SetScrollInfo(hWnd, fnBar, &si, TRUE); -} - -void HandleKeyboardScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +void OnKeyScroll(HWND hWnd, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state) { + // NOTE: Windows uses an offset of 16 pixels switch (wParam) { case VK_UP: - { - SendMessage(hWnd, WM_VSCROLL, MAKELONG(SB_LINEUP, 0), 0); + OnScroll(hWnd, SB_VERT, MAKELONG(SB_LINEUP, 0), 5, state); break; - } case VK_DOWN: - { - SendMessage(hWnd, WM_VSCROLL, MAKELONG(SB_LINEDOWN, 0), 0); + OnScroll(hWnd, SB_VERT, MAKELONG(SB_LINEDOWN, 0), 5, state); break; - } case VK_LEFT: - { - SendMessage(hWnd, WM_HSCROLL, MAKELONG(SB_LINEUP, 0), 0); + OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LINELEFT, 0), 5, state); break; - } case VK_RIGHT: - { - SendMessage(hWnd, WM_HSCROLL, MAKELONG(SB_LINEDOWN, 0), 0); + OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LINERIGHT, 0), 5, state); break; - } case VK_PRIOR: - { - SendMessage(hWnd, WM_VSCROLL, MAKELONG(SB_PAGEUP, 0), 0); + OnScroll(hWnd, SB_VERT, MAKELONG(SB_PAGEUP, 0), state->nPageY, state); break; - } case VK_NEXT: + OnScroll(hWnd, SB_VERT, MAKELONG(SB_PAGEDOWN, 0), state->nPageY, state); + break; + + case VK_HOME: { - SendMessage(hWnd, WM_VSCROLL, MAKELONG(SB_PAGEDOWN, 0), 0); + OnScroll(hWnd, SB_HORZ, MAKELONG(SB_LEFT, 0), 0, state); + if (GetKeyState(VK_CONTROL) & 0x8000) + OnScroll(hWnd, SB_VERT, MAKELONG(SB_TOP, 0), 0, state); break; } - default: + case VK_END: { + OnScroll(hWnd, SB_HORZ, MAKELONG(SB_RIGHT, 0), 0, state); + if (GetKeyState(VK_CONTROL) & 0x8000) + OnScroll(hWnd, SB_VERT, MAKELONG(SB_BOTTOM, 0), 0, state); break; } + + default: + break; } } -void HandleMouseScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state) +void OnMouseScroll(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state) { - SCROLLINFO si; - int Delta; - int NewPos; + INT nBar; + INT nPage; + INT iDelta; + UINT uLinesToScroll = state->uLinesToScroll; + INT zDelta = GET_WHEEL_DELTA_WPARAM(wParam); + WORD sbCode; - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE; - GetScrollInfo(hWnd, SB_VERT, &si); + assert(uMsg == WM_MOUSEWHEEL || uMsg == WM_MOUSEHWHEEL); - if (Globals.uLinesToScroll == WHEEL_PAGESCROLL) + if (uMsg == WM_MOUSEWHEEL) { - NewPos = si.nPage; + nBar = SB_VERT; + nPage = state->nPageY; + + /* Accumulate wheel rotation ticks */ + zDelta += state->iWheelCarryoverY; + state->iWheelCarryoverY = zDelta % WHEEL_DELTA; } - else + else // if (uMsg == WM_MOUSEHWHEEL) { - NewPos = Globals.uLinesToScroll * 5; + nBar = SB_HORZ; + nPage = state->nPageX; + + /* Accumulate wheel rotation ticks */ + zDelta += state->iWheelCarryoverX; + state->iWheelCarryoverX = zDelta % WHEEL_DELTA; } - if (GET_WHEEL_DELTA_WPARAM(wParam) > 0) + /* + * If the user specified scrolling by pages, do so. + * Due to a bug on Windows where, if the window height is + * less than the scroll lines delta default value (== 3), + * several lines would be skipped when scrolling if we + * used the WHEEL_PAGESCROLL value. Instead of this, use + * the number of lines per page as the limiting value. + * See https://www.strchr.com/corrections_to_raymond_chen_s_wheel_scrolling_code + * for more details. + */ + if (uLinesToScroll > nPage) // (uLinesToScroll == WHEEL_PAGESCROLL) + uLinesToScroll = nPage; + /* If the user specified no wheel scrolling, don't do anything */ + else if (uLinesToScroll == 0) + return; + + /* Compute the scroll direction and the absolute delta value */ + if (zDelta > 0) { - NewPos = state->CurrentY - NewPos; + sbCode = SB_LINEUP; } else { - NewPos = state->CurrentY + NewPos; - } - - NewPos = min(state->MaxY, max(0, NewPos)); - - if (NewPos == state->CurrentY) - { - return; + sbCode = SB_LINEDOWN; + zDelta = -zDelta; } - Delta = NewPos - state->CurrentY; - - state->CurrentY = NewPos; + /* Compute how many lines we should scroll (in absolute value) */ + iDelta = (INT)uLinesToScroll * zDelta / WHEEL_DELTA; - ScrollWindowEx(hWnd, 0, -Delta, NULL, NULL, NULL, NULL, SW_INVALIDATE); - - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = state->CurrentY; - SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + OnScroll(hWnd, nBar, MAKELONG(sbCode, 0), iDelta, state); } -void HandleHorizontalScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state) +void OnScroll(HWND hWnd, INT nBar, WPARAM wParam, INT iDelta, LPSCROLLSTATE state) { SCROLLINFO si; - int Delta; - int NewPos; + PINT pCurrent; + INT Maximum; + INT NewPos; + INT OldX, OldY; - ZeroMemory(&si, sizeof(si)); - si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_TRACKPOS; - GetScrollInfo(hWnd, SB_HORZ, &si); + assert(nBar == SB_HORZ || nBar == SB_VERT); - switch (LOWORD(wParam)) + if (Globals.uDisplayFormat == CF_OWNERDISPLAY) { - case SB_THUMBPOSITION: - case SB_THUMBTRACK: + if (nBar == SB_HORZ) { - NewPos = si.nTrackPos; - break; + SendClipboardOwnerMessage(TRUE, WM_HSCROLLCLIPBOARD, + (WPARAM)hWnd, (LPARAM)wParam); } - - case SB_LINELEFT: + else // if (nBar == SB_VERT) { - NewPos = state->CurrentX - 5; - break; - } - - case SB_LINERIGHT: - { - NewPos = state->CurrentX + 5; - break; - } - - case SB_PAGELEFT: - { - NewPos = state->CurrentX - si.nPage; - break; - } - - case SB_PAGERIGHT: - { - NewPos = state->CurrentX + si.nPage; - break; - } - - default: - { - NewPos = state->CurrentX; - break; + SendClipboardOwnerMessage(TRUE, WM_VSCROLLCLIPBOARD, + (WPARAM)hWnd, (LPARAM)wParam); } + return; } - NewPos = min(state->MaxX, max(0, NewPos)); - - if (NewPos == state->CurrentX) - { - return; - } - - Delta = NewPos - state->CurrentX; - - state->CurrentX = NewPos; - - ScrollWindowEx(hWnd, -Delta, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE); - - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = state->CurrentX; - SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); -} - -void HandleVerticalScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state) -{ - SCROLLINFO si; - int Delta; - int NewPos; + if (nBar == SB_HORZ) + { + pCurrent = &state->CurrentX; + Maximum = state->MaxX; + } + else // if (nBar == SB_VERT) + { + pCurrent = &state->CurrentY; + Maximum = state->MaxY; + } ZeroMemory(&si, sizeof(si)); si.cbSize = sizeof(si); - si.fMask = SIF_PAGE | SIF_TRACKPOS; - GetScrollInfo(hWnd, SB_VERT, &si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS; + GetScrollInfo(hWnd, nBar, &si); switch (LOWORD(wParam)) { @@ -207,108 +171,117 @@ void HandleVerticalScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar break; } - case SB_LINEUP: + case SB_LINEUP: // SB_LINELEFT: { - NewPos = state->CurrentY - 5; + NewPos = si.nPos - iDelta; break; } - case SB_LINEDOWN: + case SB_LINEDOWN: // SB_LINERIGHT: { - NewPos = state->CurrentY + 5; + NewPos = si.nPos + iDelta; break; } - case SB_PAGEUP: + case SB_PAGEUP: // SB_PAGELEFT: { - NewPos = state->CurrentY - si.nPage; + NewPos = si.nPos - si.nPage; break; } - case SB_PAGEDOWN: + case SB_PAGEDOWN: // SB_PAGERIGHT: { - NewPos = state->CurrentY + si.nPage; + NewPos = si.nPos + si.nPage; break; } - default: + case SB_TOP: // SB_LEFT: { - NewPos = state->CurrentY; + NewPos = si.nMin; break; } - } - NewPos = min(state->MaxY, max(0, NewPos)); + case SB_BOTTOM: // SB_RIGHT: + { + NewPos = si.nMax; + break; + } - if (NewPos == state->CurrentY) - { - return; - } + default: + { + NewPos = si.nPos; + break; + } + } + + NewPos = min(max(NewPos, 0), Maximum); - Delta = NewPos - state->CurrentY; + if (si.nPos == NewPos) + return; - state->CurrentY = NewPos; + OldX = state->CurrentX; + OldY = state->CurrentY; + *pCurrent = NewPos; - ScrollWindowEx(hWnd, 0, -Delta, NULL, NULL, NULL, NULL, SW_INVALIDATE); + ScrollWindowEx(hWnd, + OldX - state->CurrentX, + OldY - state->CurrentY, + NULL, + NULL, + NULL, + NULL, + SW_ERASE | SW_INVALIDATE); + UpdateWindow(hWnd); - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = state->CurrentY; - SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + si.fMask = SIF_POS; + si.nPos = NewPos; + SetScrollInfo(hWnd, nBar, &si, TRUE); } -void UpdateWindowScrollState(HWND hWnd, HBITMAP hBitmap, LPSCROLLSTATE lpState) +void UpdateLinesToScroll(LPSCROLLSTATE state) { - BITMAP bmp; - RECT rc; + UINT uLinesToScroll; - if (!GetObject(hBitmap, sizeof(BITMAP), &bmp)) + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &uLinesToScroll, 0)) { - bmp.bmWidth = 0; - bmp.bmHeight = 0; + /* Default value on Windows */ + state->uLinesToScroll = 3; } - - if (!GetClientRect(hWnd, &rc)) + else { - SetRectEmpty(&rc); + state->uLinesToScroll = uLinesToScroll; } - - lpState->MaxX = max(bmp.bmWidth - rc.right, 0); - lpState->CurrentX = min(lpState->CurrentX, lpState->MaxX); - InternalSetScrollInfo(hWnd, 0, bmp.bmWidth, rc.right, lpState->CurrentX, SB_HORZ); - - lpState->MaxY = max(bmp.bmHeight - rc.bottom, 0); - lpState->CurrentY = min(lpState->CurrentY, lpState->MaxY); - InternalSetScrollInfo(hWnd, 0, bmp.bmHeight, rc.bottom, lpState->CurrentY, SB_VERT); } -BOOL ScrollBlt(PAINTSTRUCT ps, HBITMAP hBmp, SCROLLSTATE state) +void UpdateWindowScrollState(HWND hWnd, INT nMaxWidth, INT nMaxHeight, LPSCROLLSTATE lpState) { - RECT rect; - BOOL ret; - HDC hdc; - int xpos; - int ypos; - - rect.left = ps.rcPaint.left; - rect.top = ps.rcPaint.top; - rect.right = (ps.rcPaint.right - ps.rcPaint.left); - rect.bottom = (ps.rcPaint.bottom - ps.rcPaint.top); - - xpos = ps.rcPaint.left + state.CurrentX; - ypos = ps.rcPaint.top + state.CurrentY; + RECT rc; + SCROLLINFO si; - ret = FALSE; + if (!GetClientRect(hWnd, &rc)) + SetRectEmpty(&rc); - hdc = CreateCompatibleDC(ps.hdc); - if (hdc) - { - if (SelectObject(hdc, hBmp)) - { - ret = BitBlt(ps.hdc, rect.left, rect.top, rect.right, rect.bottom, hdc, xpos, ypos, SRCCOPY); - } - DeleteDC(hdc); - } + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL; - return ret; + lpState->nMaxWidth = nMaxWidth; + lpState->MaxX = max(nMaxWidth - rc.right, 0); + lpState->CurrentX = min(lpState->CurrentX, lpState->MaxX); + lpState->nPageX = rc.right; + si.nMin = 0; + si.nMax = nMaxWidth; + si.nPage = lpState->nPageX; + si.nPos = lpState->CurrentX; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + + lpState->nMaxHeight = nMaxHeight; + lpState->MaxY = max(nMaxHeight - rc.bottom, 0); + lpState->CurrentY = min(lpState->CurrentY, lpState->MaxY); + lpState->nPageY = rc.bottom; + si.nMin = 0; + si.nMax = nMaxHeight; + si.nPage = lpState->nPageY; + si.nPos = lpState->CurrentY; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); } diff --git a/base/applications/clipbrd/scrollutils.h b/base/applications/clipbrd/scrollutils.h index 221bf50ce07..4645a24441e 100644 --- a/base/applications/clipbrd/scrollutils.h +++ b/base/applications/clipbrd/scrollutils.h @@ -1,22 +1,31 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/scrollutils.h - * PURPOSE: Scrolling related helper functions. - * PROGRAMMERS: Ricardo Hanke + * PROJECT: ReactOS Clipboard Viewer + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Scrolling related helper functions. + * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke + * Copyright 2015-2018 Hermes Belusca-Maito */ +#pragma once + typedef struct _SCROLLSTATE { - int CurrentX; - int CurrentY; - int MaxX; - int MaxY; + UINT uLinesToScroll; /* Number of lines to scroll on one wheel rotation movement (== one "click" == WHEEL_DELTA ticks) */ + INT iWheelCarryoverX; /* Unused wheel ticks (< WHEEL_DELTA) */ + INT iWheelCarryoverY; + INT nPageX; /* Number of lines per page */ + INT nPageY; + INT CurrentX; /* Current scrollbar position */ + INT CurrentY; + INT MaxX; /* Maximum scrollbar position */ + INT MaxY; + INT nMaxWidth; /* Maximum span of displayed data */ + INT nMaxHeight; } SCROLLSTATE, *LPSCROLLSTATE; -void HandleKeyboardScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -void HandleMouseScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state); -void HandleHorizontalScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state); -void HandleVerticalScrollEvents(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state); -void UpdateWindowScrollState(HWND hWnd, HBITMAP hBmp, LPSCROLLSTATE lpState); -BOOL ScrollBlt(PAINTSTRUCT ps, HBITMAP hBmp, SCROLLSTATE state); +void OnKeyScroll(HWND hWnd, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state); +void OnMouseScroll(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSCROLLSTATE state); +void OnScroll(HWND hWnd, INT nBar, WPARAM wParam, INT iDelta, LPSCROLLSTATE state); + +void UpdateLinesToScroll(LPSCROLLSTATE state); +void UpdateWindowScrollState(HWND hWnd, INT nMaxWidth, INT nMaxHeight, LPSCROLLSTATE lpState); diff --git a/base/applications/clipbrd/winutils.c b/base/applications/clipbrd/winutils.c index 43697d53e94..eb1cf03c2dc 100644 --- a/base/applications/clipbrd/winutils.c +++ b/base/applications/clipbrd/winutils.c @@ -1,9 +1,9 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/winutils.c - * PURPOSE: Miscellaneous helper functions. - * PROGRAMMERS: Ricardo Hanke + * 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" @@ -46,22 +46,6 @@ void BringWindowToFront(HWND hWnd) } } -int DrawTextFromResource(HINSTANCE hInstance, UINT uID, HDC hDC, LPRECT lpRect, UINT uFormat) -{ - LPWSTR lpBuffer; - int nCount; - - nCount = LoadStringW(hInstance, uID, (LPWSTR)&lpBuffer, 0); - if (nCount) - { - return DrawTextW(hDC, lpBuffer, nCount, lpRect, uFormat); - } - else - { - return 0; - } -} - int MessageBoxRes(HWND hWnd, HINSTANCE hInstance, UINT uText, UINT uCaption, UINT uType) { MSGBOXPARAMSW mb; @@ -78,45 +62,135 @@ int MessageBoxRes(HWND hWnd, HINSTANCE hInstance, UINT uText, UINT uCaption, UIN return MessageBoxIndirectW(&mb); } -void DrawTextFromClipboard(HDC hDC, LPRECT lpRect, UINT uFormat) +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; - LPWSTR lpchText; + PVOID lpText, ptr; + SIZE_T lineSize; + INT FirstLine, LastLine; - hGlobal = GetClipboardData(CF_UNICODETEXT); + hGlobal = GetClipboardData(uFormat); if (!hGlobal) return; - lpchText = GlobalLock(hGlobal); - if (!lpchText) + lpText = GlobalLock(hGlobal); + if (!lpText) return; - DrawTextW(hDC, lpchText, -1, lpRect, uFormat); + /* 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(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, int nXSrc, int nYSrc, DWORD dwRop) +void BitBltFromClipboard(PAINTSTRUCT ps, SCROLLSTATE state, DWORD dwRop) { HDC hdcMem; - HBITMAP hbm; + HBITMAP hBitmap; + BITMAP bmp; + LONG bmWidth, bmHeight; - hdcMem = CreateCompatibleDC(hdcDest); - if (hdcMem) - { - hbm = (HBITMAP)GetClipboardData(CF_BITMAP); - SelectObject(hdcMem, hbm); - BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcMem, nXSrc, nYSrc, dwRop); - DeleteDC(hdcMem); - } + 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, HDC hdc, int XDest, int YDest, int XSrc, int YSrc, UINT uStartScan, UINT fuColorUse) +void SetDIBitsToDeviceFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state, UINT fuColorUse) { + HGLOBAL hGlobal; LPBITMAPINFOHEADER lpInfoHeader; LPBYTE lpBits; LONG bmWidth, bmHeight; DWORD dwPalSize = 0; - HGLOBAL hGlobal; hGlobal = GetClipboardData(uFormat); if (!hGlobal) @@ -183,9 +257,9 @@ void SetDIBitsToDeviceFromClipboard(UINT uFormat, HDC hdc, int XDest, int YDest, * * FIXME: investigate!! * ANSWER: this is a Windows bug; part of the answer is there: - * http://go4answers.webhost4life.com/Help/bug-clipboard-format-conversions-28724.aspx + * https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/ac7ab3b5-8609-4478-b86a-976dab44c271/bug-clipboard-format-conversions-cfdib-cfdibv5-cfdib * May be related: - * http://blog.talosintel.com/2015/10/dangerous-clipboard.html + * https://blog.talosintelligence.com/2015/10/dangerous-clipboard.html */ #if 0 if ((lpInfoHeader->biSize == sizeof(BITMAPINFOHEADER)) && @@ -196,6 +270,7 @@ void SetDIBitsToDeviceFromClipboard(UINT uFormat, HDC hdc, int XDest, int YDest, #endif bmWidth = lpInfoHeader->biWidth; + /* NOTE: biHeight < 0 for bottom-up DIBs, or > 0 for top-down DIBs */ bmHeight = lpInfoHeader->biHeight; } else @@ -207,11 +282,19 @@ void SetDIBitsToDeviceFromClipboard(UINT uFormat, HDC hdc, int XDest, int YDest, lpBits = (LPBYTE)lpInfoHeader + lpInfoHeader->biSize + dwPalSize; - SetDIBitsToDevice(hdc, - XDest, YDest, - bmWidth, bmHeight, - XSrc, YSrc, - uStartScan, + /* + * 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, @@ -248,52 +331,25 @@ void PlayEnhMetaFileFromClipboard(HDC hdc, const RECT *lpRect) PlayEnhMetaFile(hdc, hEmf, lpRect); } -UINT RealizeClipboardPalette(HWND hWnd) +BOOL RealizeClipboardPalette(HDC hdc) { - HPALETTE hPalette; - HPALETTE hOldPalette; - UINT uResult; - HDC hDevContext; - - if (!OpenClipboard(Globals.hMainWnd)) - { - return GDI_ERROR; - } + BOOL Success; + HPALETTE hPalette, hOldPalette; if (!IsClipboardFormatAvailable(CF_PALETTE)) - { - CloseClipboard(); - return GDI_ERROR; - } + return FALSE; hPalette = GetClipboardData(CF_PALETTE); if (!hPalette) - { - CloseClipboard(); - return GDI_ERROR; - } - - hDevContext = GetDC(hWnd); - if (!hDevContext) - { - CloseClipboard(); - return GDI_ERROR; - } + return FALSE; - hOldPalette = SelectPalette(hDevContext, hPalette, FALSE); + hOldPalette = SelectPalette(hdc, hPalette, FALSE); if (!hOldPalette) - { - ReleaseDC(hWnd, hDevContext); - CloseClipboard(); - return GDI_ERROR; - } - - uResult = RealizePalette(hDevContext); + return FALSE; - SelectPalette(hDevContext, hOldPalette, FALSE); - ReleaseDC(hWnd, hDevContext); + Success = (RealizePalette(hdc) != GDI_ERROR); - CloseClipboard(); + SelectPalette(hdc, hOldPalette, FALSE); - return uResult; + return Success; } diff --git a/base/applications/clipbrd/winutils.h b/base/applications/clipbrd/winutils.h index 12dde908d82..e29ec927bc3 100644 --- a/base/applications/clipbrd/winutils.h +++ b/base/applications/clipbrd/winutils.h @@ -1,18 +1,20 @@ /* - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS Clipboard Viewer - * FILE: base/applications/clipbrd/winutils.h - * PURPOSE: Miscellaneous helper functions. - * PROGRAMMERS: Ricardo Hanke + * 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 */ +#pragma once + void ShowLastWin32Error(HWND hwndParent); void BringWindowToFront(HWND hWnd); -int DrawTextFromResource(HINSTANCE hInstance, UINT uID, HDC hDC, LPRECT lpRect, UINT uFormat); int MessageBoxRes(HWND hWnd, HINSTANCE hInstance, UINT uText, UINT uCaption, UINT uType); -void DrawTextFromClipboard(HDC hDC, LPRECT lpRect, UINT uFormat); -void BitBltFromClipboard(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, int nXSrc, int nYSrc, DWORD dwRop); -void SetDIBitsToDeviceFromClipboard(UINT uFormat, HDC hdc, int XDest, int YDest, int XSrc, int YSrc, UINT uStartScan, UINT fuColorUse); +void DrawTextFromResource(HINSTANCE hInstance, UINT uID, HDC hDC, LPRECT lpRect, UINT uFormat); +void DrawTextFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state); +void BitBltFromClipboard(PAINTSTRUCT ps, SCROLLSTATE state, DWORD dwRop); +void SetDIBitsToDeviceFromClipboard(UINT uFormat, PAINTSTRUCT ps, SCROLLSTATE state, UINT fuColorUse); void PlayMetaFileFromClipboard(HDC hdc, const RECT *lpRect); void PlayEnhMetaFileFromClipboard(HDC hdc, const RECT *lpRect); -UINT RealizeClipboardPalette(HWND hWnd); +BOOL RealizeClipboardPalette(HDC hdc);