
;	Test of a MDI pgm before implementing MDI in SWE 
;	------------------------------------------------

;	MDIDemo: A Multiple - Document Interface Demo based on Petzold

;	Ron Thomas Ron_Thom@Compuserve.com 1/4/00	
;----------------------------------------------------

.386				; 32 bit when .386 appears before .MODEL
.MODEL FLAT,STDCALL

include windows.inc

include user32.inc
include kernel32.inc
include gdi32.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib


WinMain		PROTO :DWORD, :DWORD, :DWORD, :SDWORD     	

CloseEnumProc	PROTO :DWORD, :DWORD 	
FrameWndProc	PROTO :DWORD, :DWORD, :DWORD, :DWORD
HelloWndProc	PROTO :DWORD, :DWORD, :DWORD, :DWORD
RectWndProc	PROTO :DWORD, :DWORD, :DWORD, :DWORD
	
.data

INIT_MENU_POS	EQU	0
HELLO_MENU_POS	EQU	2
RECT_MENU_POS	EQU	1

IDM_FIRSTCHILD		EQU	50000		; Has to be greater than all other menu ID's

IDM_FILE_NEWHELLO	EQU	40001
IDM_FILE_NEWRECT	EQU	40002
IDM_APP_EXIT		EQU	40003
IDM_FILE_CLOSE		EQU	40004
IDM_COLOR_BLACK		EQU	40005
IDM_COLOR_RED		EQU	40006
IDM_COLOR_GREEN		EQU	40007
IDM_COLOR_BLUE		EQU	40008
IDM_COLOR_WHITE		EQU	40009
IDM_WINDOW_CASCADE	EQU	40010
IDM_WINDOW_TILE		EQU	40011
IDM_WINDOW_ARRANGE	EQU	40012
IDM_WINDOW_CLOSEALL	EQU	40013

random_init	DD	1010111000110111101010011011b	; Seed for random number

ClassName 	db 	"SimpleWinClass",0
szAppName   	db 	"MDIDemo",0
szFrameClass	db 	"MdiFrame",0
szHelloClass	db	"MdiHelloChild",0
szRectClass	db	"MDIRectChild",0
szMdiclient	db	"MDICLIENT",0
szMDIDemo	db	"MDI Demonstration",0
szMdiMenuInit	db	"MdiMenuInit",0
szMdiMenuHello	db	"MdiMenuHello",0
szMdiMenuRect	db	"MdiMenuRect",0
szHelloW	db	"Hello World",0
szHello		db	"Hello",0
szOK2Close	db	"OK to close window ?",0
szRectangles	db	"Rectangles",0

clrTextArray	DWORD	0, 255, 255 SHL 8, 255 SHL 16, 255 + (255 SHL 8) + (255 SHL 16)

.data?

hInstance HINSTANCE ?
CommandLine LPSTR ?

hMenuInit		DWORD	?
hMenuHello		DWORD	?
hMenuRect		DWORD	?
hMenuInitWindow		DWORD	?
hMenuHelloWindow	DWORD	?
hMenuRectWindow		DWORD	?
hWndClient		DWORD	?
hWndFrame		DWORD	?

HELLODATA	STRUCT		; Structure for storing data unique to each Hello child window

iColour		DWORD	?
clrText		DWORD	?
	
HELLODATA	ENDS

RECTDATA	STRUCT		; Structure for storing data unique to each Rect child window

cxClient	WORD	?
cyClient	WORD	?

RECTDATA	ENDS

;Macros

include  Macros.mac
;----------------------------------------------------------------------------
.code
start:
	invoke GetModuleHandle, NULL
	mov    hInstance, eax
	invoke GetCommandLine

        invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
	invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR, iCmdShow:SDWORD

LOCAL	wc:WNDCLASSEX, msg:MSG
LOCAL	hAccel:HWND, hwndFrame:HWND, hwndClient:HWND 

