This program is a translation of MDIDEMO.C -- Multiple-Document Interface Demonstration © Charles Petzold, 1998, described and analysed in Chapter 19 of the book Programming Windows, 5th Edition.
Demonstrates the basics of writing an MDI application.
MDIDEMO supports two types of extremely simple document windows: one displays "Hello, World!" in the center of its client area, and the other displays a series of random rectangles. (In the source code listings and identifier names, these are referred to as the Hello document and the Rect document.) Different menus are associated with these two types of document windows. The document window that displays "Hello, World!" has a menu that allows you to change the color of the text.
' ========================================================================================
' MDIDEMO.BAS
' This program is a translation/adaptation of MDIDEMO.C -- Multiple-Document Interface
' Demonstration © Charles Petzold, 1998, described and analysed in Chapter 19 of the book
' Programming Windows, 5th Edition.
' Demonstrates the basics of writing an MDI application.
' ========================================================================================
#COMPILE EXE
#DIM ALL
#INCLUDE ONCE "windows.inc"
#RESOURCE RES, "mdidemo.res"
%IDM_FILE_NEWHELLO = 40001
%IDM_FILE_NEWRECT = 40002
%IDM_APP_EXIT = 40003
%IDM_FILE_CLOSE = 40004
%IDM_COLOR_BLACK = 40005
%IDM_COLOR_RED = 40006
%IDM_COLOR_GREEN = 40007
%IDM_COLOR_BLUE = 40008
%IDM_COLOR_WHITE = 40009
%IDM_WINDOW_CASCADE = 40010
%IDM_WINDOW_TILE = 40011
%IDM_WINDOW_ARRANGE = 40012
%IDM_WINDOW_CLOSEALL = 40013
%INIT_MENU_POS = 0
%HELLO_MENU_POS = 2
%RECT_MENU_POS = 1
%IDM_FIRSTCHILD = 50000
' structure for storing data unique to each Hello child window
TYPE HELLODATA
iColor AS DWORD
clrText AS DWORD
END TYPE
' structure for storing data unique to each Rect child window
TYPE RECTDATA
cxClient AS INTEGER
cyClient AS INTEGER
END TYPE
GLOBAL szAppName AS ASCIIZ * 256
GLOBAL szFrameClass AS ASCIIZ * 256
GLOBAL szHelloClass AS ASCIIZ * 256
GLOBAL szRectClass AS ASCIIZ * 256
GLOBAL hInst AS DWORD
GLOBAL hMenuInit AS DWORD
GLOBAL hMenuHello AS DWORD
GLOBAL hMenuRect AS DWORD
GLOBAL hMenuInitWindow AS DWORD
GLOBAL hMenuHelloWindow AS DWORD
GLOBAL hMenuRectWindow AS DWORD
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, _
BYVAL pszCmdLine AS WSTRINGZ PTR, BYVAL iCmdShow AS LONG) AS LONG
LOCAL hAccel AS DWORD
LOCAL hwndFrame AS DWORD
LOCAL hwndClient AS DWORD
LOCAL wcex AS WNDCLASSEX
LOCAL szCaption AS ASCIIZ * 256
szAppName = "MDIDemo"
szFrameClass = "MdiFrame"
szHelloClass = "MdiHelloChild"
szRectClass = "MdiRectChild"
' Register the frame window class
wcex.cbSize = SIZEOF(wcex)
wcex.style = %CS_HREDRAW OR %CS_VREDRAW
wcex.lpfnWndProc = CODEPTR(FrameWndProc)
wcex.cbClsExtra = 0
wcex.cbWndExtra = 0
wcex.hInstance = hInstance
wcex.hIcon = LoadIcon(%NULL, BYVAL %IDI_APPLICATION)
wcex.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
wcex.hbrBackground = %COLOR_APPWORKSPACE + 1
wcex.lpszMenuName = %NULL
wcex.lpszClassName = VARPTR(szFrameClass)
IF ISFALSE RegisterClassEx(wcex) THEN
FUNCTION = %TRUE
EXIT FUNCTION
END IF
' Register the Hello child window class
wcex.cbSize = SIZEOF(wcex)
wcex.style = %CS_HREDRAW OR %CS_VREDRAW
wcex.lpfnWndProc = CODEPTR(HelloWndProc)
wcex.cbClsExtra = 0
wcex.cbWndExtra = 4
wcex.hInstance = hInstance
wcex.hIcon = LoadIcon(%NULL, BYVAL %IDI_APPLICATION)
wcex.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
wcex.hbrBackground = GetStockObject(%WHITE_BRUSH)
wcex.lpszMenuName = %NULL
wcex.lpszClassName = VARPTR(szHelloClass)
IF ISFALSE RegisterClassEx(wcex) THEN
FUNCTION = %TRUE
EXIT FUNCTION
END IF
' Register the Rect child window class
wcex.cbSize = SIZEOF(wcex)
wcex.style = %CS_HREDRAW OR %CS_VREDRAW
wcex.lpfnWndProc = CODEPTR(RectWndProc)
wcex.cbClsExtra = 0
wcex.cbWndExtra = 4
wcex.hInstance = hInstance
wcex.hIcon = LoadIcon(%NULL, BYVAL %IDI_APPLICATION)
wcex.hCursor = LoadCursor(%NULL, BYVAL %IDC_ARROW)
wcex.hbrBackground = GetStockObject(%WHITE_BRUSH)
wcex.lpszMenuName = %NULL
wcex.lpszClassName = VARPTR(szRectClass)
IF ISFALSE RegisterClassEx(wcex) THEN
FUNCTION = %TRUE
EXIT FUNCTION
END IF
' Obtain handles to three possible menus & submenus
hMenuInit = LoadMenu(hInstance, "MdiMenuInit")
hMenuHello = LoadMenu(hInstance, "MdiMenuHello")
hMenuRect = LoadMenu(hInstance, "MdiMenuRect")
hMenuInitWindow = GetSubMenu(hMenuInit, %INIT_MENU_POS)
hMenuHelloWindow = GetSubMenu(hMenuHello, %HELLO_MENU_POS)
hMenuRectWindow = GetSubMenu(hMenuRect, %RECT_MENU_POS)
' Load accelerator table
hAccel = LoadAccelerators(hInstance, szAppName)
' Create the frame window
szCaption = "MDI Demonstration"
hWndFrame = CreateWindowEx(%WS_EX_CONTROLPARENT, _ ' extended style
szFrameClass, _ ' window class name
szCaption, _ ' window caption
%WS_OVERLAPPEDWINDOW, _ ' window style
%CW_USEDEFAULT, _ ' initial x position
%CW_USEDEFAULT, _ ' initial y position
%CW_USEDEFAULT, _ ' initial x size
%CW_USEDEFAULT, _ ' initial y size
%NULL, _ ' parent window handle
hMenuInit, _ ' window menu handle
hInstance, _ ' program instance handle
BYVAL %NULL) ' creation parameters
hwndClient = GetWindow(hwndFrame, %GW_CHILD)
ShowWindow hwndFrame, iCmdShow
UpdateWindow hwndFrame
' Enter the modified message loop
LOCAL uMsg AS tagMsg
WHILE GetMessage(uMsg, %NULL, 0, 0)
IF ISFALSE TranslateMDISysAccel(hwndClient, uMsg) THEN
IF ISFALSE TranslateAccelerator(hwndFrame, hAccel, uMsg) THEN
TranslateMessage uMsg
DispatchMessage uMsg
END IF
END IF
WEND
' Clean up by deleting unattached menus
DestroyMenu hMenuHello
DestroyMenu hMenuRect
FUNCTION = uMsg.wParam
END FUNCTION
' ========================================================================================
' ========================================================================================
' Frame dialog callback.
' ========================================================================================
FUNCTION FrameWndProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
STATIC hwndClient AS DWORD
LOCAL clientcreate AS CLIENTCREATESTRUCT
LOCAL hwndChild AS DWORD
LOCAL mdicreate AS MDICREATESTRUCT
LOCAL szTitle AS ASCIIZ * 256
SELECT CASE uMsg
CASE %WM_CREATE ' Create the client window
clientcreate.hWindowMenu = hMenuInitWindow
clientcreate.idFirstChild = %IDM_FIRSTCHILD
hwndClient = CreateWindowEx(0, "MDICLIENT", BYVAL %NULL, _
%WS_CHILD OR %WS_CLIPCHILDREN OR %WS_VISIBLE, _
0, 0, 0, 0, hwnd, 1, hInst, _
BYVAL VARPTR(clientcreate))
EXIT FUNCTION
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDM_FILE_NEWHELLO ' Create a Hello child window
szTitle = "Hello"
mdicreate.szClass = VARPTR(szHelloClass)
mdicreate.szTitle = VARPTR(szTitle)
mdicreate.hOwner = hInst
mdicreate.x = %CW_USEDEFAULT
mdicreate.y = %CW_USEDEFAULT
mdicreate.cx = %CW_USEDEFAULT
mdicreate.cy = %CW_USEDEFAULT
mdicreate.style = 0
mdicreate.lParam = 0
hwndChild = SendMessage(hwndClient, %WM_MDICREATE, 0, VARPTR(mdicreate))
EXIT FUNCTION
CASE %IDM_FILE_NEWRECT ' Create a Rect child window
szTitle = "Rectangles"
mdicreate.szClass = VARPTR(szRectClass)
mdicreate.szTitle = VARPTR(SzTitle)
mdicreate.hOwner = hInst
mdicreate.x = %CW_USEDEFAULT
mdicreate.y = %CW_USEDEFAULT
mdicreate.cx = %CW_USEDEFAULT
mdicreate.cy = %CW_USEDEFAULT
mdicreate.style = 0
mdicreate.lParam = 0
hwndChild = SendMessage(hwndClient, %WM_MDICREATE, 0, VARPTR(mdicreate))
EXIT FUNCTION
CASE %IDM_FILE_CLOSE ' Close the active window
hwndChild = SendMessage(hwndClient, %WM_MDIGETACTIVE, 0, 0)
IF SendMessage(hwndChild, %WM_QUERYENDSESSION, 0, 0) THEN
SendMessage hwndClient, %WM_MDIDESTROY, hwndChild, 0
END IF
EXIT FUNCTION
CASE %IDM_APP_EXIT ' Exit the program
SendMessage hwnd, %WM_CLOSE, 0, 0
EXIT FUNCTION
' // messages for arranging windows
CASE %IDM_WINDOW_TILE
SendMessage hwndClient, %WM_MDITILE, 0, 0
EXIT FUNCTION
CASE %IDM_WINDOW_CASCADE
SendMessage hwndClient, %WM_MDICASCADE, 0, 0
EXIT FUNCTION
CASE %IDM_WINDOW_ARRANGE
SendMessage hwndClient, %WM_MDIICONARRANGE, 0, 0
EXIT FUNCTION
CASE %IDM_WINDOW_CLOSEALL ' Attempt to close all children
EnumChildWindows hwndClient, CODEPTR(CloseEnumProc), 0
EXIT FUNCTION
CASE ELSE ' Pass to active child...
hwndChild = SendMessage (hwndClient, %WM_MDIGETACTIVE, 0, 0)
IF IsWindow(hwndChild) THEN
SendMessage hwndChild, %WM_COMMAND, wParam, lParam
END IF
' ...and fall through DefFrameProc
END SELECT
CASE %WM_QUERYENDSESSION, %WM_CLOSE ' Attempt to close all children
SendMessage hwnd, %WM_COMMAND, %IDM_WINDOW_CLOSEALL, 0
IF GetWindow(hwndClient, %GW_CHILD) <> %NULL THEN
EXIT FUNCTION
END IF
' Fall through DefFrameProc
CASE %WM_DESTROY
PostQuitMessage 0
EXIT FUNCTION
END SELECT
FUNCTION = DefFrameProc(hwnd, hwndClient, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
FUNCTION CloseEnumProc (BYVAL hwnd AS DWORD, BYVAL lParam AS LONG) AS LONG
IF GetWindow(hwnd, %GW_OWNER) THEN
FUNCTION = %TRUE
EXIT FUNCTION
END IF
SendMessage GetParent(hwnd), %WM_MDIRESTORE, hwnd, 0
IF ISFALSE SendMessage(hwnd, %WM_QUERYENDSESSION, 0, 0) THEN
FUNCTION = %TRUE
EXIT FUNCTION
END IF
SendMessage GetParent(hwnd), %WM_MDIDESTROY, hwnd, 0
FUNCTION = %TRUE
END FUNCTION
' ========================================================================================
' ========================================================================================
FUNCTION HelloWndProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
DIM clrTextArray(4) AS STATIC DWORD
STATIC hwndClient AS DWORD
STATIC hwndFrame AS DWORD
LOCAL hdc AS DWORD
LOCAL hMenu AS DWORD
LOCAL pHelloData AS HELLODATA PTR
LOCAL ps AS PAINTSTRUCT
LOCAL rc AS RECT
SELECT CASE uMsg
CASE %WM_CREATE
clrTextArray(0) = RGB(0, 0, 0)
clrTextArray(1) = RGB(255, 0, 0)
clrTextArray(2) = RGB(0, 255, 0)
clrTextArray(3) = RGB(0, 0, 255)
clrTextArray(4) = RGB(255, 255, 255)
' Allocate memory for window private data
pHelloData = HeapAlloc(GetprocessHeap, %HEAP_ZERO_MEMORY, SIZEOF(HELLODATA))
@pHelloData.iColor = %IDM_COLOR_BLACK
@pHelloData.clrText = RGB(0, 0, 0)
SetWindowLong hwnd, 0, pHelloData
' Save some window handles
hwndClient = GetParent(hwnd)
hwndFrame = GetParent(hwndClient)
EXIT FUNCTION
CASE %WM_COMMAND
SELECT CASE LO(WORD, wParam)
CASE %IDM_COLOR_BLACK, %IDM_COLOR_RED, %IDM_COLOR_GREEN, _
%IDM_COLOR_BLUE, %IDM_COLOR_WHITE
' Change the text color
pHelloData = GetWindowLong (hwnd, 0)
hMenu = GetMenu(hwndFrame)
CheckMenuItem hMenu, @pHelloData.iColor, %MF_UNCHECKED
@pHelloData.iColor = wParam
CheckMenuItem hMenu, @pHelloData.iColor, %MF_CHECKED
@pHelloData.clrText = clrTextArray(wParam - %IDM_COLOR_BLACK)
InvalidateRect hwnd, BYVAL %NULL, %FALSE
END SELECT
EXIT FUNCTION
CASE %WM_PAINT
' Paint the window
hdc = BeginPaint(hwnd, ps)
pHelloData = GetWindowLong (hwnd, 0)
SetTextColor hdc, @pHelloData.clrText
GetClientRect hwnd, rc
DrawText hdc, "Hello, World!", -1, rc, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
EndPaint hwnd, ps
EXIT FUNCTION
CASE %WM_MDIACTIVATE
' Set the Hello menu if gaining focus
IF lParam = hwnd THEN
SendMessage hWndClient, %WM_MDISETMENU, hMenuHello, hMenuHelloWindow
END IF
' Check or uncheck menu item
pHelloData = GetWindowLong(hwnd, 0)
CheckMenuItem hMenuHello, @pHelloData.iColor, _
IIF&(lParam = hwnd, %MF_CHECKED, %MF_UNCHECKED)
' Set the Init menu if losing focus
IF lParam <> hwnd THEN
SendMessage hwndCLient, %WM_MDISETMENU, hMenuInit, hMenuInitWindow
END IF
DrawMenuBar hwndFrame
EXIT FUNCTION
CASE %WM_QUERYENDSESSION, %WM_CLOSE
IF MessageBox(hwnd, "OK to close window?", "Hello", %MB_ICONQUESTION OR %MB_OKCANCEL) <> %IDOK THEN
EXIT FUNCTION
END IF
' Fall through DefMDIChildProc
CASE %WM_DESTROY
pHelloData = GetWindowLong (hwnd, 0)
HeapFree GetProcessHeap, 0, pHelloData
EXIT FUNCTION
END SELECT
' Pass unprocessed message to DefMDIChildProc
FUNCTION = DefMDIChildProc(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================
' ========================================================================================
FUNCTION RectWndProc (BYVAL hwnd AS DWORD, BYVAL uMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
STATIC hwndClient AS DWORD
STATIC hwndFrame AS DWORD
LOCAL hBrush AS DWORD
LOCAL hdc AS DWORD
LOCAL pRectData AS RECTDATA PTR
LOCAL ps AS PAINTSTRUCT
LOCAL xLeft AS LONG
LOCAL xRight AS LONG
LOCAL yTop AS LONG
LOCAL yBottom AS LONG
LOCAL nRed AS INTEGER
LOCAL nGreen AS INTEGER
LOCAL nBlue AS INTEGER
SELECT CASE uMsg
CASE %WM_CREATE
' Allocate memory for window private data
pRectData = HeapAlloc(GetProcessHeap, %HEAP_ZERO_MEMORY, SIZEOF(RECTDATA))
SetWindowLong hwnd, 0, pRectData
' Start the timer going
SetTimer hwnd, 1, 250, %NULL
' Save some window handles
hwndClient = GetParent(hwnd)
hwndFrame = GetParent(hwndClient)
EXIT FUNCTION
CASE %WM_SIZE ' // If not minimized, save the window size
IF wParam <> %SIZE_MINIMIZED THEN
pRectData = GetWindowLong(hwnd, 0)
@pRectData.cxClient = LO(WORD, lParam)
@pRectData.cyClient = HI(WORD, lParam)
END IF
' %WM_SIZE must be processed by DefMDIChildProc
CASE %WM_TIMER ' // Display a random rectangle
pRectData = GetWindowLong(hwnd, 0)
xLeft = RND * @pRectData.cxClient
xRight = RND * @pRectData.cxClient
yTop = RND * @pRectData.cyClient
yBottom = RND * @pRectData.cyClient
nRed = RND * 255
nGreen = RND * 255
nBlue = RND * 255
hdc = GetDC(hwnd)
hBrush = CreateSolidBrush(RGB(nRed, nGreen, nBlue))
SelectObject hdc, hBrush
Rectangle hdc, min (xLeft, xRight), min (yTop, yBottom), _
MAX&(xLeft, xRight), MAX&(yTop, yBottom)
ReleaseDC hwnd, hdc
DeleteObject hBrush
EXIT FUNCTION
CASE %WM_PAINT ' Clear the window
InvalidateRect hwnd, BYVAL %NULL, %TRUE
hdc = BeginPaint(hwnd, ps)
EndPaint hwnd, ps
EXIT FUNCTION
CASE %WM_MDIACTIVATE '/ Set the appropriate menu
IF lParam = hwnd THEN
SendMessage hwndClient, %WM_MDISETMENU, hMenuRect, hMenuRectWindow
ELSE
SendMessage hwndClient, %WM_MDISETMENU, hMenuInit, hMenuInitWindow
END IF
DrawMenuBar hwndFrame
EXIT FUNCTION
CASE %WM_DESTROY
pRectData = GetWindowLong(hwnd, 0)
HeapFree GetProcessHeap, 0, pRectData
KillTimer hwnd, 1
EXIT FUNCTION
END SELECT
' Pass unprocessed message to DefMDIChildProc
FUNCTION = DefMDIChildProc(hwnd, uMsg, wParam, lParam)
END FUNCTION
' ========================================================================================