2008-02-01 21:40:18 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS VGA Font Editor
|
|
|
|
* LICENSE: GNU General Public License Version 2.0 or any later version
|
|
|
|
* FILE: devutils/vgafontedit/fontboxeswnd.c
|
|
|
|
* PURPOSE: Implements the window showing the character boxes for a font
|
|
|
|
* COPYRIGHT: Copyright 2008 Colin Finck <mail@colinfinck.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
|
|
static const WCHAR szFontBoxesWndClass[] = L"VGAFontEditFontBoxesWndClass";
|
|
|
|
|
|
|
|
static VOID
|
|
|
|
DrawCharacterPixel(IN PAINTSTRUCT *ps, IN UINT uCharacter, IN UCHAR uRow, IN UCHAR uColumn, IN UCHAR uBit, IN COLORREF clBackground)
|
|
|
|
{
|
|
|
|
INT x;
|
|
|
|
INT y;
|
|
|
|
|
|
|
|
x = (uCharacter % 16) * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING) + 24 + uColumn;
|
|
|
|
y = (uCharacter / 16) * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING)+ 1 + CHARACTER_INFO_BOX_HEIGHT + 2 + uRow;
|
|
|
|
|
|
|
|
SetPixel( ps->hdc, x, y, (uBit ? 0 : clBackground) );
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
GetCharacterRect(IN UINT uFontRow, IN UINT uFontColumn, OUT LPRECT CharacterRect)
|
|
|
|
{
|
|
|
|
CharacterRect->left = uFontColumn * (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING);
|
|
|
|
CharacterRect->top = uFontRow * (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING);
|
|
|
|
CharacterRect->right = CharacterRect->left + CHARACTER_BOX_WIDTH;
|
|
|
|
CharacterRect->bottom = CharacterRect->top + CHARACTER_BOX_HEIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
__inline VOID
|
|
|
|
GetCharacterPosition(IN UINT uCharacter, OUT PUINT uFontRow, OUT PUINT uFontColumn)
|
|
|
|
{
|
|
|
|
*uFontRow = uCharacter / 16;
|
|
|
|
*uFontColumn = uCharacter % 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
static INT
|
|
|
|
FontBoxesHitTest(IN UINT xPos, IN UINT yPos, OUT LPRECT CharacterRect)
|
|
|
|
{
|
|
|
|
UINT uFontColumn;
|
|
|
|
UINT uFontRow;
|
|
|
|
|
|
|
|
uFontColumn = xPos / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING);
|
|
|
|
uFontRow = yPos / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING);
|
|
|
|
GetCharacterRect(uFontRow, uFontColumn, CharacterRect);
|
|
|
|
|
|
|
|
if(xPos > (UINT)CharacterRect->right || yPos > (UINT)CharacterRect->bottom)
|
|
|
|
// The user clicked on separator space, so return HITTEST_SEPARATOR
|
|
|
|
return HITTEST_SEPARATOR;
|
|
|
|
else
|
|
|
|
// Return the character number
|
|
|
|
return (uFontRow * 16 + uFontColumn);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID
|
|
|
|
SetSelectedCharacter(IN PFONT_WND_INFO Info, IN UINT uNewCharacter, OPTIONAL IN LPRECT NewCharacterRect)
|
|
|
|
{
|
|
|
|
LPRECT pCharacterRect;
|
|
|
|
RECT OldCharacterRect;
|
|
|
|
UINT uFontColumn;
|
|
|
|
UINT uFontRow;
|
|
|
|
|
|
|
|
// Remove the selection of the old character
|
|
|
|
GetCharacterPosition(Info->uSelectedCharacter, &uFontRow, &uFontColumn);
|
|
|
|
GetCharacterRect(uFontRow, uFontColumn, &OldCharacterRect);
|
|
|
|
InvalidateRect(Info->hFontBoxesWnd, &OldCharacterRect, FALSE);
|
|
|
|
|
|
|
|
// You may pass the RECT of the new character, otherwise we'll allocate memory for one and get it ourselves
|
|
|
|
if(NewCharacterRect)
|
|
|
|
pCharacterRect = NewCharacterRect;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GetCharacterPosition(uNewCharacter, &uFontRow, &uFontColumn);
|
|
|
|
pCharacterRect = (LPRECT) HeapAlloc( hProcessHeap, 0, sizeof(RECT) );
|
|
|
|
GetCharacterRect(uFontRow, uFontColumn, pCharacterRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select the new character
|
|
|
|
Info->uSelectedCharacter = uNewCharacter;
|
|
|
|
InvalidateRect(Info->hFontBoxesWnd, pCharacterRect, FALSE);
|
|
|
|
|
|
|
|
if(!NewCharacterRect)
|
|
|
|
HeapFree(hProcessHeap, 0, pCharacterRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VOID
|
|
|
|
DrawProc(IN PFONT_WND_INFO Info, IN PAINTSTRUCT* ps)
|
|
|
|
{
|
|
|
|
COLORREF clBackground;
|
|
|
|
HBRUSH hBrush = 0;
|
|
|
|
HBRUSH hOldBrush = 0;
|
|
|
|
HDC hBoxDC;
|
|
|
|
HFONT hFont;
|
|
|
|
HFONT hOldFont;
|
|
|
|
RECT CharacterRect;
|
|
|
|
UINT uFontColumn;
|
|
|
|
UINT uStartColumn;
|
|
|
|
UINT uEndColumn;
|
|
|
|
UINT uFontRow;
|
|
|
|
UINT uStartRow;
|
|
|
|
UINT uEndRow;
|
|
|
|
UINT uCharacter;
|
|
|
|
UCHAR uCharacterColumn;
|
|
|
|
UCHAR uCharacterRow;
|
|
|
|
UCHAR uBit;
|
|
|
|
WCHAR szInfoText[9];
|
2008-09-20 14:13:10 +00:00
|
|
|
HBITMAP hBitmapOld;
|
2008-02-01 21:40:18 +00:00
|
|
|
|
|
|
|
// Preparations
|
|
|
|
hBoxDC = CreateCompatibleDC(NULL);
|
2008-09-20 14:13:10 +00:00
|
|
|
hBitmapOld = SelectObject(hBoxDC, Info->MainWndInfo->hBoxBmp);
|
2008-02-01 21:40:18 +00:00
|
|
|
|
|
|
|
hFont = CreateFontW(13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Tahoma");
|
|
|
|
hOldFont = SelectObject(ps->hdc, hFont);
|
|
|
|
|
|
|
|
SetBkMode( ps->hdc, TRANSPARENT );
|
|
|
|
|
|
|
|
// What ranges do we have to draw?
|
|
|
|
uStartRow = ps->rcPaint.top / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING);
|
|
|
|
uEndRow = ps->rcPaint.bottom / (CHARACTER_BOX_HEIGHT + CHARACTER_BOX_PADDING);
|
|
|
|
uStartColumn = ps->rcPaint.left / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING);
|
|
|
|
uEndColumn = ps->rcPaint.right / (CHARACTER_BOX_WIDTH + CHARACTER_BOX_PADDING);
|
|
|
|
|
|
|
|
for(uFontRow = uStartRow; uFontRow <= uEndRow; uFontRow++)
|
|
|
|
{
|
|
|
|
for(uFontColumn = uStartColumn; uFontColumn <= uEndColumn; uFontColumn++)
|
|
|
|
{
|
|
|
|
GetCharacterRect(uFontRow, uFontColumn, &CharacterRect);
|
|
|
|
uCharacter = uFontRow * 16 + uFontColumn;
|
|
|
|
|
|
|
|
// Draw the Character Info Box (header)
|
|
|
|
BitBlt(ps->hdc,
|
|
|
|
CharacterRect.left,
|
|
|
|
CharacterRect.top,
|
|
|
|
CHARACTER_BOX_WIDTH,
|
|
|
|
CHARACTER_INFO_BOX_HEIGHT,
|
|
|
|
hBoxDC,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
SRCCOPY);
|
|
|
|
|
|
|
|
// Draw the header text
|
|
|
|
wsprintfW(szInfoText, L"%02u = %02X", uCharacter, uCharacter);
|
|
|
|
DrawTextW( ps->hdc, szInfoText, -1, &CharacterRect, DT_CENTER );
|
|
|
|
|
|
|
|
// Draw the Character Bitmap Box (rectangle with the actual character)
|
|
|
|
if(Info->uSelectedCharacter == uCharacter)
|
|
|
|
{
|
|
|
|
clBackground = RGB(255, 255, 0);
|
|
|
|
hBrush = CreateSolidBrush(clBackground);
|
|
|
|
hOldBrush = SelectObject(ps->hdc, hBrush);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
clBackground = RGB(255, 255, 255);
|
|
|
|
SelectObject( ps->hdc, GetStockObject(WHITE_BRUSH) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Rectangle(ps->hdc,
|
|
|
|
CharacterRect.left,
|
|
|
|
CharacterRect.top + CHARACTER_INFO_BOX_HEIGHT,
|
|
|
|
CharacterRect.right,
|
|
|
|
CharacterRect.bottom);
|
|
|
|
|
|
|
|
// Draw the actual character into the box
|
|
|
|
for(uCharacterRow = 0; uCharacterRow < 8; uCharacterRow++)
|
|
|
|
{
|
|
|
|
for(uCharacterColumn = 0; uCharacterColumn < 8; uCharacterColumn++)
|
|
|
|
{
|
|
|
|
uBit = Info->Font->Bits[uCharacter * 8 + uCharacterRow] << uCharacterColumn & 0x80;
|
|
|
|
DrawCharacterPixel(ps, uCharacter, uCharacterRow, uCharacterColumn, uBit, clBackground);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-20 14:13:10 +00:00
|
|
|
SelectObject(hBoxDC, hBitmapOld);
|
2008-02-01 21:40:18 +00:00
|
|
|
SelectObject(ps->hdc, hOldFont);
|
|
|
|
DeleteObject(hFont);
|
|
|
|
SelectObject(ps->hdc, hOldBrush);
|
|
|
|
DeleteObject(hBrush);
|
|
|
|
DeleteDC(hBoxDC);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
EditCurrentGlyph(PFONT_WND_INFO FontWndInfo)
|
|
|
|
{
|
|
|
|
PEDIT_GLYPH_INFO EditGlyphInfo;
|
|
|
|
|
2008-02-07 19:14:55 +00:00
|
|
|
// Has the window for this character already been opened?
|
|
|
|
EditGlyphInfo = FontWndInfo->FirstEditGlyphWnd;
|
|
|
|
|
|
|
|
while(EditGlyphInfo)
|
|
|
|
{
|
|
|
|
if(EditGlyphInfo->uCharacter == FontWndInfo->uSelectedCharacter)
|
|
|
|
{
|
|
|
|
// Yes, it has. Bring it to the front.
|
|
|
|
SetFocus(EditGlyphInfo->hSelf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EditGlyphInfo = EditGlyphInfo->NextEditGlyphWnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No. Then create a new one
|
2008-02-01 21:40:18 +00:00
|
|
|
EditGlyphInfo = (PEDIT_GLYPH_INFO) HeapAlloc( hProcessHeap, 0, sizeof(EDIT_GLYPH_INFO) );
|
|
|
|
EditGlyphInfo->FontWndInfo = FontWndInfo;
|
|
|
|
EditGlyphInfo->uCharacter = FontWndInfo->uSelectedCharacter;
|
|
|
|
RtlCopyMemory( EditGlyphInfo->CharacterBits, FontWndInfo->Font->Bits + FontWndInfo->uSelectedCharacter * 8, sizeof(EditGlyphInfo->CharacterBits) );
|
|
|
|
|
2008-02-07 19:14:55 +00:00
|
|
|
// Add the new window to the linked list
|
|
|
|
EditGlyphInfo->PrevEditGlyphWnd = FontWndInfo->LastEditGlyphWnd;
|
|
|
|
EditGlyphInfo->NextEditGlyphWnd = NULL;
|
|
|
|
|
|
|
|
if(FontWndInfo->LastEditGlyphWnd)
|
|
|
|
FontWndInfo->LastEditGlyphWnd->NextEditGlyphWnd = EditGlyphInfo;
|
|
|
|
else
|
|
|
|
FontWndInfo->FirstEditGlyphWnd = EditGlyphInfo;
|
|
|
|
|
|
|
|
FontWndInfo->LastEditGlyphWnd = EditGlyphInfo;
|
|
|
|
|
|
|
|
// Open the window as a modeless dialog, so people can edit several characters at the same time.
|
|
|
|
EditGlyphInfo->hSelf = CreateDialogParamW(hInstance, MAKEINTRESOURCEW(IDD_EDITGLYPH), FontWndInfo->hSelf, EditGlyphDlgProc, (LPARAM)EditGlyphInfo);
|
|
|
|
ShowWindow(EditGlyphInfo->hSelf, SW_SHOW);
|
2008-02-01 21:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
CreateFontBoxesWindow(IN PFONT_WND_INFO FontWndInfo)
|
|
|
|
{
|
|
|
|
FontWndInfo->hFontBoxesWnd = CreateWindowExW(0,
|
|
|
|
szFontBoxesWndClass,
|
|
|
|
0,
|
|
|
|
WS_CHILD | WS_VISIBLE,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
FontWndInfo->hSelf,
|
|
|
|
NULL,
|
|
|
|
hInstance,
|
|
|
|
FontWndInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT CALLBACK
|
|
|
|
FontBoxesWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
PFONT_WND_INFO Info;
|
|
|
|
|
|
|
|
Info = (PFONT_WND_INFO) GetWindowLongW(hwnd, GWLP_USERDATA);
|
|
|
|
|
|
|
|
if(Info || uMsg == WM_CREATE)
|
|
|
|
{
|
|
|
|
switch(uMsg)
|
|
|
|
{
|
|
|
|
case WM_CREATE:
|
|
|
|
Info = (PFONT_WND_INFO)( ( (LPCREATESTRUCT)lParam )->lpCreateParams );
|
|
|
|
SetWindowLongW(hwnd, GWLP_USERDATA, (LONG)Info);
|
|
|
|
|
|
|
|
// Set a fixed window size
|
|
|
|
SetWindowPos(hwnd, NULL, 0, 0, FONT_BOXES_WND_WIDTH, FONT_BOXES_WND_HEIGHT, SWP_NOZORDER | SWP_NOMOVE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
SetWindowLongW(hwnd, GWLP_USERDATA, 0);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
switch(wParam)
|
|
|
|
{
|
|
|
|
case VK_DOWN:
|
|
|
|
if(Info->uSelectedCharacter < 239)
|
|
|
|
SetSelectedCharacter(Info, Info->uSelectedCharacter + 16, NULL);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VK_LEFT:
|
|
|
|
if(Info->uSelectedCharacter)
|
|
|
|
SetSelectedCharacter(Info, Info->uSelectedCharacter - 1, NULL);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VK_RETURN:
|
|
|
|
EditCurrentGlyph(Info);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VK_RIGHT:
|
|
|
|
if(Info->uSelectedCharacter < 255)
|
|
|
|
SetSelectedCharacter(Info, Info->uSelectedCharacter + 1, NULL);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case VK_UP:
|
|
|
|
if(Info->uSelectedCharacter > 15)
|
|
|
|
SetSelectedCharacter(Info, Info->uSelectedCharacter - 16, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
|
|
{
|
|
|
|
EditCurrentGlyph(Info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
{
|
|
|
|
RECT CharacterRect;
|
|
|
|
INT iRet;
|
|
|
|
|
|
|
|
iRet = FontBoxesHitTest( GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &CharacterRect );
|
|
|
|
|
|
|
|
if(iRet >= 0)
|
|
|
|
SetSelectedCharacter( Info, (UINT)iRet, &CharacterRect );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
{
|
|
|
|
PAINTSTRUCT ps;
|
|
|
|
|
|
|
|
BeginPaint(hwnd, &ps);
|
|
|
|
DrawProc(Info, &ps);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
|
|
InitFontBoxesWndClass(VOID)
|
|
|
|
{
|
|
|
|
WNDCLASSW wc = {0,};
|
|
|
|
|
|
|
|
wc.lpfnWndProc = FontBoxesWndProc;
|
|
|
|
wc.hInstance = hInstance;
|
|
|
|
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
|
|
|
|
wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 );
|
|
|
|
wc.lpszClassName = szFontBoxesWndClass;
|
|
|
|
wc.style = CS_DBLCLKS;
|
|
|
|
|
|
|
|
return RegisterClassW(&wc) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
UnInitFontBoxesWndClass(VOID)
|
|
|
|
{
|
|
|
|
UnregisterClassW(szFontBoxesWndClass, hInstance);
|
|
|
|
}
|