;	Register the Frame window class
;	-------------------------------

	mov	wc.cbSize, sizeof WNDCLASSEX
	mov	wc.style, CS_HREDRAW or CS_VREDRAW 
	mov	wc.lpfnWndProc, offset FrameWndProc
	mov	wc.cbClsExtra,NULL
	mov	wc.cbWndExtra,NULL
	M2M	wc.hInstance, hInst
	mov     wc.hbrBackground,COLOR_APPWORKSPACE+1
	mov	wc.lpszMenuName, NULL
	mov	wc.lpszClassName, offset szFrameClass
	invoke  LoadIcon,NULL,IDI_APPLICATION
	mov     wc.hIcon,eax
	mov	wc.hIconSm,0
  	invoke  LoadCursor,NULL,IDC_ARROW
	mov	wc.hCursor,eax

	invoke	RegisterClassEx, ADDR wc
	
;	Register the Hello child window class
;	-------------------------------------

	mov	wc.cbSize, sizeof WNDCLASSEX
	mov	wc.style, CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
	mov	wc.lpfnWndProc, offset HelloWndProc
	mov	wc.cbClsExtra,0
	mov	wc.cbWndExtra, sizeof HANDLE 		; Space for pointer
	M2M	wc.hInstance, hInst
	mov     wc.hbrBackground,COLOR_WINDOW+1
	mov	wc.lpszMenuName, NULL
	mov	wc.lpszClassName, offset szHelloClass
	invoke  LoadIcon,NULL,IDI_APPLICATION
	mov     wc.hIcon,eax
	mov	wc.hIconSm,0
	invoke  LoadCursor,NULL,IDC_ARROW
	mov	wc.hCursor,eax

	invoke	RegisterClassEx, ADDR wc
	
;	Resister the Rect child window class
;	------------------------------------

	mov	wc.cbSize, sizeof WNDCLASSEX
	mov	wc.style, CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
	mov	wc.lpfnWndProc, offset RectWndProc
	mov	wc.cbClsExtra,NULL
	mov	wc.cbWndExtra, sizeof HANDLE 		; Space for pointer
	M2M	wc.hInstance, hInst
	mov     wc.hbrBackground,COLOR_WINDOW+1
	mov	wc.lpszMenuName, NULL
	mov	wc.lpszClassName, offset szRectClass
	invoke  LoadIcon,NULL,IDI_APPLICATION
	mov     wc.hIcon,eax
	mov	wc.hIconSm,0
  	invoke  LoadCursor,NULL,IDC_ARROW
	mov	wc.hCursor,eax

	invoke	RegisterClassEx, ADDR wc

;	Obtain handles to 3 possible menus and submenus
;	-----------------------------------------------

	invoke	LoadMenu, hInst, ADDR szMdiMenuInit	; No documents present
	mov	hMenuInit,eax
	
	invoke	LoadMenu, hInst, ADDR szMdiMenuHello	; For Hello
	mov	hMenuHello,eax

	invoke	LoadMenu, hInst, ADDR szMdiMenuRect	; For Rectangles
	mov	hMenuRect,eax

	invoke  GetSubMenu, hMenuInit, INIT_MENU_POS
	mov	hMenuInitWindow,eax
	invoke  GetSubMenu, hMenuHello, HELLO_MENU_POS
	mov	hMenuHelloWindow,eax
	invoke  GetSubMenu, hMenuRect, RECT_MENU_POS
	mov	hMenuRectWindow,eax

	invoke	LoadAccelerators, hInst, szAppName	;Load the accelerator table
	mov	hAccel, eax

;	Create the frame window
;	-----------------------

	invoke	CreateWindowEx, WS_EX_LEFT, ADDR szFrameClass, ADDR szMDIDemo, \
	   WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN, CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenuInit,\
           hInst,NULL
	mov	hwndFrame, eax

	invoke	GetWindow, eax, GW_CHILD
	mov	hwndClient,eax

        INVOKE ShowWindow, hwndFrame, iCmdShow	; 1 if previously visible; 0 if hidden 
        INVOKE UpdateWindow, hwndFrame

