
;-----------------------------------------
;   Header2.asm -- A header demo
;
;	   by	Ron Thomas 26/1/01
;
;   Ron_Thom@Compuserve.com	
;-----------------------------------------

.386				; 32 bit when .386 appears before .MODEL
.MODEL FLAT,STDCALL
option casemap :none   		; case sensitive

include windows.inc

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

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


WinMain		PROTO :DWORD, :DWORD, :DWORD, :SDWORD
InitHeader	PROTO :DWORD
lnstr		PROTO :DWORD
copystr		PROTO :DWORD, :DWORD, :DWORD
		
NUMCOLS		EQU	4
DEFWIDTH	EQU	100
MINWIDTH	EQU	10
MAXWIDTH	EQU	400
SPACING		EQU	8
NUMENTRIES	EQU	6

IDM_HELP	EQU	100
IDM_RESET	EQU	101
IDM_EXIT	EQU	102

ID_HEADCONTROL	EQU	500

.data

BookDB	STRUCT					; Define structure for books (4 fields)

	_title  db	79 DUP (?)		; Avoid the "title" keyword 
		db	0			; Zero terminator	 
	author  db	79 DUP (?)
		db	0
	publisher db	79 DUP (?)
		db	0
	date	db	19 DUP (?)
		db	0

BookDB	ENDS

datab   \
	 BookDB <'Using Assembly Language',,'Allen Wyatt',,'Que',,'1992'>
	 BookDB <'Advanced Assembly Language',,'Allen Wyatt',,'Que',,'1992'>
	 BookDB <'Programmers Guide to the EGA & VGA Cards',,'Richard Ferraro',,\
		 'Addison-Wesley',,'1990'>
	 BookDB <'Programmers Guide to PC & PS/2 Video System',,'Richard Wilton',,\
		 'MicroSoft',,'1987'>
	 BookDB <'Computer Animation Programming Methods and Techniques',,\
	         'Sanchez & Canton',,'McGraw Hill',,'1990'>
 	 BookDB <'Numerical Programming the 387, 486 & Pentium',,\
	         'Sanchez & Canton',,'McGraw Hill',,'1995'>
 	 
columns		dd	NUMCOLS DUP (DEFWIDTH)
WC_HEADER	db	"SysHeader32",0			; Definition for the Header class
	
MYMENU		db	"MYMENU",0
help		db	"Help",0	
quit		db	"Quit the program?",0
exit		db	"Exit",0
TryResize	db	"Try resizing the header.",0

szWinName	db	"MyWin",0
szTitle		db	"An Enhanced Header Control",0
szBtitle	db	"Title",0
szAuthor	db	"Author",0
szPublisher	db	"Publisher",0
szCopyright	db	"Copyright",0
ellipsis	db	"...",0
	
.data?

SIZ		STRUCT			; A replacement for the SIZE structure
					; with invalid cx and cy members
	cxx	dd	?
	cyy	dd	?

SIZ		ENDS

WINPOS		STRUCT			; We have to use our own structure as the
					; default WINDOWPOS uses members cx & cy
	hwnd		dd	?	; which are illegal
	hwndInsertAfter	dd	?
	x		dd	?
	y		dd	?
	cxx		dd	?
	cyy		dd	?
	flags		dd	?

WINPOS		ENDS

_HD_ITEM	STRUCT			; HD_ITEM Structure in Windows.inc is empty !

	_mask	dd	?		; Also objects to mask !
	cxy	dd	?		
	pszText	dd	?
	hbm	dd	?
	cchTextMax  dd	?
	fmt	dd	?
	lParam	dd	?
	
_HD_ITEM	ENDS

hInstance HINSTANCE 	?
CommandLine LPSTR 	?
hHeadWnd	dd	?
HeaderHeight	dd	?
maxX		dd	?		; Screen dimensions
maxY		dd	?

;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,CmdShow:SDWORD

	LOCAL wc:WNDCLASSEX, msg:MSG, hwnd:HWND, hAccel:HWND

	    mov   wc.cbSize,SIZEOF WNDCLASSEX
	    mov   wc.lpszClassName,OFFSET szWinName
	    M2M   wc.hInstance,hInstance
	    mov   wc.lpfnWndProc, OFFSET WndProc
	    invoke LoadCursor,NULL,IDC_ARROW
	    mov   wc.hCursor,eax			; Cursor style
	    invoke LoadIcon, 0, IDI_APPLICATION		
 	    mov   wc.hIcon,eax				; Large icon
 	    mov   wc.hIconSm,eax			; Small icon
	    mov   wc.lpszMenuName, offset MYMENU	; Menu name
	    mov   wc.hbrBackground,COLOR_WINDOW+1
	    mov   wc.style, 0				; default style
	    mov   wc.cbClsExtra,NULL
	    mov   wc.cbWndExtra,NULL
       
            invoke RegisterClassEx, addr wc

	    .IF   !eax
		  return 0
	    .ENDIF
		
        invoke CreateWindowEx,NULL,ADDR szWinName,ADDR szTitle,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT, \
	   CW_USEDEFAULT,CW_USEDEFAULT,HWND_DESKTOP,NULL, hInst,NULL

        mov   hwnd,eax

	M2M   hInstance, hInst				; Save current instance handle
	
	invoke LoadAccelerators, eax, ADDR MYMENU
	mov hAccel, eax

	invoke InitCommonControls
 
	invoke ShowWindow, hwnd, CmdShow	
        invoke UpdateWindow, hwnd

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

		invoke	TranslateAccelerator, hwnd, hAccel, ADDR msg

		.IF !eax
                    invoke TranslateMessage, ADDR msg
                    invoke DispatchMessage, ADDR msg
		.ENDIF

        .ENDW

        mov     eax,msg.wParam
        ret
