2005-04-14 13:30:31 +02:00
|
|
|
/*
|
|
|
|
* Hex Edit control
|
|
|
|
*
|
|
|
|
* Copyright 2005 Robert Shearman
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2005-04-14 13:30:31 +02:00
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
* - Selection support
|
|
|
|
* - Cut, copy and paste
|
|
|
|
* - Mouse support
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "wingdi.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "winnls.h"
|
|
|
|
#include "commctrl.h"
|
|
|
|
|
2018-02-07 00:20:07 +01:00
|
|
|
#include "wine/heap.h"
|
2005-04-14 13:30:31 +02:00
|
|
|
#include "main.h"
|
|
|
|
|
|
|
|
/* spaces dividing hex and ASCII */
|
|
|
|
#define DIV_SPACES 4
|
|
|
|
|
|
|
|
typedef struct tagHEXEDIT_INFO
|
|
|
|
{
|
|
|
|
HWND hwndSelf;
|
|
|
|
HFONT hFont;
|
|
|
|
BOOL bFocus : 1;
|
|
|
|
BOOL bFocusHex : 1; /* TRUE if focus is on hex, FALSE if focus on ASCII */
|
|
|
|
BOOL bInsert : 1; /* insert mode if TRUE, overwrite mode if FALSE */
|
|
|
|
INT nHeight; /* height of text */
|
|
|
|
INT nCaretPos; /* caret pos in nibbles */
|
|
|
|
BYTE *pData;
|
|
|
|
INT cbData;
|
|
|
|
INT nBytesPerLine; /* bytes of hex to display per line of the control */
|
|
|
|
INT nScrollPos; /* first visible line */
|
|
|
|
} HEXEDIT_INFO;
|
|
|
|
|
|
|
|
static inline LRESULT HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw);
|
|
|
|
|
2011-04-16 10:07:21 +02:00
|
|
|
static inline BYTE hexchar_to_byte(WCHAR ch)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
|
|
|
if (ch >= '0' && ch <= '9')
|
|
|
|
return ch - '0';
|
|
|
|
else if (ch >= 'a' && ch <= 'f')
|
|
|
|
return ch - 'a' + 10;
|
|
|
|
else if (ch >= 'A' && ch <= 'F')
|
|
|
|
return ch - 'A' + 10;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-11 18:28:32 +01:00
|
|
|
static LPWSTR HexEdit_GetLineText(int offset, BYTE *pData, LONG cbData, LONG pad)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
2018-03-11 18:28:32 +01:00
|
|
|
WCHAR *lpszLine = heap_xalloc((6 + cbData * 3 + pad * 3 + DIV_SPACES + cbData + 1) * sizeof(WCHAR));
|
2005-04-14 13:30:31 +02:00
|
|
|
LONG i;
|
|
|
|
|
2022-04-14 15:01:35 +02:00
|
|
|
wsprintfW(lpszLine, L"%04X ", offset);
|
2018-03-11 18:28:32 +01:00
|
|
|
|
2005-04-14 13:30:31 +02:00
|
|
|
for (i = 0; i < cbData; i++)
|
2022-04-14 15:01:35 +02:00
|
|
|
wsprintfW(lpszLine + 6 + i*3, L"%02X ", pData[offset + i]);
|
2005-04-14 13:30:31 +02:00
|
|
|
for (i = 0; i < pad * 3; i++)
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine[6 + cbData * 3 + i] = ' ';
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
for (i = 0; i < DIV_SPACES; i++)
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine[6 + cbData * 3 + pad * 3 + i] = ' ';
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
/* attempt an ASCII representation if the characters are printable,
|
|
|
|
* otherwise display a '.' */
|
|
|
|
for (i = 0; i < cbData; i++)
|
|
|
|
{
|
|
|
|
/* (C1_ALPHA|C1_BLANK|C1_PUNCT|C1_DIGIT|C1_LOWER|C1_UPPER) */
|
2019-04-01 11:47:50 +02:00
|
|
|
if (iswprint(pData[offset + i]))
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine[6 + cbData * 3 + pad * 3 + DIV_SPACES + i] = pData[offset + i];
|
2005-04-14 13:30:31 +02:00
|
|
|
else
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine[6 + cbData * 3 + pad * 3 + DIV_SPACES + i] = '.';
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine[6 + cbData * 3 + pad * 3 + DIV_SPACES + cbData] = 0;
|
2005-04-14 13:30:31 +02:00
|
|
|
return lpszLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
HexEdit_Paint(HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
PAINTSTRUCT ps;
|
|
|
|
HDC hdc = BeginPaint(infoPtr->hwndSelf, &ps);
|
|
|
|
INT nXStart, nYStart;
|
|
|
|
COLORREF clrOldText;
|
|
|
|
HFONT hOldFont;
|
|
|
|
INT iMode;
|
|
|
|
LONG lByteOffset = infoPtr->nScrollPos * infoPtr->nBytesPerLine;
|
2018-03-11 18:28:32 +01:00
|
|
|
int i;
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
/* Make a gap from the frame */
|
|
|
|
nXStart = GetSystemMetrics(SM_CXBORDER);
|
|
|
|
nYStart = GetSystemMetrics(SM_CYBORDER);
|
|
|
|
|
2011-04-16 10:07:21 +02:00
|
|
|
if (GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_DISABLED)
|
2005-04-14 13:30:31 +02:00
|
|
|
clrOldText = SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
|
|
|
|
else
|
|
|
|
clrOldText = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
|
|
|
|
|
|
|
|
iMode = SetBkMode(hdc, TRANSPARENT);
|
|
|
|
hOldFont = SelectObject(hdc, infoPtr->hFont);
|
2018-03-11 18:28:32 +01:00
|
|
|
|
|
|
|
for (i = lByteOffset; i < infoPtr->cbData; i += infoPtr->nBytesPerLine)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
2011-04-16 10:07:21 +02:00
|
|
|
LPWSTR lpszLine;
|
2018-03-11 18:28:32 +01:00
|
|
|
LONG nLineLen = min(infoPtr->cbData - i, infoPtr->nBytesPerLine);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine = HexEdit_GetLineText(i, infoPtr->pData, nLineLen, infoPtr->nBytesPerLine - nLineLen);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
/* FIXME: draw hex <-> ASCII mapping highlighted? */
|
2018-03-11 18:28:32 +01:00
|
|
|
TextOutW(hdc, nXStart, nYStart, lpszLine, lstrlenW(lpszLine));
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
nYStart += infoPtr->nHeight;
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(lpszLine);
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
SetBkMode(hdc, iMode);
|
|
|
|
SetTextColor(hdc, clrOldText);
|
|
|
|
|
|
|
|
EndPaint(infoPtr->hwndSelf, &ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
HexEdit_UpdateCaret(HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
HDC hdc;
|
|
|
|
HFONT hOldFont;
|
|
|
|
SIZE size;
|
|
|
|
INT nCaretBytePos = infoPtr->nCaretPos/2;
|
|
|
|
INT nByteLinePos = nCaretBytePos % infoPtr->nBytesPerLine;
|
|
|
|
INT nLine = nCaretBytePos / infoPtr->nBytesPerLine;
|
|
|
|
LONG nLineLen = min(infoPtr->cbData - nLine * infoPtr->nBytesPerLine, infoPtr->nBytesPerLine);
|
2018-03-11 18:28:32 +01:00
|
|
|
LPWSTR lpszLine = HexEdit_GetLineText(nLine * infoPtr->nBytesPerLine, infoPtr->pData, nLineLen, infoPtr->nBytesPerLine - nLineLen);
|
2005-04-14 13:30:31 +02:00
|
|
|
INT nCharOffset;
|
|
|
|
|
|
|
|
/* calculate offset of character caret is on in the line */
|
|
|
|
if (infoPtr->bFocusHex)
|
2018-03-11 18:28:32 +01:00
|
|
|
nCharOffset = 6 + nByteLinePos*3 + infoPtr->nCaretPos % 2;
|
2005-04-14 13:30:31 +02:00
|
|
|
else
|
2018-03-11 18:28:32 +01:00
|
|
|
nCharOffset = 6 + infoPtr->nBytesPerLine*3 + DIV_SPACES + nByteLinePos;
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
hdc = GetDC(infoPtr->hwndSelf);
|
|
|
|
hOldFont = SelectObject(hdc, infoPtr->hFont);
|
|
|
|
|
2011-04-16 10:07:21 +02:00
|
|
|
GetTextExtentPoint32W(hdc, lpszLine, nCharOffset, &size);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
ReleaseDC(infoPtr->hwndSelf, hdc);
|
|
|
|
|
|
|
|
if (!nLineLen) size.cx = 0;
|
|
|
|
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(lpszLine);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
SetCaretPos(
|
|
|
|
GetSystemMetrics(SM_CXBORDER) + size.cx,
|
|
|
|
GetSystemMetrics(SM_CYBORDER) + (nLine - infoPtr->nScrollPos) * infoPtr->nHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
HexEdit_UpdateScrollbars(HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
RECT rcClient;
|
|
|
|
INT nLines = infoPtr->cbData / infoPtr->nBytesPerLine;
|
|
|
|
INT nVisibleLines;
|
|
|
|
SCROLLINFO si;
|
|
|
|
|
|
|
|
GetClientRect(infoPtr->hwndSelf, &rcClient);
|
|
|
|
InflateRect(&rcClient, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER));
|
|
|
|
|
|
|
|
nVisibleLines = (rcClient.bottom - rcClient.top) / infoPtr->nHeight;
|
|
|
|
si.cbSize = sizeof(si);
|
|
|
|
si.fMask = SIF_RANGE | SIF_PAGE;
|
|
|
|
si.nMin = 0;
|
|
|
|
si.nMax = max(nLines - nVisibleLines, nLines);
|
|
|
|
si.nPage = nVisibleLines;
|
|
|
|
SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
HexEdit_EnsureVisible(HEXEDIT_INFO *infoPtr, INT nCaretPos)
|
|
|
|
{
|
|
|
|
INT nLine = nCaretPos / (2 * infoPtr->nBytesPerLine);
|
|
|
|
SCROLLINFO si;
|
|
|
|
|
|
|
|
si.cbSize = sizeof(si);
|
|
|
|
si.fMask = SIF_POS | SIF_PAGE;
|
|
|
|
GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
|
|
|
|
if (nLine < si.nPos)
|
|
|
|
si.nPos = nLine;
|
|
|
|
else if (nLine >= si.nPos + si.nPage)
|
|
|
|
si.nPos = nLine - si.nPage + 1;
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
si.fMask = SIF_POS;
|
|
|
|
|
|
|
|
SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, FALSE);
|
2011-04-16 10:07:21 +02:00
|
|
|
SendMessageW(infoPtr->hwndSelf, WM_VSCROLL, MAKELONG(SB_THUMBPOSITION, 0), 0);
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static LRESULT
|
|
|
|
HexEdit_SetData(HEXEDIT_INFO *infoPtr, INT cbData, const BYTE *pData)
|
|
|
|
{
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(infoPtr->pData);
|
2005-04-14 13:30:31 +02:00
|
|
|
infoPtr->cbData = 0;
|
|
|
|
|
2017-08-02 14:35:42 +02:00
|
|
|
infoPtr->pData = heap_xalloc(cbData);
|
|
|
|
memcpy(infoPtr->pData, pData, cbData);
|
|
|
|
infoPtr->cbData = cbData;
|
2005-04-14 13:30:31 +02:00
|
|
|
|
2017-08-02 14:35:42 +02:00
|
|
|
infoPtr->nCaretPos = 0;
|
|
|
|
HexEdit_UpdateScrollbars(infoPtr);
|
|
|
|
HexEdit_UpdateCaret(infoPtr);
|
|
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
|
|
|
|
return TRUE;
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT
|
|
|
|
HexEdit_GetData(HEXEDIT_INFO *infoPtr, INT cbData, BYTE *pData)
|
|
|
|
{
|
|
|
|
if (pData)
|
|
|
|
memcpy(pData, infoPtr->pData, min(cbData, infoPtr->cbData));
|
|
|
|
return infoPtr->cbData;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
2011-04-16 10:07:21 +02:00
|
|
|
HexEdit_Char (HEXEDIT_INFO *infoPtr, WCHAR ch)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
|
|
|
INT nCaretBytePos = infoPtr->nCaretPos/2;
|
|
|
|
|
|
|
|
assert(nCaretBytePos >= 0);
|
|
|
|
|
|
|
|
/* backspace is special */
|
|
|
|
if (ch == '\b')
|
|
|
|
{
|
|
|
|
if (infoPtr->nCaretPos == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* if at end of byte then delete the whole byte */
|
|
|
|
if (infoPtr->bFocusHex && (infoPtr->nCaretPos % 2 == 0))
|
|
|
|
{
|
|
|
|
memmove(infoPtr->pData + nCaretBytePos - 1,
|
|
|
|
infoPtr->pData + nCaretBytePos,
|
|
|
|
infoPtr->cbData - nCaretBytePos);
|
|
|
|
infoPtr->cbData--;
|
|
|
|
infoPtr->nCaretPos -= 2; /* backtrack two nibble */
|
|
|
|
}
|
|
|
|
else /* blank upper nibble */
|
|
|
|
{
|
|
|
|
infoPtr->pData[nCaretBytePos] &= 0x0f;
|
|
|
|
infoPtr->nCaretPos--; /* backtrack one nibble */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (infoPtr->bFocusHex && hexchar_to_byte(ch) == (BYTE)-1)
|
|
|
|
{
|
|
|
|
MessageBeep(MB_ICONWARNING);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((infoPtr->bInsert && (infoPtr->nCaretPos % 2 == 0)) || (nCaretBytePos >= infoPtr->cbData))
|
|
|
|
{
|
|
|
|
/* make room for another byte */
|
|
|
|
infoPtr->cbData++;
|
2017-08-02 14:35:42 +02:00
|
|
|
infoPtr->pData = heap_xrealloc(infoPtr->pData, infoPtr->cbData + 1);
|
|
|
|
|
2005-04-14 13:30:31 +02:00
|
|
|
/* move everything after caret up one byte */
|
|
|
|
memmove(infoPtr->pData + nCaretBytePos + 1,
|
|
|
|
infoPtr->pData + nCaretBytePos,
|
|
|
|
infoPtr->cbData - nCaretBytePos);
|
|
|
|
/* zero new byte */
|
|
|
|
infoPtr->pData[nCaretBytePos] = 0x0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* overwrite a byte */
|
|
|
|
|
|
|
|
assert(nCaretBytePos < infoPtr->cbData);
|
|
|
|
|
|
|
|
if (infoPtr->bFocusHex)
|
|
|
|
{
|
|
|
|
BYTE orig_byte = infoPtr->pData[nCaretBytePos];
|
|
|
|
BYTE digit = hexchar_to_byte(ch);
|
|
|
|
if (infoPtr->nCaretPos % 2) /* set low nibble */
|
|
|
|
infoPtr->pData[nCaretBytePos] = (orig_byte & 0xf0) | digit;
|
|
|
|
else /* set high nibble */
|
|
|
|
infoPtr->pData[nCaretBytePos] = (orig_byte & 0x0f) | digit << 4;
|
|
|
|
infoPtr->nCaretPos++; /* advance one nibble */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
infoPtr->pData[nCaretBytePos] = (BYTE)ch;
|
|
|
|
infoPtr->nCaretPos += 2; /* advance two nibbles */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HexEdit_UpdateScrollbars(infoPtr);
|
|
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
|
|
|
|
HexEdit_UpdateCaret(infoPtr);
|
|
|
|
HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_Destroy (HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
HWND hwnd = infoPtr->hwndSelf;
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(infoPtr->pData);
|
2005-04-14 13:30:31 +02:00
|
|
|
/* free info data */
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(infoPtr);
|
2011-04-16 10:07:21 +02:00
|
|
|
SetWindowLongPtrW(hwnd, 0, 0);
|
2005-04-14 13:30:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_GetFont (HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
return (LRESULT)infoPtr->hFont;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_KeyDown (HEXEDIT_INFO *infoPtr, DWORD key, DWORD flags)
|
|
|
|
{
|
|
|
|
INT nInc = (infoPtr->bFocusHex) ? 1 : 2;
|
|
|
|
SCROLLINFO si;
|
|
|
|
|
|
|
|
switch (key)
|
|
|
|
{
|
|
|
|
case VK_LEFT:
|
|
|
|
infoPtr->nCaretPos -= nInc;
|
|
|
|
if (infoPtr->nCaretPos < 0)
|
|
|
|
infoPtr->nCaretPos = 0;
|
|
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
|
|
infoPtr->nCaretPos += nInc;
|
|
|
|
if (infoPtr->nCaretPos > infoPtr->cbData*2)
|
|
|
|
infoPtr->nCaretPos = infoPtr->cbData*2;
|
|
|
|
break;
|
|
|
|
case VK_UP:
|
|
|
|
if ((infoPtr->nCaretPos - infoPtr->nBytesPerLine*2) >= 0)
|
|
|
|
infoPtr->nCaretPos -= infoPtr->nBytesPerLine*2;
|
|
|
|
break;
|
|
|
|
case VK_DOWN:
|
|
|
|
if ((infoPtr->nCaretPos + infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2)
|
|
|
|
infoPtr->nCaretPos += infoPtr->nBytesPerLine*2;
|
|
|
|
break;
|
|
|
|
case VK_HOME:
|
|
|
|
infoPtr->nCaretPos = 0;
|
|
|
|
break;
|
|
|
|
case VK_END:
|
|
|
|
infoPtr->nCaretPos = infoPtr->cbData*2;
|
|
|
|
break;
|
|
|
|
case VK_PRIOR: /* page up */
|
|
|
|
si.cbSize = sizeof(si);
|
|
|
|
si.fMask = SIF_PAGE;
|
|
|
|
GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
|
|
|
|
if ((infoPtr->nCaretPos - (INT)si.nPage*infoPtr->nBytesPerLine*2) >= 0)
|
|
|
|
infoPtr->nCaretPos -= si.nPage*infoPtr->nBytesPerLine*2;
|
|
|
|
else
|
|
|
|
infoPtr->nCaretPos = 0;
|
|
|
|
break;
|
|
|
|
case VK_NEXT: /* page down */
|
|
|
|
si.cbSize = sizeof(si);
|
|
|
|
si.fMask = SIF_PAGE;
|
|
|
|
GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
|
|
|
|
if ((infoPtr->nCaretPos + (INT)si.nPage*infoPtr->nBytesPerLine*2) <= infoPtr->cbData*2)
|
|
|
|
infoPtr->nCaretPos += si.nPage*infoPtr->nBytesPerLine*2;
|
|
|
|
else
|
|
|
|
infoPtr->nCaretPos = infoPtr->cbData*2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HexEdit_UpdateCaret(infoPtr);
|
|
|
|
HexEdit_EnsureVisible(infoPtr, infoPtr->nCaretPos);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_KillFocus (HEXEDIT_INFO *infoPtr, HWND receiveFocus)
|
|
|
|
{
|
|
|
|
infoPtr->bFocus = FALSE;
|
|
|
|
DestroyCaret();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_LButtonDown (HEXEDIT_INFO *infoPtr)
|
|
|
|
{
|
|
|
|
SetFocus(infoPtr->hwndSelf);
|
|
|
|
|
|
|
|
/* FIXME: hittest and set caret */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-16 10:07:03 +02:00
|
|
|
static inline LRESULT HexEdit_NCCreate (HWND hwnd, LPCREATESTRUCTW lpcs)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
|
|
|
HEXEDIT_INFO *infoPtr;
|
2011-04-16 10:07:03 +02:00
|
|
|
SetWindowLongW(hwnd, GWL_EXSTYLE,
|
|
|
|
lpcs->dwExStyle | WS_EX_CLIENTEDGE);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
/* allocate memory for info structure */
|
2017-08-02 14:35:42 +02:00
|
|
|
infoPtr = heap_xalloc(sizeof(HEXEDIT_INFO));
|
|
|
|
memset(infoPtr, 0, sizeof(HEXEDIT_INFO));
|
2011-04-16 10:07:03 +02:00
|
|
|
SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
/* initialize info structure */
|
|
|
|
infoPtr->nCaretPos = 0;
|
|
|
|
infoPtr->hwndSelf = hwnd;
|
|
|
|
infoPtr->nBytesPerLine = 2;
|
|
|
|
infoPtr->bFocusHex = TRUE;
|
|
|
|
infoPtr->bInsert = TRUE;
|
|
|
|
|
2011-04-16 10:07:03 +02:00
|
|
|
return DefWindowProcW(infoPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)lpcs);
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_SetFocus (HEXEDIT_INFO *infoPtr, HWND lostFocus)
|
|
|
|
{
|
|
|
|
infoPtr->bFocus = TRUE;
|
|
|
|
|
|
|
|
CreateCaret(infoPtr->hwndSelf, NULL, 1, infoPtr->nHeight);
|
|
|
|
HexEdit_UpdateCaret(infoPtr);
|
|
|
|
ShowCaret(infoPtr->hwndSelf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_SetFont (HEXEDIT_INFO *infoPtr, HFONT hFont, BOOL redraw)
|
|
|
|
{
|
2011-04-16 10:07:21 +02:00
|
|
|
TEXTMETRICW tm;
|
2005-04-14 13:30:31 +02:00
|
|
|
HDC hdc;
|
|
|
|
HFONT hOldFont = NULL;
|
|
|
|
LONG i;
|
|
|
|
RECT rcClient;
|
|
|
|
|
|
|
|
infoPtr->hFont = hFont;
|
|
|
|
|
|
|
|
hdc = GetDC(infoPtr->hwndSelf);
|
|
|
|
if (infoPtr->hFont)
|
|
|
|
hOldFont = SelectObject(hdc, infoPtr->hFont);
|
|
|
|
|
2011-04-16 10:07:21 +02:00
|
|
|
GetTextMetricsW(hdc, &tm);
|
2005-04-14 13:30:31 +02:00
|
|
|
infoPtr->nHeight = tm.tmHeight + tm.tmExternalLeading;
|
|
|
|
|
|
|
|
GetClientRect(infoPtr->hwndSelf, &rcClient);
|
|
|
|
|
|
|
|
for (i = 0; ; i++)
|
|
|
|
{
|
2017-08-02 14:35:42 +02:00
|
|
|
BYTE *pData = heap_xalloc(i);
|
|
|
|
WCHAR *lpszLine;
|
2005-04-14 13:30:31 +02:00
|
|
|
SIZE size;
|
2017-08-02 14:35:42 +02:00
|
|
|
|
|
|
|
memset(pData, 0, i);
|
2018-03-11 18:28:32 +01:00
|
|
|
lpszLine = HexEdit_GetLineText(0, pData, i, 0);
|
2011-04-16 10:07:21 +02:00
|
|
|
GetTextExtentPoint32W(hdc, lpszLine, lstrlenW(lpszLine), &size);
|
2017-08-02 14:35:42 +02:00
|
|
|
heap_free(lpszLine);
|
|
|
|
heap_free(pData);
|
2005-04-14 13:30:31 +02:00
|
|
|
if (size.cx > (rcClient.right - rcClient.left))
|
|
|
|
{
|
|
|
|
infoPtr->nBytesPerLine = i - 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-11 18:28:34 +01:00
|
|
|
HexEdit_UpdateScrollbars(infoPtr);
|
|
|
|
|
2005-04-14 13:30:31 +02:00
|
|
|
if (infoPtr->hFont)
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
|
|
|
|
|
|
if (redraw)
|
|
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline LRESULT
|
|
|
|
HexEdit_VScroll (HEXEDIT_INFO *infoPtr, INT action)
|
|
|
|
{
|
|
|
|
SCROLLINFO si;
|
|
|
|
|
|
|
|
/* get all scroll bar info */
|
|
|
|
si.cbSize = sizeof(si);
|
|
|
|
si.fMask = SIF_ALL;
|
|
|
|
GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
|
|
|
|
|
|
|
|
switch (LOWORD(action))
|
|
|
|
{
|
|
|
|
case SB_TOP: /* user pressed the home key */
|
|
|
|
si.nPos = si.nMin;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_BOTTOM: /* user pressed the end key */
|
|
|
|
si.nPos = si.nMax;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_LINEUP: /* user clicked the up arrow */
|
|
|
|
si.nPos -= 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_LINEDOWN: /* user clicked the down arrow */
|
|
|
|
si.nPos += 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_PAGEUP: /* user clicked the scroll bar above the scroll thumb */
|
|
|
|
si.nPos -= si.nPage;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_PAGEDOWN: /* user clicked the scroll bar below the scroll thumb */
|
|
|
|
si.nPos += si.nPage;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SB_THUMBTRACK: /* user dragged the scroll thumb */
|
|
|
|
si.nPos = si.nTrackPos;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the position and then retrieve it to let the system handle the
|
|
|
|
* cases where the position is out of range */
|
|
|
|
si.fMask = SIF_POS;
|
|
|
|
SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si, TRUE);
|
|
|
|
GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &si);
|
|
|
|
|
|
|
|
if (si.nPos != infoPtr->nScrollPos)
|
|
|
|
{
|
|
|
|
ScrollWindow(infoPtr->hwndSelf, 0, infoPtr->nHeight * (infoPtr->nScrollPos - si.nPos), NULL, NULL);
|
|
|
|
infoPtr->nScrollPos = si.nPos;
|
|
|
|
UpdateWindow(infoPtr->hwndSelf);
|
|
|
|
|
|
|
|
/* need to update caret position since it depends on the scroll position */
|
|
|
|
HexEdit_UpdateCaret(infoPtr);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static LRESULT WINAPI
|
|
|
|
HexEdit_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2011-04-16 10:07:03 +02:00
|
|
|
HEXEDIT_INFO *infoPtr = (HEXEDIT_INFO *)GetWindowLongPtrW(hwnd, 0);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
if (!infoPtr && (uMsg != WM_NCCREATE))
|
2011-04-16 10:07:03 +02:00
|
|
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
switch (uMsg)
|
|
|
|
{
|
|
|
|
case HEM_SETDATA:
|
|
|
|
return HexEdit_SetData (infoPtr, (INT)wParam, (const BYTE *)lParam);
|
|
|
|
|
|
|
|
case HEM_GETDATA:
|
|
|
|
return HexEdit_GetData (infoPtr, (INT)wParam, (BYTE *)lParam);
|
|
|
|
|
|
|
|
case WM_CHAR:
|
2011-04-16 10:07:03 +02:00
|
|
|
return HexEdit_Char (infoPtr, (WCHAR)wParam);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
return HexEdit_Destroy (infoPtr);
|
|
|
|
|
|
|
|
case WM_GETDLGCODE:
|
|
|
|
return DLGC_WANTCHARS | DLGC_WANTARROWS;
|
|
|
|
|
|
|
|
case WM_GETFONT:
|
|
|
|
return HexEdit_GetFont (infoPtr);
|
|
|
|
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
return HexEdit_KeyDown (infoPtr, wParam, lParam);
|
|
|
|
|
|
|
|
case WM_KILLFOCUS:
|
|
|
|
return HexEdit_KillFocus (infoPtr, (HWND)wParam);
|
|
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
return HexEdit_LButtonDown (infoPtr);
|
|
|
|
|
|
|
|
case WM_NCCREATE:
|
2011-04-16 10:07:03 +02:00
|
|
|
return HexEdit_NCCreate (hwnd, (LPCREATESTRUCTW)lParam);
|
2005-04-14 13:30:31 +02:00
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
HexEdit_Paint(infoPtr);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_SETFOCUS:
|
|
|
|
return HexEdit_SetFocus (infoPtr, (HWND)wParam);
|
|
|
|
|
|
|
|
case WM_SETFONT:
|
|
|
|
return HexEdit_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam));
|
|
|
|
|
|
|
|
case WM_VSCROLL:
|
|
|
|
return HexEdit_VScroll (infoPtr, (INT)wParam);
|
|
|
|
|
|
|
|
default:
|
2011-04-16 10:07:03 +02:00
|
|
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-10 22:00:47 +01:00
|
|
|
void HexEdit_Register(void)
|
2005-04-14 13:30:31 +02:00
|
|
|
{
|
2011-04-16 10:07:03 +02:00
|
|
|
WNDCLASSW wndClass;
|
2005-04-14 13:30:31 +02:00
|
|
|
|
2011-04-16 10:07:03 +02:00
|
|
|
ZeroMemory(&wndClass, sizeof(WNDCLASSW));
|
2005-04-14 13:30:31 +02:00
|
|
|
wndClass.style = 0;
|
|
|
|
wndClass.lpfnWndProc = HexEdit_WindowProc;
|
|
|
|
wndClass.cbClsExtra = 0;
|
|
|
|
wndClass.cbWndExtra = sizeof(HEXEDIT_INFO *);
|
2019-04-20 06:16:53 +02:00
|
|
|
wndClass.hCursor = LoadCursorW(0, (const WCHAR *)IDC_IBEAM);
|
2019-04-19 05:39:15 +02:00
|
|
|
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
2022-04-14 15:01:35 +02:00
|
|
|
wndClass.lpszClassName = L"HexEdit";
|
2005-04-14 13:30:31 +02:00
|
|
|
|
2011-04-16 10:07:03 +02:00
|
|
|
RegisterClassW(&wndClass);
|
2005-04-14 13:30:31 +02:00
|
|
|
}
|