;	Message Loop
;	------------

        .WHILE TRUE
                invoke GetMessage, ADDR msg, NULL, 0, 0
                .BREAK .IF (!eax)

		invoke TranslateMDISysAccel, hwndClient, ADDR msg
		push	eax

		invoke TranslateAccelerator, hwndFrame, hAccel, ADDR msg
		pop	ecx
 
		.IF !ecx && !eax
		    invoke TranslateMessage, ADDR msg
		    invoke DispatchMessage, ADDR msg
		.ENDIF
        .ENDW

	invoke	DestroyMenu, hMenuHello		; Clean up by detaching unattached menus
	invoke	DestroyMenu, hMenuRect		;

        mov     eax,msg.wParam

        ret
WinMain endp

FrameWndProc	proc hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL	hwndChild:HWND, clientcreate:CLIENTCREATESTRUCT,mdicreate:MDICREATESTRUCT

	mov	eax,message

	.IF	eax==WM_CREATE				; Create the client window

		M2M	clientcreate.hWindowMenu, hMenuInitWindow
		mov	clientcreate.idFirstChild, IDM_FIRSTCHILD

		invoke	CreateWindowEx, WS_EX_MDICHILD, ADDR szMdiclient, 0, \
			WS_CHILD or WS_CLIPCHILDREN or WS_VISIBLE,\
			0,0,0,0, hWnd, 1, hInstance, ADDR clientcreate
		mov	hWndClient,eax

		return  0

	.ELSEIF eax==WM_COMMAND

		LOWORD	wParam	

		.IF	eax==IDM_FILE_NEWHELLO		; Create a Hello Window

		    mov	mdicreate.szClass, offset szHelloClass
		    mov mdicreate.szTitle, offset szHello
		    M2M	mdicreate.hOwner, hInstance
		    mov	mdicreate.x, CW_USEDEFAULT
		    mov mdicreate.y, CW_USEDEFAULT 
		    mov	mdicreate.lx, CW_USEDEFAULT	
		    mov mdicreate.ly, CW_USEDEFAULT	
		    mov mdicreate.style,  WS_HSCROLL or WS_VSCROLL 
		    mov mdicreate.lParam, 0

		    invoke SendMessage, hWndClient, WM_MDICREATE, 0, ADDR mdicreate
		    
  		    mov hwndChild, eax
		
		    return 0

		.ELSEIF eax==IDM_FILE_NEWRECT	; Create a Rect child window

		    mov	mdicreate.szClass, offset szRectClass
		    mov mdicreate.szTitle, offset szRectangles
		    M2M	mdicreate.hOwner, hInstance
		    mov	mdicreate.x, CW_USEDEFAULT
		    mov mdicreate.y, CW_USEDEFAULT 
		    mov	mdicreate.lx, CW_USEDEFAULT
		    mov mdicreate.ly, CW_USEDEFAULT
		    mov mdicreate.style, WS_HSCROLL or WS_VSCROLL
		    mov mdicreate.lParam, 0

		    invoke SendMessage, hWndClient, WM_MDICREATE, 0, ADDR mdicreate
		    mov hwndChild, eax

		    return 0 
		    				
		.ELSEIF eax==IDM_FILE_CLOSE	; Close the active window
		    
		    invoke SendMessage, hWndClient, WM_MDIGETACTIVE,0,0
		    mov	hwndChild, eax

		    invoke SendMessage, hwndChild, WM_QUERYENDSESSION, 0,0
	   
		    .IF	eax
			invoke SendMessage, hWndClient, WM_MDIDESTROY, hwndChild, 0
		    .ENDIF

		    return 0	
		    
		.ELSEIF eax==IDM_APP_EXIT

		    invoke SendMessage, hWnd, WM_CLOSE, 0, 0
		    return 0

;		Messages for arranging windows
;		------------------------------

		.ELSEIF eax==IDM_WINDOW_TILE

		    invoke SendMessage, hWndClient, WM_MDITILE, MDITILE_HORIZONTAL, 0
		    return 0
		    
		.ELSEIF eax==IDM_WINDOW_CASCADE

		    invoke SendMessage, hWndClient, WM_MDICASCADE, 0,0
		    return 0

		.ELSEIF eax==IDM_WINDOW_ARRANGE
	
		    invoke SendMessage, hWndClient, WM_MDIICONARRANGE, 0,0
		    return 0

		.ELSEIF eax==IDM_WINDOW_CLOSEALL	; Attempt to close all windows
			
		    invoke EnumChildWindows, hWndClient, ADDR CloseEnumProc, 0
		    return 0