WinMain endp

WndProc proc  uses ebx esi edi, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

	LOCAL	rect:RECT, hd_layout:HD_LAYOUT, winpos:WINPOS, hd_notify:HD_NOTIFY
	LOCAL	hd_item:_HD_ITEM, ps:PAINTSTRUCT, tm:TEXTMETRIC, siz:SIZ
	LOCAL	hdc:DWORD, ColStart:DWORD, chrs:DWORD, linespacing:DWORD
	LOCAL	i:DWORD, j:DWORD, entry:DWORD, strr[80]:BYTE 
 
	mov   eax,uMsg

	.IF     eax==WM_CREATE

	    invoke InitHeader, hWnd
	    mov hHeadWnd, eax
 
	.ELSEIF eax==WM_COMMAND 

	    LOWORD wParam

	    .IF     eax==IDM_RESET		;Resore default widths

	        mov hd_item._mask, HDI_WIDTH

		mov ebx, 0
		mov i, 0

		.WHILE i < NUMCOLS

		    invoke SendMessage, hHeadWnd, HDM_GETITEM, i, ADDR hd_item

		    mov hd_item.cxy, DEFWIDTH
		    mov columns[ebx], DEFWIDTH

		    invoke SendMessage, hHeadWnd, HDM_SETITEM, i, ADDR hd_item

		    add ebx, 4		  
		    inc i

		.ENDW

		invoke InvalidateRect, hWnd, 0, 1

	    .ELSEIF eax==IDM_EXIT
	    
		invoke MessageBox, hWnd, ADDR quit, ADDR exit, MB_YESNO

		.IF eax==IDYES

		    invoke PostQuitMessage, 0

		.ENDIF
 
	    .ELSEIF eax==IDM_HELP

		invoke MessageBox, hWnd, ADDR TryResize, ADDR help, MB_OK
 
	    .ENDIF

	.ELSEIF eax==WM_SIZE	; Resize header control with parent window change size

	    invoke GetClientRect, hWnd, ADDR rect

	    lea    eax, rect
	    mov	   hd_layout.prc, eax
	    lea    eax, winpos 
	    mov	   hd_layout.pwpos, eax

	    invoke SendMessage, hHeadWnd, HDM_LAYOUT, 0, ADDR hd_layout
	 
	    invoke MoveWindow, hHeadWnd, winpos.x, winpos.y, winpos.cxx, winpos.cyy, 1

	.ELSEIF eax==WM_NOTIFY			; Respond to notify messages

	    LOWORD wParam

	    .IF eax==ID_HEADCONTROL

		mov  ebx, lParam		; lParm points @ HD_NOTIFY parm block 
		mov  esi, HD_NOTIFY.pitem[ebx]	; Get address of HD_ITEM parm block   

		mov  eax, HD_NOTIFY.hdr.code[ebx] ; Get notification code

		.IF eax==HDN_ENDTRACK		; User changed column width

		    .IF _HD_ITEM.cxy[esi] < MINWIDTH

		        mov _HD_ITEM.cxy[esi], MINWIDTH
			mov ebx, HD_NOTIFY.iItem[ebx]	; Get index 
			shl ebx, 2			; make dword index
			mov columns[ebx], MINWIDTH	

		    .ELSE

		  	mov eax, _HD_ITEM.cxy[esi]
		        mov ebx, HD_NOTIFY.iItem[ebx]
			shl ebx, 2
			mov columns[ebx], eax		; Reset the column width

			invoke InvalidateRect, hWnd, 0, 1

		    .ENDIF

		.ELSEIF eax==HDN_TRACK			; User changing column width

		    invoke GetClientRect, hWnd, ADDR rect

		    .IF _HD_ITEM.cxy[esi] < MINWIDTH

			mov _HD_ITEM.cxy[esi], MINWIDTH
			mov ebx, HD_NOTIFY.iItem[ebx]	; Get index
			shl ebx, 2			; make dword index
			mov columns[ebx], MINWIDTH

		    .ELSE

			mov eax, _HD_ITEM.cxy[esi]
 			mov ebx, HD_NOTIFY.iItem[ebx]
			shl ebx, 2
			mov columns[ebx], eax		; Reset the column width

			M2M rect.top, HeaderHeight

			invoke InvalidateRect, hWnd, ADDR rect, 1
   
		    .ENDIF
 
		.ELSEIF eax==HDN_ITEMCLICK		; User clicked header button
		  	
		    mov hd_item._mask, HDI_WIDTH	; Return to default width

		    mov ecx, HD_NOTIFY.iItem[ebx]	; Get index
		    invoke SendMessage, hHeadWnd, HDM_GETITEM, ecx, ADDR hd_item	

		    mov hd_item.cxy, DEFWIDTH

		    mov edi, HD_NOTIFY.iItem[ebx]
		    shl edi, 2
		    mov columns[edi], DEFWIDTH

		    mov ecx, HD_NOTIFY.iItem[ebx]	; Get index
		    invoke SendMessage, hHeadWnd, HDM_SETITEM, ecx, ADDR hd_item

		    invoke InvalidateRect, hWnd, 0, 1
		 
		.ELSEIF eax==HDN_ITEMDBLCLICK	; User double-clicked
        	
		    M2M hd_item, HDI_WIDTH
		    mov ecx, HD_NOTIFY.iItem[ebx]	; Get index
		    invoke SendMessage, hHeadWnd, HDM_GETITEM, ecx, ADDR hd_item

		    shl hd_item.cxy, 1			; Double width of header

		    .IF hd_item.cxy > MAXWIDTH

			mov hd_item.cxy, MAXWIDTH		        

		    .ENDIF

		    mov edi, HD_NOTIFY.iItem[ebx]
		    shl edi, 2
		    M2M columns[edi], hd_item.cxy

		    mov ecx, HD_NOTIFY.iItem[ebx]	; Get index
		    invoke SendMessage, hHeadWnd, HDM_SETITEM, ecx, ADDR hd_item

		    invoke InvalidateRect, hWnd, 0, 1

		.ENDIF

	    .ENDIF

	.ELSEIF eax==WM_PAINT

	    invoke BeginPaint, hWnd, ADDR ps
	    mov    hdc, eax
	    invoke GetTextMetrics, hdc, ADDR tm

	    mov	   eax, tm.tmHeight
	    add	   eax, tm.tmInternalLeading
	    mov    linespacing, eax

	    mov    entry, 0			; Index to books
	    mov    ebx,0			

	    .WHILE entry < NUMENTRIES		; Loop for all books in database  

		mov ColStart, 0			
		mov i, 0			; Start with 1st column 
 	        mov esi, 0

		.WHILE i < NUMCOLS

		    .IF     i==0		; 1st column

			invoke lnstr, ADDR datab._title[ebx]	; Get string length
			invoke copystr, ADDR strr, ADDR datab._title[ebx], eax ; Copy to strr	

		    .ELSEIF i==1		; 2nd Column

			invoke lnstr, ADDR datab.author[ebx]		
			invoke copystr, ADDR strr, ADDR datab.author[ebx], eax

		    .ELSEIF i==2		; 3rd Column	

			invoke lnstr, ADDR datab.publisher[ebx]		
			invoke copystr, ADDR strr, ADDR datab.publisher[ebx], eax

		    .ELSEIF i==3		; 4th Column
	
			invoke lnstr, ADDR datab.date[ebx]		
			invoke copystr, ADDR strr, ADDR datab.date[ebx], eax

		    .ENDIF

	    	    invoke lnstr, ADDR strr		; Get string length
		    mov  ecx, eax

