; #########################################################################

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

; #########################################################################

      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc

      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib

; #########################################################################

      ;=============
      ; Local macros
      ;=============

      szText MACRO Name, Text:VARARG
        LOCAL lbl
          jmp lbl
            Name db Text,0
          lbl:
        ENDM

      m2m MACRO M1, M2
        push M2
        pop  M1
      ENDM

      return MACRO arg
        mov eax, arg
        ret
      ENDM

        ;=================
        ; Local prototypes
        ;=================
        WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
        WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        TopXY PROTO   :DWORD,:DWORD
        EditSl PROTO  :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        Static PROTO  :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
        Ed1Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        Ed2Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD
        Ed3Proc PROTO :DWORD,:DWORD,:DWORD,:DWORD

    .data
        szDisplayName db " Filtered Input DEMO",0
        CommandLine   dd 0
        hWnd          dd 0
        hInstance     dd 0
        hEdit1        dd 0
        hEdit2        dd 0
        hEdit3        dd 0
        lpfnEd1Proc   dd 0
        lpfnEd2Proc   dd 0
        lpfnEd3Proc   dd 0

    .code

start:
        invoke GetModuleHandle, NULL
        mov hInstance, eax

        invoke GetCommandLine
        mov CommandLine, eax

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

; #########################################################################

WinMain proc hInst     :DWORD,
             hPrevInst :DWORD,
             CmdLine   :DWORD,
             CmdShow   :DWORD

        ;====================
        ; Put LOCALs on stack
        ;====================

        LOCAL wc   :WNDCLASSEX
        LOCAL msg  :MSG

        LOCAL Wwd  :DWORD
        LOCAL Wht  :DWORD
        LOCAL Wtx  :DWORD
        LOCAL Wty  :DWORD

        ;==================================================
        ; Fill WNDCLASSEX structure with required variables
        ;==================================================

        mov wc.cbSize,         sizeof WNDCLASSEX
        mov wc.style,          CS_HREDRAW or CS_VREDRAW \
                               or CS_BYTEALIGNWINDOW
        mov wc.lpfnWndProc,    offset WndProc
        mov wc.cbClsExtra,     NULL
        mov wc.cbWndExtra,     NULL
        m2m wc.hInstance,      hInst   ;<< NOTE: macro not mnemonic
        mov wc.hbrBackground,  COLOR_BTNFACE+1
        mov wc.lpszMenuName,   NULL
        mov wc.lpszClassName,  offset szClassName
          invoke LoadIcon,hInst,500    ; icon ID
        mov wc.hIcon,          eax
          invoke LoadCursor,NULL,IDC_ARROW
        mov wc.hCursor,        eax
        mov wc.hIconSm,        0

        invoke RegisterClassEx, ADDR wc

        ;================================
        ; Centre window at following size
        ;================================

        mov Wwd, 340
        mov Wht, 250

        invoke GetSystemMetrics,SM_CXSCREEN
        invoke TopXY,Wwd,eax
        mov Wtx, eax

        invoke GetSystemMetrics,SM_CYSCREEN
        invoke TopXY,Wht,eax
        mov Wty, eax

        szText szClassName,"Template_Class"

        invoke CreateWindowEx,WS_EX_OVERLAPPEDWINDOW,
                              ADDR szClassName,
                              ADDR szDisplayName,
                              WS_OVERLAPPEDWINDOW,
                              Wtx,Wty,Wwd,Wht,
                              NULL,NULL,
                              hInst,NULL
        mov   hWnd,eax

        invoke LoadMenu,hInst,600  ; menu ID
        invoke SetMenu,hWnd,eax

        invoke ShowWindow,hWnd,SW_SHOWNORMAL
        invoke UpdateWindow,hWnd

      ;===================================
      ; Loop until PostQuitMessage is sent
      ;===================================

    StartLoop:
      invoke GetMessage,ADDR msg,NULL,0,0
      cmp eax, 0
      je ExitLoop
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage,  ADDR msg
      jmp StartLoop
    ExitLoop:

      return msg.wParam

WinMain endp

; #########################################################################