;		Pass to active child
;		--------------------

		.ELSE

		    invoke SendMessage, hWndClient, WM_MDIGETACTIVE, 0,0
		    mov	hwndChild, eax

		    invoke IsWindow, hwndChild

		    .IF eax
		    	    invoke SendMessage, hwndChild, WM_COMMAND, wParam, lParam
		    .ENDIF
		
		.ENDIF

	.ELSEIF eax==WM_QUERYENDSESSION || eax==WM_CLOSE ; Attempt to close all children

		invoke  SendMessage, hWnd, WM_COMMAND, IDM_WINDOW_CLOSEALL, 0
		invoke	GetWindow, hWndClient, GW_CHILD
		
		.IF eax != NULL
		    return 0
		.ENDIF

	.ELSEIF	eax==WM_DESTROY

		invoke	PostQuitMessage, 0
		return 0

	.ENDIF

;	Pass unprocessed messages to DefFrameProc
;	-----------------------------------------

	invoke	DefFrameProc, hWnd, hWndClient, message, wParam, lParam
  
	return eax

FrameWndProc	endp

CloseEnumProc	proc hWnd:HWND, lParam:LPARAM

	invoke	GetWindow, hWnd, GW_OWNER	; Check for icon title

	.IF	eax
		return TRUE
	.ENDIF

	invoke	GetParent, hWnd	
	invoke	SendMessage, eax, WM_MDIRESTORE, hWnd, 0

	invoke	SendMessage, hWnd, WM_QUERYENDSESSION, 0,0

	.IF	!eax
		return TRUE
	.ENDIF

	invoke	GetParent, hWnd
	invoke  SendMessage, eax, WM_MDIDESTROY, hWnd, 0

	return TRUE

CloseEnumProc	endp	

HelloWndProc proc uses ebx esi, hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL	ps:PAINTSTRUCT, rect:RECT, hdc:HDC, hMenu:DWORD, pHelloData:DWORD 

	mov	eax, message

	.IF	eax==WM_CREATE

;	    Allocate memory for window private data

		invoke  GetProcessHeap
		invoke  HeapAlloc, eax, HEAP_ZERO_MEMORY, sizeof HELLODATA
		mov	pHelloData, eax
		mov	ebx, eax
		mov	HELLODATA.iColour[ebx], IDM_COLOR_BLACK
		mov	HELLODATA.clrText[ebx],0	; RGB 0,0,0

		invoke	SetWindowLong, hWnd, 0, ebx 	; Change attribute of specified window
							; 0 means GWL_HWNDFIRST
;	    Save some window handles

		invoke	GetParent, hWnd
		mov	hWndClient, eax
		invoke	GetParent, hWndClient
		mov	hWndFrame, eax
		return  0

	.ELSEIF eax==WM_COMMAND

	LOWORD	wParam

	    .IF	eax==IDM_COLOR_BLACK || eax==IDM_COLOR_RED  || \
		eax==IDM_COLOR_GREEN || eax==IDM_COLOR_BLUE || eax==IDM_COLOR_WHITE

;		Change the text colour
;		----------------------

		invoke  GetWindowLong, hWnd, 0
		mov	pHelloData, eax
		mov	ebx,eax

		invoke  GetMenu, hWndFrame
		mov	hMenu, eax
		invoke	CheckMenuItem, hMenu, HELLODATA.iColour[ebx], MF_UNCHECKED

		M2M	HELLODATA.iColour[ebx], wParam
		invoke	CheckMenuItem, hMenu, HELLODATA.iColour[ebx], MF_CHECKED

		mov	esi, wParam
		sub	esi, IDM_COLOR_BLACK
		shl	esi,2				; Make dword index
		mov	eax, clrTextArray[esi]
		mov	HELLODATA.clrText[ebx], eax
		invoke	InvalidateRect, hWnd, 0,0

	    .ENDIF	
	    return 0
	
	.ELSEIF eax==WM_PAINT

		invoke	BeginPaint, hWnd, ADDR ps
		mov	hdc, eax
		
		invoke	GetWindowLong, hWnd, 0
		mov	pHelloData,eax
		mov	ebx,eax
		invoke	SetTextColor, hdc, HELLODATA.clrText[ebx]	 
		invoke  GetClientRect, hWnd, ADDR rect

		invoke	DrawText, hdc, ADDR szHelloW, -1, ADDR rect, \	; Write "Hello World"
			DT_SINGLELINE or DT_CENTER or DT_VCENTER	; to child window

		invoke	EndPaint, hWnd, ADDR ps
		return 0

	.ELSEIF	eax==WM_MDIACTIVATE
		
