mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
df54fb01fd
Based on KRosUser's vgafont.patch. CORE-19192
445 lines
15 KiB
C
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);
|
|
}
|