WndProc proc hWin   :DWORD,
             uMsg   :DWORD,
             wParam :DWORD,
             lParam :DWORD

    .if uMsg == WM_COMMAND
    ;======== menu commands ========
        .if wParam == 1000
            invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL
        .elseif wParam == 1900
            szText TheMsg,"Assembler, Pure & Simple"
            invoke MessageBox,hWin,ADDR TheMsg,ADDR szDisplayName,MB_OK
        .endif
    ;====== end menu commands ======


    .elseif uMsg == WM_CREATE

        jmp wpLbl
          txt1    db " Numeric",0
          txt2    db " Lcase",0
          txt3    db " Ucase",0
          nulbyte db 0
        wpLbl:

        invoke Static,ADDR txt1,hWin,10,10,100,22,500
        invoke Static,ADDR txt2,hWin,10,40,100,22,501
        invoke Static,ADDR txt3,hWin,10,70,100,22,502

        invoke EditSl,ADDR nulbyte,120,10,200,23,hWin,200
        mov hEdit1, eax
        invoke EditSl,ADDR nulbyte,120,40,200,23,hWin,201
        mov hEdit2, eax
        invoke EditSl,ADDR nulbyte,120,70,200,23,hWin,202
        mov hEdit3, eax

        invoke SetWindowLong,hEdit1,GWL_WNDPROC,Ed1Proc
        mov lpfnEd1Proc, eax

        invoke SetWindowLong,hEdit2,GWL_WNDPROC,Ed2Proc
        mov lpfnEd2Proc, eax

        invoke SetWindowLong,hEdit3,GWL_WNDPROC,Ed3Proc
        mov lpfnEd3Proc, eax

    .elseif uMsg == WM_CLOSE
        szText TheText,"Please Confirm Exit"
        invoke MessageBox,hWin,ADDR TheText,ADDR szDisplayName,MB_YESNO
          .if eax == IDNO
            return 0
          .endif
    .elseif uMsg == WM_DESTROY
        invoke PostQuitMessage,NULL
        return 0 
    .endif

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

    ret

WndProc endp

; ########################################################################

TopXY proc wDim:DWORD, sDim:DWORD

    shr sDim, 1      ; divide screen dimension by 2
    shr wDim, 1      ; divide window dimension by 2
    mov eax, wDim    ; copy window dimension into eax
    sub sDim, eax    ; sub half win dimension from half screen dimension

    return sDim

TopXY endp

; ########################################################################

EditSl proc szMsg:DWORD,a:DWORD,b:DWORD,
               wd:DWORD,ht:DWORD,hParent:DWORD,ID:DWORD

; EditSl PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
; invoke EditSl,adrTxt:DWORD,200,10,150,250,hWnd,700

    szText slEdit,"EDIT"

    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR slEdit,szMsg,
                WS_VISIBLE or WS_CHILDWINDOW or \
                ES_AUTOHSCROLL or ES_NOHIDESEL,
              a,b,wd,ht,hParent,ID,hInstance,NULL

    ret

EditSl endp

; ########################################################################

Static proc lpText:DWORD,hParent:DWORD,
                 a:DWORD,b:DWORD,wd:DWORD,ht:DWORD,ID:DWORD

    szText statClass,"STATIC"

    invoke CreateWindowEx,WS_EX_STATICEDGE,
            ADDR statClass,lpText,
            WS_CHILD or WS_VISIBLE or SS_LEFT,
            a,b,wd,ht,hParent,ID,
            hInstance,NULL

    ret

Static endp

; ########################################################################

Ed1Proc proc hCtl   :DWORD,
             uMsg   :DWORD,
             wParam :DWORD,
             lParam :DWORD

    LOCAL Buffer[32]:BYTE

    ; -----------------------------
    ; Process control messages here
    ; -----------------------------

    .if uMsg == WM_CHAR
        .if wParam == 8             ; backspace
            jmp accept
        .endif

        .if wParam == "."           ; only allow one decimal point

            invoke SendMessage,hCtl,WM_GETTEXT,sizeof Buffer,ADDR Buffer

            mov ecx, sizeof Buffer  ; byte count in ecx
            lea esi, Buffer         ; address in esi
          @xxx:
            lodsb                   ; load byte into al

            cmp al, "."             ; if decimal point already in Buffer
            jne @xx1
              return 0              ; throw it away
            @xx1:

            dec ecx
            cmp ecx, 0
            jne @xxx

            jmp accept
        .endif

        .if wParam < "0"
            return 0
        .endif

        .if wParam > "9"
            return 0
        .endif

    .endif

    accept:

    invoke CallWindowProc,lpfnEd1Proc,hCtl,uMsg,wParam,lParam

    ret

Ed1Proc endp

; #########################################################################

Ed2Proc proc hCtl   :DWORD,
             uMsg   :DWORD,
             wParam :DWORD,
             lParam :DWORD

    ; -----------------------------
    ; Process control messages here
    ; -----------------------------

    .if uMsg == WM_CHAR
        .if wParam >= "A"
          .if wParam <= "Z"
            add wParam, 32
          .endif
        .endif
    .endif

    invoke CallWindowProc,lpfnEd2Proc,hCtl,uMsg,wParam,lParam

    ret

Ed2Proc endp

; #########################################################################

Ed3Proc proc hCtl   :DWORD,
             uMsg   :DWORD,
             wParam :DWORD,
             lParam :DWORD

    ; -----------------------------
    ; Process control messages here
    ; -----------------------------

    .if uMsg == WM_CHAR
        .if wParam >= "a"
          .if wParam <= "z"
            sub wParam, 32
          .endif
        .endif
    .endif

    invoke CallWindowProc,lpfnEd3Proc,hCtl,uMsg,wParam,lParam

    ret

Ed3Proc endp

; #########################################################################

end start