;		Set the Hello menu if gaining focus
;		-----------------------------------
		mov	eax, lParam

		.IF	eax==hWnd

		    invoke SendMessage, hWndClient, WM_MDISETMENU, \
					hMenuHello, hMenuHelloWindow
		.ENDIF
		
;		Check/Uncheck menu item
;		-----------------------

		invoke	GetWindowLong, hWnd, 0
		mov	pHelloData,eax
		mov	ebx,eax

		mov	eax, hWnd

		.IF	eax==lParam
		    mov	eax, MF_CHECKED
		.ELSE
		    mov eax, MF_UNCHECKED
		.ENDIF

		invoke	CheckMenuItem, hMenuHello, \ ;	Returns previous state; 8 if 
			HELLODATA.iColour[ebx], eax  ;  previously checked, 0 if unchecked
 					
;		Set the Init menu if losing focus
;		---------------------------------
		
		mov	eax, hWnd

		.IF	eax != lParam

		    invoke SendMessage, hWndClient, WM_MDISETMENU, \
					hMenuInit, hMenuInitWindow	 		
		.ENDIF

		invoke DrawMenuBar, hWndFrame		; Redraw the menu bar
		return 0

	.ELSEIF	eax==WM_QUERYENDSESSION || eax==WM_CLOSE

		invoke	MessageBox, hWnd, ADDR szOK2Close, ADDR szHello, \
			MB_ICONQUESTION or MB_OKCANCEL
		.IF	eax != IDOK
			return 0 
		.ENDIF
    
	.ELSEIF	eax==WM_DESTROY
	 
		invoke  GetWindowLong, hWnd, 0
		mov	pHelloData, eax
			
		invoke	GetProcessHeap
		invoke	HeapFree, eax, 0, pHelloData 
		return 0
	.ENDIF

;	Pass unprocessed messages

	invoke  DefMDIChildProc, hWnd, message, wParam, lParam
	return	eax
		
HelloWndProc	endp

RectWndProc	proc uses ebx, hWnd:HWND, message:UINT, wParam:WPARAM, lParam:LPARAM

LOCAL	ps:PAINTSTRUCT, hdc:HDC, pRectData:DWORD, hBrush:DWORD 
LOCAL	xLeft:DWORD, xRight:DWORD, yTop:DWORD, yBottom:DWORD
LOCAL	nRed:WORD, nGreen:WORD, nBlue:WORD
 
	mov	eax, message

	.IF	eax==WM_CREATE

;	    Allocate memory for window private data

		invoke  GetProcessHeap
		invoke  HeapAlloc, eax, HEAP_ZERO_MEMORY, sizeof RECTDATA
		mov	pRectData, eax

		invoke	SetWindowLong, hWnd, 0, pRectData	; Save pointer to RectData
							
;	    Start the timer going

		invoke SetTimer, hWnd, 1, 250, 0
					
;	    Save some window handles

		invoke	GetParent, hWnd
		mov	hWndClient, eax
		invoke	GetParent, hWndClient
		mov	hWndFrame, eax
		return  0

	.ELSEIF eax==WM_SIZE			; If not minimised, save window size
		
		.IF	wParam != SIZE_MINIMIZED

			invoke GetWindowLong, hWnd, 0	; Get pointer to memory block
							; holding the Rectdata
		     	mov	pRectData, eax
			mov	ebx, eax

			LOWORD	lParam
			mov	RECTDATA.cxClient[ebx],ax
			HIWORD  lParam
			mov	RECTDATA.cyClient[ebx],ax
		.ENDIF
		
	.ELSEIF eax==WM_TIMER			; Display a random rectangle

		invoke GetWindowLong, hWnd, 0	; Get pointer to memory block
						; holding the Rectdata