;		    Compute the width & width of specified string "strr"
;		    using currently selected font
	 		
	   	    invoke GetTextExtentPoint32, hdc, ADDR strr, ecx, ADDR siz

	    	    mov j, 2

		    mov edi, columns[esi]
		    sub edi, SPACING
		   
		    .WHILE edi < siz.cxx

			xor edx,edx
			mov eax, columns[esi]
			div tm.tmAveCharWidth
			mov chrs, eax

			push ebx
			mov  ebx, chrs
			sub  ebx, j
			invoke copystr, ADDR strr[ebx], ADDR ellipsis, 4 ; Copy ...0
			pop  ebx
 	
			invoke lnstr, ADDR strr		; Get string length
			mov  ecx, eax

;		        Compute the width & width of specified string "strr"

	   	        invoke GetTextExtentPoint32, hdc, ADDR strr, ecx, ADDR siz
	 		
			inc j		

		    .ENDW

;		    Compute x offset for text

		    mov eax, ColStart
		    add eax, SPACING		; ColStart + SPACING
		    push eax			; Save on stack

;		    Compute y offset for text

		    mov eax, linespacing
		    mul entry			; entry*Linespacing  ; Multiply by 0,1,2,3,4, or 5

		    add eax, HeaderHeight	; HeaderHeight + entry*Linespacing 		
 		    push eax		    	; Save on stack
		
		    invoke lnstr, ADDR strr	; Get string length

		    pop	ecx			; HeaderHeight + entry*Linespacing into ecx 
		    pop edx			; ColStart+SPACING into edx

