reactos/modules/rosapps/applications/devutils/vgafontedit/fontwnd.c
Katayama Hirofumi MZ df54fb01fd [VGAFONTEDIT] Fix exception on New
Based on KRosUser's vgafont.patch. CORE-19192
2023-11-23 08:34:56 +09:00

445 lines
15 KiB
C

/*
* PROJECT: ReactOS VGA Font Editor
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Implements the MDI child window for a font
* COPYRIGHT: Copyright 2008 Colin Finck (colin@reactos.org)
*/
#include "precomp.h"
static const WCHAR szFontWndClass[] = L"VGAFontEditFontWndClass";
static BOOL
InitFont(IN PFONT_WND_INFO Info)
{
Info->Font = (PBITMAP_FONT) HeapAlloc( hProcessHeap, 0, sizeof(BITMAP_FONT) );
if(Info->OpenInfo->bCreateNew)
{
ZeroMemory( Info->Font, sizeof(BITMAP_FONT) );
return TRUE;
}
else
{
// Load a font
BOOL bRet = FALSE;
DWORD dwTemp;
HANDLE hFile;
hFile = CreateFileW(Info->OpenInfo->pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
LocalizedError( IDS_OPENERROR, GetLastError() );
return FALSE;
}
// Let's first check the file size to determine the file type
dwTemp = GetFileSize(hFile, NULL);
switch(dwTemp)
{
case 2048:
// It should be a binary font file
Info->OpenInfo->bBinaryFileOpened = TRUE;
if( ReadFile(hFile, Info->Font, sizeof(BITMAP_FONT), &dwTemp, NULL) )
bRet = TRUE;
else
LocalizedError( IDS_READERROR, GetLastError() );
break;
case 2052:
{
PSF1_HEADER Header;
// Probably it's a PSFv1 file, check the header to make sure
if( !ReadFile(hFile, &Header, sizeof(PSF1_HEADER) , &dwTemp, NULL) )
{
LocalizedError( IDS_READERROR, GetLastError() );
break;
}
else
{
if(Header.uMagic[0] == PSF1_MAGIC0 && Header.uMagic[1] == PSF1_MAGIC1)
{
// Yes, it is a PSFv1 file.
// Check the mode and character size. We only support 8x8 fonts with no special mode.
if(Header.uCharSize == 8 && Header.uMode == 0)
{
// Perfect! The file pointer is already set correctly, so we can just read the font bitmap now.
if( ReadFile(hFile, Info->Font, sizeof(BITMAP_FONT), &dwTemp, NULL) )
bRet = TRUE;
else
LocalizedError( IDS_READERROR, GetLastError() );
}
else
LocalizedError(IDS_UNSUPPORTEDPSF);
break;
}
// Fall through if the magic numbers aren't there
}
}
default:
LocalizedError(IDS_UNSUPPORTEDFORMAT);
}
CloseHandle(hFile);
return bRet;
}
}
static LRESULT CALLBACK
FontWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PFONT_WND_INFO Info;
Info = (PFONT_WND_INFO) GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if(Info || uMsg == WM_CREATE)
{
switch(uMsg)
{
case WM_CHILDACTIVATE:
Info->MainWndInfo->CurrentFontWnd = Info;
SetToolbarFileButtonState(Info->MainWndInfo, TRUE);
SetPasteButtonState(Info->MainWndInfo);
break;
case WM_CREATE:
Info = (PFONT_WND_INFO)( ( (LPMDICREATESTRUCT) ( (LPCREATESTRUCT)lParam )->lpCreateParams )->lParam );
Info->hSelf = hwnd;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)Info);
CreateFontBoxesWindow(Info);
return 0;
case WM_USER_APPCLOSE:
case WM_CLOSE:
// The user has to close all open edit dialogs first
if(Info->FirstEditGlyphWnd)
{
PWSTR pszMessage;
AllocAndLoadString(&pszMessage, IDS_CLOSEEDIT);
MessageBoxW(hwnd, pszMessage, szAppName, MB_OK | MB_ICONEXCLAMATION);
HeapFree(hProcessHeap, 0, pszMessage);
return 0;
}
// Prompt if the current file has been modified
if(Info->OpenInfo->bModified)
{
INT nMsgBoxResult;
PWSTR pszPrompt;
WCHAR szFile[MAX_PATH];
GetWindowTextW(hwnd, szFile, MAX_PATH);
LoadAndFormatString(IDS_SAVEPROMPT, &pszPrompt, szFile);
nMsgBoxResult = MessageBoxW(hwnd, pszPrompt, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION);
LocalFree(pszPrompt);
switch(nMsgBoxResult)
{
case IDYES:
DoFileSave(Info->MainWndInfo, FALSE);
break;
case IDCANCEL:
// 0 = Stop the process of closing the windows (same value for both WM_CLOSE and WM_USER_APPCLOSE)
return 0;
// IDNO is handled automatically
}
}
// If there is another child, it will undo the following actions through its WM_CHILDACTIVATE handler.
// Otherwise CurrentFontWnd will stay NULL, so the main window knows that no more childs are opened.
Info->MainWndInfo->CurrentFontWnd = NULL;
SetToolbarFileButtonState(Info->MainWndInfo, FALSE);
SetPasteButtonState(Info->MainWndInfo);
if(uMsg == WM_USER_APPCLOSE)
{
// First do the tasks we would do for a normal WM_CLOSE message, then return the value for WM_USER_APPCLOSE
// Anything other than 0 indicates that the application shall continue closing the windows
DefMDIChildProcW(hwnd, WM_CLOSE, 0, 0);
return 1;
}
break;
case WM_DESTROY:
// Remove the window from the linked list
if(Info->PrevFontWnd)
Info->PrevFontWnd->NextFontWnd = Info->NextFontWnd;
else
Info->MainWndInfo->FirstFontWnd = Info->NextFontWnd;
if(Info->NextFontWnd)
Info->NextFontWnd->PrevFontWnd = Info->PrevFontWnd;
else
Info->MainWndInfo->LastFontWnd = Info->PrevFontWnd;
// Free memory
if(Info->Font)
HeapFree(hProcessHeap, 0, Info->Font);
if(Info->OpenInfo->pszFileName)
HeapFree(hProcessHeap, 0, Info->OpenInfo->pszFileName);
HeapFree(hProcessHeap, 0, Info->OpenInfo);
HeapFree(hProcessHeap, 0, Info);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
return 0;
case WM_SETFOCUS:
// Set the keyboard focus to the FontBoxes window every time the Font window gets the focus
SetFocus(Info->hFontBoxesWnd);
break;
case WM_SIZE:
{
INT nHeight = HIWORD(lParam);
INT nWidth = LOWORD(lParam);
POINT pt;
RECT WndRect;
// This ugly workaround is necessary for not setting either the Height or the Width of the window with SetWindowPos
GetWindowRect(Info->hFontBoxesWnd, &WndRect);
pt.x = WndRect.left;
pt.y = WndRect.top;
ScreenToClient(hwnd, &pt);
if(nHeight < FONT_BOXES_WND_HEIGHT)
{
SCROLLINFO si;
// Set the vertical scroll bar
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = FONT_BOXES_WND_HEIGHT;
si.nPage = nHeight;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
}
else
{
ShowScrollBar(hwnd, SB_VERT, FALSE);
// Store the new y coordinate in pt.y as well (needed for the SetWindowPos call for setting a new x coordinate)
pt.y = nHeight / 2 - FONT_BOXES_WND_HEIGHT / 2;
SetWindowPos(Info->hFontBoxesWnd,
NULL,
pt.x,
pt.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER);
}
if(nWidth < FONT_BOXES_WND_WIDTH)
{
SCROLLINFO si;
// Set the horizontal scroll bar
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = FONT_BOXES_WND_WIDTH;
si.nPage = nWidth;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
}
else
{
ShowScrollBar(hwnd, SB_HORZ, FALSE);
SetWindowPos(Info->hFontBoxesWnd,
NULL,
nWidth / 2 - FONT_BOXES_WND_WIDTH / 2,
pt.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER);
}
// We have to call DefMDIChildProcW here as well, otherwise we won't get the Minimize/Maximize/Close buttons for a maximized MDI child.
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
{
INT nBar;
INT nOrgPos;
SCROLLINFO si;
if(uMsg == WM_HSCROLL)
nBar = SB_HORZ;
else
nBar = SB_VERT;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, nBar, &si);
nOrgPos = si.nPos;
switch( LOWORD(wParam) )
{
// Constant is the same as SB_LEFT for WM_HSCROLL
case SB_TOP:
si.nPos = si.nMin;
break;
// Constant is the same as SB_RIGHT for WM_HSCROLL
case SB_BOTTOM:
si.nPos = si.nMax;
break;
// Constant is the same as SB_LINELEFT for WM_HSCROLL
case SB_LINEUP:
si.nPos -= 20;
break;
// Constant is the same as SB_LINERIGHT for WM_HSCROLL
case SB_LINEDOWN:
si.nPos += 20;
break;
// Constant is the same as SB_PAGELEFT for WM_HSCROLL
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
// Constant is the same as SB_PAGERIGHT for WM_HSCROLL
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hwnd, nBar, &si, TRUE);
GetScrollInfo(hwnd, nBar, &si);
if(si.nPos != nOrgPos)
{
// This ugly workaround is necessary for not setting the x coordinate
POINT pt;
RECT WndRect;
GetWindowRect(Info->hFontBoxesWnd, &WndRect);
pt.x = WndRect.left;
pt.y = WndRect.top;
ScreenToClient(hwnd, &pt);
if(uMsg == WM_HSCROLL)
SetWindowPos(Info->hFontBoxesWnd, NULL, -si.nPos, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
else
SetWindowPos(Info->hFontBoxesWnd, NULL, pt.x, -si.nPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
return 0;
}
}
}
return DefMDIChildProcW(hwnd, uMsg, wParam, lParam);
}
BOOL
CreateFontWindow(IN PMAIN_WND_INFO MainWndInfo, IN PFONT_OPEN_INFO OpenInfo)
{
HWND hFontWnd;
PFONT_WND_INFO Info;
Info = (PFONT_WND_INFO) HeapAlloc( hProcessHeap, HEAP_ZERO_MEMORY, sizeof(FONT_WND_INFO) );
if(Info)
{
Info->MainWndInfo = MainWndInfo;
Info->OpenInfo = OpenInfo;
if( InitFont(Info) )
{
PWSTR pch, pszWindowTitle;
if(OpenInfo->pszFileName)
{
pch = wcsrchr(OpenInfo->pszFileName, '\\');
pszWindowTitle = (pch ? (pch + 1) : OpenInfo->pszFileName);
}
else
{
LoadAndFormatString(IDS_DOCNAME, &pszWindowTitle, ++MainWndInfo->uDocumentCounter);
}
hFontWnd = CreateMDIWindowW( szFontWndClass,
pszWindowTitle,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
FONT_WND_MIN_WIDTH,
FONT_WND_MIN_HEIGHT,
MainWndInfo->hMdiClient,
hInstance,
(LPARAM)Info );
if(!OpenInfo->pszFileName)
LocalFree(pszWindowTitle);
if(hFontWnd)
{
// Add the new window to the linked list
Info->PrevFontWnd = Info->MainWndInfo->LastFontWnd;
if(Info->MainWndInfo->LastFontWnd)
Info->MainWndInfo->LastFontWnd->NextFontWnd = Info;
else
Info->MainWndInfo->FirstFontWnd = Info;
Info->MainWndInfo->LastFontWnd = Info;
return TRUE;
}
}
HeapFree(hProcessHeap, 0, Info);
}
return FALSE;
}
BOOL
InitFontWndClass(VOID)
{
WNDCLASSW wc = {0,};
wc.lpfnWndProc = FontWndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hIcon = LoadIconW( hInstance, MAKEINTRESOURCEW(IDI_DOC) );
wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 );
wc.lpszClassName = szFontWndClass;
return RegisterClassW(&wc) != 0;
}
VOID
UnInitFontWndClass(VOID)
{
UnregisterClassW(szFontWndClass, hInstance);
}