;	     	mov	pRectData, eax
		mov	ebx,eax
		mov	bx,RECTDATA.cxClient[ebx]

	        mov	al,16
	        call	random		; Get random number 16 bits big (in eax)
		mov	edx,0
		div	bx
		mov	xLeft,edx	; Get remainder (modulus)

		mov	al,16
		call	random
		mov	edx,0
		div	bx
		mov	xRight,edx

		mov	al,16
		call	random		
		mov	edx,0
		div	bx
		mov	yTop,edx

		mov	al,16
		call	random		
		mov	edx,0
		div	bx
		mov	yBottom,edx

		mov	al,16
		call	random
		and	ax, 255
		mov	nRed, ax

		mov	al,16
		call	random
		and	ax, 255
		mov	nGreen, ax

		mov	al,16
		call	random
		and	ax, 255
		mov	nBlue,ax

		invoke	GetDC, hWnd
		mov	hdc, eax

		mov	al,Byte ptr nBlue	; Use RGB values to make COLORREF 
		shl	eax,8
		add	al,Byte ptr nGreen
		shl	eax,8
		add	al,Byte ptr nRed
		add	eax,0FFFFFFh		; Mask out top byte to complete COLORREF dword 

		invoke	CreateSolidBrush, eax 	
		mov	hBrush, eax
		invoke	SelectObject, hdc, hBrush

		MIN	xLeft, xRight				; Get the corners
		push	eax
		MIN	yTop, yBottom
		push 	eax
		MAX	xLeft, xRight
		push	eax
		MAX	yTop, yBottom

		pop	ebx
		pop	ecx
		pop	edx

		invoke	Rectangle, hdc, edx, ecx, ebx, eax	; Draw rectangle

;		invoke	Sleep, 10			; Slow it down 

		invoke	ReleaseDC, hWnd, hdc
		invoke	DeleteObject, hBrush
 
		return  0
	
	.ELSEIF eax==WM_PAINT

		invoke  InvalidateRect, hWnd, 0, TRUE
		invoke	BeginPaint, hWnd, ADDR ps
		mov	hdc, eax
		invoke	EndPaint, hWnd, ADDR ps
		return  0

	.ELSEIF eax==WM_MDIACTIVATE		; Set appropriate menu

		mov	eax, lParam

		.IF	eax==hWnd
		    invoke	SendMessage, hWndClient, WM_MDISETMENU, hMenuRect, \
				hMenuRectWindow
		.ELSE
		    invoke	SendMessage, hWndClient, WM_MDISETMENU, hMenuInit, \
				hMenuInitWindow	 	
		.ENDIF

		invoke	DrawMenuBar, hWndFrame

		return 0
 		    	
	.ELSEIF eax==WM_DESTROY

		invoke	GetWindowLong, hWnd, 0
		mov	pRectData, eax

		invoke	GetProcessHeap
		invoke	HeapFree, eax, 0, pRectData
		invoke	KillTimer, hWnd, 1

		return 0
	.ENDIF 

;	Pass unprocessed messages

	invoke  DefMDIChildProc, hWnd, message, wParam, lParam
	return	eax

RectWndProc	endp

random proc	uses ebx	   
;-----------------------------------------------;
; Generate Pseudo Random number (Fast)		;
;						;
; Entry:   al = size of random number		;					
;						;
; Return: eax = random number, cl bits in size	;
;-----------------------------------------------;
	mov	cl,al
        xor	eax,eax
        mov	bl,byte ptr random_init
        and	bl,1
EVEN
Gen_bit:			; make n bit numbers
        shl	eax,1

        mov	edx,random_init	; Copy seed  

        shr	edx,9
        xor	bl,dl

        shr	edx,5
        xor	bl,dl

    	bt	ebx,1		; Copy bit 1 to carry flag
        rcr	random_init,1	; Rotate seed right 1 bit

        setc	bl		; Set bl TRUE if carry is set		
	or	al,bl

	dec	cl
        jnz	Gen_bit

	ret
random  endp
        end start