;		    Print out data for the column item
 
		    invoke TextOut, hdc, edx, ecx, ADDR strr, eax

		    mov eax, columns[esi]	; Prepare to process next column
		    add	ColStart, eax

 		    inc i
		    add esi, 4			; Bump index to next column

		.ENDW

		inc entry			; Increment to the next book
		add ebx, sizeof BookDB		
	    
	    .ENDW	

	    invoke EndPaint, hWnd, ADDR ps

	.ELSEIF eax==WM_DESTROY
            
            invoke  PostQuitMessage,NULL
	    
	.ELSE
            invoke DefWindowProc,hWnd,uMsg,wParam,lParam
            ret
	.ENDIF

	return 0

WndProc endp

InitHeader	proc hParent:HWND

	LOCAL  rect:RECT, hd_layout:HD_LAYOUT, winpos:WINPOS, hd_item:_HD_ITEM
	LOCAL  hHdWnd:DWORD

	invoke GetClientRect, hParent, ADDR rect

;	Create the Header window

	invoke CreateWindowEx, 0, ADDR WC_HEADER, 0, WS_CHILD or WS_BORDER or HDS_BUTTONS, \
  	                       CW_USEDEFAULT, CW_USEDEFAULT,0,0, hParent, \
			       ID_HEADCONTROL, hInstance, 0

	mov hHdWnd, eax			; Header handle

;	Get header control layout to fit in client area

	lea	eax, winpos
	mov	hd_layout.pwpos, eax
	lea	eax, rect
	mov	hd_layout.prc, eax

	invoke  SendMessage, hHdWnd, HDM_LAYOUT, 0, ADDR hd_layout 

;	Set header dimensions to fit in client area

	invoke  MoveWindow, hHdWnd, winpos.x, winpos.y, winpos.cxx, winpos.cyy, 0

	M2M	HeaderHeight, winpos.cyy		; Save header height
	
;	Now insert items into the header

	mov	hd_item._mask, HDI_FORMAT or HDI_WIDTH or HDI_TEXT
	M2M	hd_item.pszText, offset szBtitle
	invoke  lnstr, ADDR hd_item.pszText
	M2M	hd_item.cchTextMax, eax
	mov	hd_item.cxy, DEFWIDTH
	mov	hd_item.fmt, HDF_STRING or HDF_LEFT
	invoke  SendMessage, hHdWnd, HDM_INSERTITEM, 0, ADDR hd_item 

	M2M	hd_item.pszText, offset szAuthor
	invoke  lnstr, ADDR hd_item.pszText
	mov	hd_item.cchTextMax, eax
	invoke  SendMessage, hHdWnd, HDM_INSERTITEM, 1, ADDR hd_item 		 
 	
	M2M	hd_item.pszText, offset szPublisher
	invoke  lnstr, ADDR hd_item.pszText
	mov	hd_item.cchTextMax, eax
 	invoke  SendMessage, hHdWnd, HDM_INSERTITEM, 2, ADDR hd_item 		 
 
	M2M	hd_item.pszText, offset szCopyright
	invoke  lnstr, ADDR hd_item.pszText
	mov	hd_item.cchTextMax, eax
	invoke  SendMessage, hHdWnd, HDM_INSERTITEM, 3, ADDR hd_item 		 
 
	invoke	ShowWindow, hHdWnd, SW_SHOW	; Display the header control
						; (returns 0 as window previously hidden)
	return hHdWnd

InitHeader	endp
 
lnstr proc lpszString:DWORD

  ; ---------------------------
  ; put length of string in eax
  ; EXCLUDING terminating zero
  ; ---------------------------
      mov eax, lpszString

    @@:
      mov dl, [eax]         ;  1
      inc eax               ;  1
      cmp dl, 0             ;  1
      jne @B                ;  3 when jmp taken

      sub eax, lpszString
      dec eax               ;  correct count
 
      ret

lnstr endp

copystr	proc	uses esi edi, pDest:DWORD, pSource:DWORD, Len:DWORD

;	Copies a zero terminated string, including the 0 terminator
 
	cld			;Work upward

	mov	ecx, Len	; # of bytes to copy
	inc	ecx		; Copy the 0 terminator as well
	mov	esi, pSource	; Point at source string
	mov	edi, pDest	; Point at destination string
	rep	movsb

	ret
copystr	endp

        end start

