initialize RegistferPropGridClass
.wc WNDCLASS
begin
        xor     eax, eax
        lea     edi, [.wc]
        mov     esi, edi
        mov     ecx, sizeof.WNDCLASS shr 2
        rep stosd

        mov     [.wc.style], CS_PARENTDC
        mov     [.wc.lpfnWndProc], PropGridWinProc
        mov     [.wc.cbWndExtra], 4     ; pointer to the grid data.
        mov     eax, [hInstance]
        mov     [.wc.hInstance], eax
        invoke  LoadCursorW, 0, IDC_ARROW
        mov     [.wc.hCursor], eax
        mov     [.wc.hbrBackground], 0; COLOR_BTNFACE+1

        mov     [.wc.lpszClassName], cPropGridClassName

        invoke  RegisterClassW, esi
        return
endp

iglobal
  cPropGridClassName du 'TPropertyGrid',0
endg


iglobal
StandardProperties:
      PropInfo                                                                                 \
\;        'Align',   ptByte,   TWinTemplateDT.Align,   AlignPropEditor,  0,  frmAlign,       \
        'Style',   ptDword,   TWinTemplateDT.style,   StylePropEditor,  0,  frmStyleEditor, \
        'StyleEx', ptDword,   TWinTemplateDT.styleEx, IntPropEditor,    ntsHex, 0,          \
        'Left',    ptWord,   TWinTemplateDT.left,    IntPropEditor,    ntsDec, 0,       \
        'Top',     ptWord,   TWinTemplateDT.top,     IntPropEditor,    ntsDec, 0,        \
        'Width',   ptWord,   TWinTemplateDT.width,   IntPropEditor,    ntsDec, 0,          \
        'Height',  ptWord,   TWinTemplateDT.height,  IntPropEditor,    ntsDec, 0,          \
        'Name',    ptText,      TWinTemplateDT.hName,  StringPropEditor, 0, 0,                \
        'ID',      ptWord,   TWinTemplateDT.ID,      IntPropEditor,    ntsDec, 0,          \
        'Text',    ptText,      TWinTemplateDT.ptrText,   StringPropEditor, 0, frmTextEdit,   \
        'Class name', ptText,   TWinTemplateDT.ptrClass,  StringPropEditor, pfReadOnly, 0,    \
        'Msg handler', ptText,  TWinTemplateDT.WinProc,StringPropEditor, 0, 0
endg


proc PropGridWinProc, .hwnd, .wmsg, .wparam, .lparam
.rect RECT
.rectl RECT
.rectr RECT
.ps   PAINTSTRUCT
.hdc  dd ?
.si   SCROLLINFO
.maxaddr dd ?

.nm     NMHDR

begin
        push    esi edi ebx

        invoke  GetWindowLongW, [.hwnd], 0
        mov     edi, eax

        mov     ebx, [.wmsg]
        dispatch ebx

;        Messages                                \
;        WM_PAINT,   .wmpaint,                   \
;        WM_CREATE, .wmcreate,                   \
;        WM_DESTROY, .wmdestroy,                 \
;        WM_VSCROLL, .vscroll,                   \
;        WM_SIZE,    .wmsize,                    \
;        WM_GETFONT, .wmgetfont,                 \
;        WM_LBUTTONDOWN, .wmlbuttondown,         \
;        WM_COMMAND,     .wmcommand,             \
;        WM_PARENTNOTIFY, .parentnotify,         \
;        PEM_CLEARALL, .pemclear,           \
;        PEM_ADDPROPERTY, .pemaddprop,      \
;        PEM_SETCOUNT,    .pemsetcount,     \
;        PEM_SETCONTROL,  .pemsetcontrol,   \
;        PEM_GETCONTROL,  .pemgetcontrol,   \
;        PEM_SETSELECTION, .pemsetselection,\
;        PEM_UPDATECONTROL, .pemupdatecontrol,   \
;        PEM_SETEDITORTEXT, .pemseteditortext,      \
;        PEM_SETUPDATEPROC, .pemsetupdateproc,      \
;\
;        BEM_BUTTONCLICK,  .bembuttonclick       ;,      \
;\;        WM_SETFOCUS, .focus,                    \
;\;        WM_NEXTDLGCTL, .nextdlgctrl,            \
;        WM_KEYDOWN, .wmkeydown
;\;        WM_SYSCOMMAND, .syscommand


.default:

        invoke  DefWindowProcW, [.hwnd], [.wmsg], [.wparam], [.lparam]

.finish:
        pop     ebx edi esi
        return


;--------------------------------------------
.qfalse:
        xor     eax, eax
        jmp     .finish

;------------------------------------------------------------------------------
oncase PRM_UPDATEPROP

        invoke  InvalidateRect, [.hwnd], 0, FALSE
        jmp     .finish

;------------------------------------------------------------------------------

oncase WM_KEYDOWN
        cmp     [.wparam], VK_TAB
        jne     .default

;        DebugMsg "WM_KEYDOWN with VK_TAB to the property editor window!"

oncase WM_NEXTDLGCTL

        DebugMsg "WM_NEXTDLGCTL to the property editor window!"
        OutputValue "wparam = ", [.wparam], 16, 8
        OutputValue "lparam = ", [.lparam], 16, 8

        cmp     [edi+TPropGrid.ptrCtrl], 0      ; ignore if there is no control assigned.
        je      .qfalse

        invoke  SendMessageW, [.hwnd], PEM_UPDATECONTROL, 0, 0

        mov     eax, [edi+TPropGrid.iSelected]
        mov     ebx, [edi+TPropGrid.RowCount]
        dec     ebx
        cmp     [.wparam], 1
        je      .nextrow

        cmp     [.wparam], 2
        jne     .qfalse

        dec     eax
        cmovs   eax, ebx

.updatetab:
        invoke  SendMessageW, [.hwnd], PEM_SETSELECTION, eax, 0
;        invoke  SendMessageW, [edi+TPropGrid.hEditor], EM_SETSEL, 0, -1
        invoke  SetFocus, [edi+TPropGrid.hEditor]  ;[.hwnd]
        jmp     .qfalse


.nextrow:
        inc     eax
        cmp     eax, ebx
        jle     .updatetab
        xor     eax,eax
        jmp     .updatetab

;------------------------------------------------------

oncase WM_SETFOCUS

;        DebugMsg "Property editor takes focus."

        cmp     [edi+TPropGrid.hEditor], 0
        je      .default

;        DebugMsg "Property editor sets focus to the internal editor."

        invoke  SetFocus, [edi+TPropGrid.hEditor]

.notify_parent_focus:
; also, notify the parent window.

        invoke  GetAncestor, [.hwnd], GA_PARENT
        test    eax, eax
        jz      .qfalse

        mov     ebx, eax

        mov     eax, [.hwnd]
        mov     [.nm.hwndFrom], eax

        invoke  GetWindowLongW, [.hwnd], GWL_ID
        mov     [.nm.idFrom], eax
        mov     [.nm.code], NM_SETFOCUS

        lea     eax, [.nm]
        invoke  SendMessageW, ebx, WM_NOTIFY, [.nm.idFrom], eax

        jmp     .qfalse


;------- WM_NOTIFY ------------------------

oncase WM_NOTIFY

        mov     eax, [.lparam]
        cmp     [eax+NMHDR.code], NM_SETFOCUS
        je      .notify_parent_focus

        jmp     .default


;--------- WM_PARENTNOTIFY ----------------

oncase WM_PARENTNOTIFY

        cmp     word [.wparam], WM_CREATE
        jne     @f

        invoke  GetPropW, [.lparam], [propOwnFont]
        test    eax, eax
        jnz     @f

        invoke  SendMessageW, [.hwnd], WM_GETFONT, 0, 0
        invoke  SendMessageW, [.lparam], WM_SETFONT, eax, TRUE

@@:
        jmp     .qfalse


;--------- WM_COMMAND ---------------------

oncase WM_COMMAND

        cmp     word [.wparam], IDOK
        je      .enter_key

        cmp     eax, EN_SETFOCUS
        je      .editor_focused

        jmp     .qfalse

.editor_focused:
        invoke  SendMessageW, [edi+TPropGrid.hEditor], EM_SETSEL, 0, -1
        jmp     .qfalse

.enter_key:
        invoke  SendMessageA, [.hwnd], PEM_UPDATECONTROL, 0, 0
        invoke  SendMessageW, [edi+TPropGrid.hEditor], EM_SETSEL, 0, -1
        jmp     .qfalse


;---------- WM_LBUTTONDOWN ------------------

oncase WM_LBUTTONDOWN

        movsx   ebx, word [.lparam+2]
        sar     ebx, 4

        invoke  GetScrollPos, [.hwnd], SB_VERT
        add     ebx, eax
        js      .default

        cmp     ebx, [edi+TPropGrid.RowCount]
        jge     .default

        invoke  SendMessageW, [.hwnd], PEM_SETSELECTION, ebx, 0
        jmp     .default


;---------- WM_GETFONT ----------------------

oncase WM_GETFONT

        stdcall GetDefaultFont, gdfMessage
        jmp     .finish

;----------- BEM_BUTTONCLICK ----------------

oncase BEM_BUTTONCLICK

        cmp     [edi+TPropGrid.hEditor], 0
        je      .qfalse

        mov     ebx, [edi+TPropGrid.iSelected]
        imul    ebx, sizeof.TPropInfo
        add     ebx, [edi+TPropGrid.ptrArray]

        cmp     [ebx+TPropInfo.Form], 0
        je      .qfalse

        lea     eax, [.rect]
        invoke  GetWindowRect, [edi+TPropGrid.hEditor], eax

        stdcall CreateForm, [ebx+TPropInfo.Form], [.hwnd]
        invoke  SetWindowPos, ebx, 0, [.rect.left], [.rect.bottom], 0, 0, SWP_NOSIZE or SWP_NOZORDER

        invoke  SetWindowLongW, ebx, 4, [edi+TPropGrid.ptrCtrl]

        stdcall GetControlTextUtf8, [edi+TPropGrid.hEditor]
        stdcall SetDlgItemTextUtf8, ebx, 1000, eax
        stdcall StrDel, eax


        stdcall ShowModal, ebx, 0
        push    eax
        invoke  DestroyWindow, ebx
        pop     eax

        cmp     eax, MR_OK
        jne     .qfalse

        invoke  SendMessageW, [.hwnd], PEM_UPDATECONTROL, 0, 0
        invoke  SetFocus, [.hwnd]

        jmp     .qfalse

;----------PEM_SETEDITORTEXT-----------------

oncase PEM_SETEDITORTEXT

        cmp     [edi+TPropGrid.hEditor], 0
        je      .qfalse

        stdcall SetControlTextUtf8, [edi+TPropGrid.hEditor], [.lparam]
        jmp     .qfalse

;---------- PEM_UPDATECONTROL ---------------

oncase PEM_UPDATECONTROL

;        DebugMsg "PEM_UPDATECONTROL"

        cmp     [edi+TPropGrid.hEditor], 0
        je      .baseupdated

        mov     ebx, [edi+TPropGrid.iSelected]
        imul    ebx, sizeof.TPropInfo
        add     ebx, [edi+TPropGrid.ptrArray]

        cmp     [ebx+TPropInfo.procEditor], 0
        je      .qfalse

        stdcall [ebx+TPropInfo.procEditor], [edi+TPropGrid.ptrCtrl], ebx, pecUpdateWindow, NULL, [edi+TPropGrid.hEditor]
        test    eax, eax
        jnz     .baseupdated

.baseupdated:
        cmp     [edi+TPropGrid.ptrCtrl], 0
        je      .qfalse

        cmp     [edi+TPropGrid.ptrUpdateProc], 0
        je      .qfalse

        stdcall [edi+TPropGrid.ptrUpdateProc], [edi+TPropGrid.ptrCtrl]

        mov     eax, [edi+TPropGrid.ptrCtrl]

;        stdcall ProcessMessages
;        invoke  SetFocus, [.hwnd]

; Mark control as modified...

        mov     eax, [edi+TPropGrid.ptrCtrl]
.search:
        cmp     [eax+TWinTemplateDT.parent], 0
        je      .found
        mov     eax, [eax+TWinTemplateDT.parent]
        jmp     .search

.found:
        mov     eax, [eax+TWinTemplateDT.ptrFile]
        test    eax, eax
        jz      .qfalse

        invoke  SendMessageW, [eax+TOpenFile.hEditor], _CEM_SETMODIFIED, TRUE, 0
        jmp     .qfalse

;---------- PEM_SETSELECTION ----------------

oncase PEM_SETSELECTION

        mov     ebx, [.wparam]
        test    ebx, ebx
        js      .update

        cmp     ebx, [edi+TPropGrid.RowCount]
        jge     .update

        mov     esi, [edi+TPropGrid.hEditor]

        cmp     ebx, [edi+TPropGrid.iSelected]
        jne     .select_new

        test    esi, esi
        jnz     .focus_editor

.select_new:

        mov     [edi+TPropGrid.iSelected], ebx

        imul    ebx, sizeof.TPropInfo
        add     ebx, [edi+TPropGrid.ptrArray]

        cmp     [ebx+TPropInfo.procEditor], NULL
        je      .update

        xor     eax, eax
        stdcall [ebx+TPropInfo.procEditor], [edi+TPropGrid.ptrCtrl], ebx, pecGetEditor, eax, [.hwnd]

        mov     esi, eax
        stdcall SubclassWindow, esi, PropEditorProc

        xchg    esi, [edi+TPropGrid.hEditor]
        test    esi, esi
        jz      .focus_editor

        invoke  DestroyWindow, esi

.focus_editor:

;        DebugMsg "Try to focus the property editor."
        invoke  SetFocus, [edi+TPropGrid.hEditor]      ;[.hwnd]

.update:
        call    .updateeditor
        jmp     .qfalse

;---------- PEM_SETCONTROL ------------------

oncase PEM_SETCONTROL

        invoke  SendMessageW, [.hwnd], PEM_CLEARALL, 0, 0
        mov     eax, [.wparam]
        mov     [edi+TPropGrid.ptrCtrl], eax
        test    eax, eax
        jz      .set_finished

        mov     eax, [.lparam]
        test    eax, eax
        jnz     .add_prop

        mov     eax, StandardProperties

.add_prop:
        invoke  SendMessageW, [.hwnd], PEM_ADDPROPERTY, 0, eax

.set_finished:
        stdcall __RefreshLeftPane
        jmp     .qfalse

;---------- PEM_GETCONTROL ------------------

oncase PEM_GETCONTROL

        mov     eax, [edi+TPropGrid.ptrCtrl]
        jmp     .finish

;---------- PEM_SETUPDATEPROC ---------------

oncase PEM_SETUPDATEPROC

        mov     eax, [.lparam]
        mov     [edi+TPropGrid.ptrUpdateProc], eax
        jmp     .qfalse

;---------- WM_SIZE -------------------------

oncase WM_SIZE

        invoke  SendMessageW, [.hwnd], PEM_SETCOUNT, [edi+TPropGrid.RowCount], 0
        call    .updateeditor
        jmp     .qfalse

;---------- WM_VSCROLL ----------------------

oncase WM_VSCROLL

        movzx   ebx, word [.wparam]
        movzx   esi, word [.wparam+2]

        cmp     ebx, SB_THUMBPOSITION
        je      .setit
        cmp     ebx, SB_THUMBTRACK
        je      .setit

        invoke  GetScrollPos, [.hwnd], SB_VERT
        mov     esi, eax

        inc     esi
        cmp     ebx, SB_LINEDOWN
        je      .setit
        cmp     ebx, SB_PAGEDOWN
        je      .setit
        sub     esi, 2
        cmp     ebx, SB_LINEUP
        je      .setit
        cmp     ebx, SB_PAGEUP
        je      .setit

        jmp     .qfalse


.setit:
        invoke  SetScrollPos, [.hwnd], SB_VERT, esi, TRUE
        call    .updateeditor
;        invoke  InvalidateRect, [.hwnd], NULL, TRUE
        jmp     .qfalse


;---------- PEM_SETCOUNT --------------------

oncase PEM_SETCOUNT

        mov     ebx, [.wparam]
        test    ebx, ebx
        js      .qfalse

        mov     [edi+TPropGrid.RowCount], ebx
        imul    ebx, sizeof.TPropInfo
        test    ebx, ebx
        jnz     @f
        mov     ebx, sizeof.TPropInfo
@@:
        stdcall ResizeMem, [edi+TPropGrid.ptrArray], ebx
        mov     [edi+TPropGrid.ptrArray], eax

        mov     [.si.cbSize], sizeof.SCROLLINFO
        mov     [.si.fMask], SIF_PAGE or SIF_RANGE; or SIF_DISABLENOSCROLL
        mov     [.si.nMin], 0
        push    [.wparam]
        pop     [.si.nMax]

        lea     eax, [.rect]
        invoke  GetClientRect, [.hwnd], eax
        mov     eax, [.rect.bottom]
        shr     eax, 4
        inc     eax
        mov     [.si.nPage], eax

        lea     ecx, [.si]
        invoke  SetScrollInfo, [.hwnd], SB_VERT, ecx, TRUE
        jmp     .qfalse

;---------- PEM_CLEARALL --------------------

oncase PEM_CLEARALL

        cmp     [edi+TPropGrid.RowCount], 0
        je      .endfree

        mov     esi, [edi+TPropGrid.ptrArray]

.freeloop:
        stdcall StrDel, [esi+TPropInfo.hName]
        add     esi, sizeof.TPropInfo
        dec     [edi+TPropGrid.RowCount]
        jnz     .freeloop

.endfree:
        cmp     [edi+TPropGrid.hEditor], 0
        je      @f
        invoke  DestroyWindow, [edi+TPropGrid.hEditor]
        mov     [edi+TPropGrid.hEditor], 0
@@:
        invoke  SendMessageW, [.hwnd], PEM_SETCOUNT, 0, 0
        invoke  InvalidateRect, [.hwnd], NULL, TRUE
        jmp     .qfalse

;--------- PEM_ADDPROPERTY ---------------

oncase PEM_ADDPROPERTY

        mov     esi, [.lparam]
        test    esi, esi
        je      .qfalse

        mov     ebx, [edi+TPropGrid.RowCount]
        lodsd
        push    eax
        add     eax, ebx

        invoke  SendMessageW, [.hwnd], PEM_SETCOUNT, eax, 0

;        mov     [edi+TPropGrid.iSelected], ebx

        imul    ebx, sizeof.TPropInfo
        add     ebx, [edi+TPropGrid.ptrArray]
        pop     ecx

.addloop:
        stdcall StrNew
        mov     [ebx+TPropInfo.hName], eax
        stdcall StrCopy, eax, [esi+TPropInfo.hName]

        push    [esi+TPropInfo.type]
        pop     [ebx+TPropInfo.type]
        push    [esi+TPropInfo.procEditor]
        pop     [ebx+TPropInfo.procEditor]
        push    [esi+TPropInfo.ofsVal]
        pop     [ebx+TPropInfo.ofsVal]
        push    [esi+TPropInfo.Tag]
        pop     [ebx+TPropInfo.Tag]
        push    [esi+TPropInfo.Form]
        pop     [ebx+TPropInfo.Form]

        add     esi, sizeof.TPropInfo
        add     ebx, sizeof.TPropInfo
        loop    .addloop

;        invoke  SendMessageA, [.hwnd], PEM_SETSELECTION, [edi+TPropGrid.iSelected], 0
        jmp     .qfalse

;------------ WM_CREATE ------------------

oncase WM_CREATE

        stdcall GetMem, sizeof.TPropGrid
        mov     edi, eax
        stdcall GetMem, sizeof.TPropInfo
        mov     [edi+TPropGrid.ptrArray], eax

        invoke  SetWindowLongW, [.hwnd], 0, edi
        invoke  SetScrollRange, [.hwnd], SB_VERT, 0, 0, FALSE

        ; LITTLE BIT TRICKY, but unfortunately I couldn't think it in more smart way.
        invoke  CreateWindowExW, 0, cEditClassName, NULL, WS_CHILD or WS_TABSTOP, 0, 0, 0, 0, [.hwnd], 101, [hInstance], NULL
        stdcall SubclassWindow, eax, NextEditorWinProc

        invoke  CreateWindowExW, 0, cEditClassName, NULL, WS_CHILD or WS_TABSTOP, 0, 0, 0, 0, [.hwnd], 102, [hInstance], NULL
        stdcall SubclassWindow, eax, NextEditorWinProc
        jmp     .qfalse

;----------- WM_DESTROY -------------------

oncase WM_DESTROY

        invoke  SendMessageW, [.hwnd], PEM_CLEARALL, 0, 0

        stdcall FreeMem, [edi+TPropGrid.ptrArray]
        stdcall FreeMem, edi
        jmp     .qfalse


;-------- WM_PAINT -----------------------

oncase WM_PAINT

        lea     eax, [.ps]
        invoke  BeginPaint, [.hwnd], eax
        test    eax, eax
        jz      .quitpaint

        mov     [.hdc], eax

        lea     ebx, [.rect]
        invoke  GetClientRect, [.hwnd], ebx
        xor     eax, eax
        mov     ecx, [.rect.right]
        mov     edx, [.rect.right]
        shr     ecx, 1
        mov     [.rectl.left], eax
        mov     [.rectl.right], ecx
        mov     [.rectl.top], eax
        mov     [.rectl.bottom], 15
        mov     [.rectr.left], ecx
        mov     [.rectr.right], edx
        mov     [.rectr.top], eax
        mov     [.rectr.bottom], 15

        invoke  CreatePen, PS_SOLID, 0, $a0a0a0
        invoke  SelectObject, [.hdc], eax
        push    eax

        invoke  GetStockObject, BLACK_BRUSH
        invoke  SelectObject, [.hdc], eax
        push    eax

        invoke  SetBkMode, [.hdc], TRANSPARENT

        stdcall GetDefaultFont, gdfMessage
        invoke  SelectObject, [.hdc], eax
        push    eax

; Draw properties
        invoke  GetScrollPos, [.hwnd], SB_VERT
        mov     ebx, eax
        imul    ebx, sizeof.TPropInfo

        add     ebx, [edi+TPropGrid.ptrArray]
        mov     esi, [edi+TPropGrid.RowCount]
        imul    esi, sizeof.TPropInfo
        add     esi, [edi+TPropGrid.ptrArray]
        mov     [.maxaddr], esi

        mov     esi, eax

.foundbeg:
        cmp     ebx, [.maxaddr]
        jge     .cleartoend

        lea     ecx, [.rectl]
        invoke  FillRect, [.hdc], ecx, COLOR_BTNFACE+1

        invoke  MoveToEx, [.hdc], [.rectl.left], [.rectl.bottom], NULL
        invoke  LineTo, [.hdc], [.rectl.right], [.rectl.bottom]

        stdcall utf8ToWideChar, [ebx+TPropInfo.hName]
        push    eax

        add     [.rectl.left], 2
        lea     ecx, [.rectl]
        invoke  DrawTextW, [.hdc], eax, -1, ecx, DT_TOP or DT_END_ELLIPSIS or DT_LEFT or DT_NOPREFIX or DT_SINGLELINE
        stdcall FreeMem ; from the stack
        sub     [.rectl.left], 2

        lea     eax, [.rectr]
        invoke  FillRect, [.hdc], eax, COLOR_BTNFACE+1
        invoke  MoveToEx, [.hdc], [.rectr.left], [.rectr.bottom], NULL
        invoke  LineTo, [.hdc], [.rectr.right], [.rectr.bottom]

        cmp     esi, [edi+TPropGrid.iSelected]
        jne     @f

        lea     eax, [.rectl]
        invoke  DrawFocusRect, [.hdc], eax

        cmp     [edi+TPropGrid.hEditor], 0
        je      @f

        invoke  GetFocus
        cmp     eax, [edi+TPropGrid.hEditor]
        je      @f

        stdcall [ebx+TPropInfo.procEditor], [edi+TPropGrid.ptrCtrl], ebx, pecRefreshEditor, eax, [edi+TPropGrid.hEditor]

@@:
        cmp     [edi+TPropGrid.ptrCtrl], NULL
        je      .cleartoend

        cmp     [ebx+TPropInfo.procEditor], NULL
        je      .nextline

        add     [.rectr.left], 2
        lea     eax, [.rectr]
        stdcall [ebx+TPropInfo.procEditor], [edi+TPropGrid.ptrCtrl], ebx, pecDrawValue, eax, [.hdc]
        sub     [.rectr.left], 2

.nextline:
        add     [.rectl.top], 16
        add     [.rectl.bottom], 16
        add     [.rectr.top], 16
        add     [.rectr.bottom], 16
        add     [.rect.top], 16

        mov     eax, [.rectl.top]
        cmp     eax, [.rect.bottom]
        jge     .endpaint

        add     ebx, sizeof.TPropInfo
        inc     esi
        jmp     .foundbeg

.cleartoend:
        lea     eax, [.rect]
        invoke  FillRect, [.hdc], eax, COLOR_BTNFACE+1

.endpaint:
        xor     eax, eax
        xchg    eax, [.rectr.top]
        mov     [.rectr.bottom], eax
        lea     eax, [.rectr]
        invoke  DrawEdge, [.hdc], eax, EDGE_ETCHED, BF_LEFT

        invoke  SelectObject, [.hdc] ; from the stack
        invoke  DeleteObject, eax
        invoke  SelectObject, [.hdc] ; from the stack
        invoke  SelectObject, [.hdc] ; from the stack
        invoke  DeleteObject, eax

.quitpaint:
        lea     eax, [.ps]
        invoke  EndPaint, [.hwnd], eax

        jmp     .qfalse


        enddispatch


.updateeditor:
        cmp     [edi+TPropGrid.hEditor], NULL
        je      .updated

        lea     eax, [.rect]
        invoke  GetClientRect, [.hwnd], eax

        invoke  GetScrollPos, [.hwnd], SB_VERT
        mov     ebx, [edi+TPropGrid.iSelected]

        sub     ebx, eax
        js      .hideeditor

        shl     ebx, 4         ; Y Coordinate.
        cmp     ebx, [.rect.bottom]
        jge     .hideeditor

.showeditor:
        mov     eax, [.rect.right]
        shr     eax, 1
        add     eax, 2
        mov     ecx, [.rect.right]
        sub     ecx, eax
        invoke  SetWindowPos, [edi+TPropGrid.hEditor], 0, eax, ebx, ecx, 14, SWP_SHOWWINDOW or SWP_NOZORDER

        invoke  GetDlgItem, [.hwnd], 101
        invoke  SetWindowPos, eax, [edi+TPropGrid.hEditor], [.rect.right], ebx, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_SHOWWINDOW or SWP_NOACTIVATE
        invoke  GetDlgItem, [.hwnd], 102
        invoke  ShowWindow, eax, SW_SHOWNOACTIVATE

        DebugMsg "Editor shown"

        jmp     .updated

.hideeditor:
        DebugMsg "Editor hidden"

        invoke  ShowWindow, [edi+TPropGrid.hEditor], SW_HIDE
        invoke  GetDlgItem, [.hwnd], 101
        invoke  ShowWindow, eax, SW_HIDE
        invoke  GetDlgItem, [.hwnd], 102
        invoke  ShowWindow, eax, SW_HIDE

.updated:
        invoke  InvalidateRect, [.hwnd], NULL, TRUE
        ret
endp



proc PropEditorProc, .hwnd, .wmsg, .wparam, .lparam
.nm NMHDR
begin

        dispatch [.wmsg]

.ondefault:
        stc
        return


oncase WM_SETFOCUS

        invoke  SendMessageW, [.hwnd], EM_SETSEL, 0, -1

        invoke  GetAncestor, [.hwnd], GA_PARENT
        test    eax, eax
        jz      .ondefault

        mov     ebx, eax

        mov     eax, [.hwnd]
        mov     [.nm.hwndFrom], eax

        invoke  GetWindowLongW, [.hwnd], GWL_ID
        mov     [.nm.idFrom], eax
        mov     [.nm.code], NM_SETFOCUS

        lea     eax, [.nm]
        invoke  SendMessageW, ebx, WM_NOTIFY, [.nm.idFrom], eax

        jmp     .ondefault


        enddispatch
endp




proc NextEditorWinProc, .hwnd, .wmsg, .wparam, .lparam
begin
        mov     ebx, [.wmsg]
        dispatch ebx

        stc
        return

oncase WM_SETFOCUS

        invoke  GetWindowLongW, [.hwnd], GWL_ID
        lea     ebx, [eax-100]

        OutputValue "FAKE CONTROL GET FOCUS ID: ", eax, 10, -1

        invoke  GetParent, [.hwnd]
        mov     esi, eax

        invoke  PostMessageW, esi, WM_NEXTDLGCTL, ebx, 0

;        invoke  SendMessageW, esi, PEM_UPDATECONTROL, 0, 0

        xor     eax, eax
        clc
        return

        enddispatch
endp




proc IntPropEditor, .ptrCtrl, .ptrPropInfo, .cmd, .ptrRect, .lParam
begin
        push    edi esi edx ecx ebx eax

        cmp     [.cmd], pecDrawValue
        je      .draw

        cmp     [.cmd], pecGetEditor
        je      .geteditor

        cmp     [.cmd], pecRefreshEditor
        je      .refresheditor

        cmp     [.cmd], pecUpdateWindow
        jne     .finish

.updatewindow:

        stdcall GetControlTextUtf8, [.lParam]
        push    eax

        stdcall StrToFasmNum, eax
        stdcall StrDel ; from the stack
        jc      .finish

        mov     esi, [.ptrPropInfo]
        mov     ecx, [esi+TPropInfo.ofsVal]
        add     ecx, [.ptrCtrl]

        movzx   edx, byte [ecx]
        mov     [ecx], al
        cmp     [esi+TPropInfo.type], ptByte
        je      .changed
        movzx   edx, word [ecx]
        mov     [ecx], ax
        cmp     [esi+TPropInfo.type], ptWord
        je      .changed
        mov     edx, [ecx]
        mov     [ecx], eax

.changed:
        xor     ecx, ecx
        cmp     edx, eax
        je      @f
        inc     ecx
@@:
        mov     [esp], ecx
        jmp     .finish


.refresheditor:
        call    .createstr

        stdcall SetControlTextUtf8, [.lParam], eax
        stdcall StrDel, eax

        jmp     .finish

.geteditor:
        invoke  CreateWindowExW, 0, cEditClassName, eax,                  \
                WS_CHILD or WS_CLIPSIBLINGS or                            \
                ES_AUTOHSCROLL,                                           \
                0, 0, 56, 16, [.lParam],                                  \
                NULL, [hInstance], NULL
        mov     [esp], eax
        mov     ebx, eax
        stdcall IntPropEditor, [.ptrCtrl], [.ptrPropInfo], pecRefreshEditor, NULL, ebx
        jmp     .finish

.draw:
        call    .createstr
        push    eax

        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack
        push    eax

        invoke  DrawTextW, [.lParam], eax, -1, [.ptrRect], DT_TOP or DT_END_ELLIPSIS or DT_LEFT or DT_NOPREFIX or DT_SINGLELINE
        stdcall FreeMem ; from the stack

.finish:
        pop     eax ebx ecx edx esi edi
        return

.createstr:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        mov     ecx, [esi+TPropInfo.Tag]

        movsx   edx, byte [eax]
        cmp     [esi+TPropInfo.type], ptByte
        je      .sign

        movsx   edx, word [eax]
        cmp     [esi+TPropInfo.type], ptWord
        je      .sign

        mov     edx, [eax]
        jmp     .convert

.sign:
        test    ecx, ntsUnsigned
        jz      .convert

        and     edx, $ffff
        cmp     [esi+TPropInfo.type], ptWord
        je      .convert

        and     edx, $ff

.convert:
        stdcall NumToStr, edx, ecx
        mov     ecx, [esi+TPropInfo.Tag]
        and     ecx, $ff00

        cmp     ecx, ntsHex
        jne     @f
        stdcall StrCharInsert, eax, '$', 0
        jmp     .doit
@@:
        cmp     ecx, ntsBin
        jne     .doit
        stdcall StrCharCat, eax, 'b'
.doit:
        retn
endp




proc StringPropEditor, .ptrCtrl, .ptrPropInfo, .cmd, .ptrRect, .lParam
begin
        push    edi esi edx ecx ebx eax

        cmp     [.cmd], pecDrawValue
        je      .draw

        cmp     [.cmd], pecGetEditor
        je      .geteditor

        cmp     [.cmd], pecRefreshEditor
        je      .refresheditor

        cmp     [.cmd], pecUpdateWindow
        jne     .finish

.updatewindow:

        stdcall GetControlTextUtf8, [.lParam]
        mov     ebx, eax

        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        xor     ecx, ecx
        stdcall StrCompCase, [eax], ebx
        jnc     .copy

        mov     dword [esp], 0
        jmp     .finish

.copy:
        stdcall StrCopy, [eax], ebx
        mov     dword [esp], 1

        stdcall StrDel, ebx
        jmp     .finish

.refresheditor:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        stdcall SetControlTextUtf8, [.lParam], [eax]
        jmp     .finish

.geteditor:
        mov     eax, WS_CHILD or ES_AUTOHSCROLL or WS_CLIPCHILDREN
        mov     ecx, [.ptrPropInfo]
        test    [ecx+TPropInfo.Tag], pfReadOnly
        jz      @f
        or      eax, ES_READONLY
@@:
        invoke  CreateWindowExW, 0, cButtonEditClassName, 0, eax,          \
                0, 0, 56, 16, [.lParam],                             \
                NULL, [hInstance], NULL
        mov     [esp], eax
        mov     ebx, eax

        invoke  SendMessageW, ebx, BEM_SETBUTTONTEXT, 0, cElipsis
        stdcall StringPropEditor, [.ptrCtrl], [.ptrPropInfo], pecRefreshEditor, NULL, ebx
        jmp     .finish

.draw:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]
        mov     eax, [eax]
        test    eax, eax
        jz      .finish

        stdcall utf8ToWideChar, eax
        push    eax
        invoke  DrawTextW, [.lParam], eax, -1, [.ptrRect], DT_TOP or DT_END_ELLIPSIS or DT_LEFT or DT_NOPREFIX or DT_SINGLELINE
        stdcall FreeMem ; from the stack

.finish:
        pop     eax ebx ecx edx esi edi
        return
endp


iglobal
  tblAlignProp  dd  cwaNone
                dd  cwaLeft
                dd  cwaRight
                dd  cwaTop
                dd  cwaBottom
                dd  cwaClient
  tblAlignEnd:
                dd  cwaError
  cwaNone   du 'waNone = 0',0
  cwaLeft   du 'waLeft = 1',0
  cwaRight  du 'waRight = 2',0
  cwaTop    du 'waTop = 3',0
  cwaBottom du 'waBottom = 4',0
  cwaClient du 'waClient = 5',0
  cwaError  du 'waError',0

  cElipsis du '...',0
endg


proc AlignPropEditor, .ptrCtrl, .ptrPropInfo, .cmd, .ptrRect, .lParam
.str rb 256
begin
        push    edi esi edx ecx ebx eax

        cmp     [.cmd], pecDrawValue
        je      .draw

        cmp     [.cmd], pecGetEditor
        je      .geteditor

        cmp     [.cmd], pecRefreshEditor
        je      .refresheditor

        cmp     [.cmd], pecUpdateWindow
        jne     .finish

.updatewindow:
        lea     ebx, [.str]
        invoke  SendMessageW, [.lParam], WM_GETTEXT, 128, ebx

        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        mov     ecx, dword [.str]
        cmp     ecx, '0'
        jb      .finish
        cmp     ecx, '5'
        ja      .finish

        sub     cl, '0'
        xchg    byte [eax], cl

        xor     edx, edx
        cmp     [eax], cl
        je      @f
        inc     edx
@@:
        mov     [esp], edx
        jmp     .finish

.refresheditor:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        movzx   eax, byte [eax]
        stdcall NumToStr, eax, ntsDec

        stdcall SetControlTextUtf8, [.lParam], eax
        stdcall StrDel, eax
        jmp     .finish

.geteditor:
        invoke  CreateWindowExW, 0, cButtonEditClassName, eax,                  \
                WS_CHILD or WS_CLIPCHILDREN                                     \
                ES_NUMBER ,                                                     \
                0, 0, 56, 16, [.lParam],                                        \
                NULL, [hInstance], NULL
        mov     [esp], eax
        mov     ebx, eax
        invoke  SendMessageW, ebx, BEM_SETBUTTONTEXT, 0, cElipsis
        stdcall AlignPropEditor, [.ptrCtrl], [.ptrPropInfo], pecRefreshEditor, NULL, ebx
        jmp     .finish

.draw:
;        mov     esi, [.ptrPropInfo]
;        mov     eax, [esi+TPropInfo.ofsVal]
;        add     eax, [.ptrCtrl]
;        movzx   eax, byte [eax]
;        cmp     eax, waClient
;        jbe     @f
;        mov     eax, waClient+1
;@@:
;        mov     eax, [tblAlignProp+eax*4]
;        invoke  DrawTextW, [.lParam], eax, -1, [.ptrRect], DT_TOP or DT_END_ELLIPSIS or DT_LEFT or DT_NOPREFIX or DT_SINGLELINE

.finish:
        pop     eax ebx ecx edx esi edi
        return
endp



proc StylePropEditor, .ptrCtrl, .ptrPropInfo, .cmd, .ptrRect, .lParam
begin
        push    edi esi edx ecx ebx eax

        cmp     [.cmd], pecDrawValue
        je      .draw

        cmp     [.cmd], pecGetEditor
        je      .geteditor

        cmp     [.cmd], pecRefreshEditor
        je      .refresheditor

        cmp     [.cmd], pecUpdateWindow
        jne     .finish

.updatewindow:

        stdcall GetControlTextUtf8, [.lParam]
        push    eax
        stdcall StrToFasmNum, eax
        stdcall StrDel ; from the stack

        jc      .finish

        mov     esi, [.ptrPropInfo]
        mov     ecx, [esi+TPropInfo.ofsVal]
        add     ecx, [.ptrCtrl]
        xchg    [ecx], eax

        xor     edx, edx
        cmp     eax, [ecx]
        je      @f
        inc     edx
@@:
        mov     [esp], edx
        jmp     .finish


.refresheditor:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        stdcall NumToStr, [eax], ntsHex
        push    eax
        stdcall StrCharInsert, eax, '$', 0

        stdcall SetControlTextUtf8, [.lParam], eax
        stdcall StrDel ; From the stack
        jmp     .finish


.geteditor:
        invoke  CreateWindowExW, 0, cButtonEditClassName, eax,   \
                WS_CHILD or WS_CLIPCHILDREN or ES_AUTOHSCROLL,   \
                0, 0, 56, 16, [.lParam],                         \
                NULL, [hInstance], NULL

        mov     [esp], eax
        mov     ebx, eax
        invoke  SendMessageW, ebx, BEM_SETBUTTONTEXT, 0, cElipsis
        stdcall StylePropEditor, [.ptrCtrl], [.ptrPropInfo], pecRefreshEditor, NULL, ebx
        jmp     .finish

.draw:
        mov     esi, [.ptrPropInfo]
        mov     eax, [esi+TPropInfo.ofsVal]
        add     eax, [.ptrCtrl]

        stdcall NumToStr, [eax], ntsHex
        push    eax

        stdcall StrCharInsert, eax, '$', 0
        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack
        push    eax

        invoke  DrawTextW, [.lParam], eax, -1, [.ptrRect], DT_TOP or DT_END_ELLIPSIS or DT_LEFT or DT_NOPREFIX or DT_SINGLELINE
        stdcall FreeMem ; From stack

.finish:
        pop     eax ebx ecx edx esi edi
        return
endp




;----------------------------------------
; Maybe slow but compatible in all cases.
;
; Convert the string in hString to number
; using FASM internal function get_number
;
; Returns: 64bit value in edx:eax
; Carry = 0 if the number is converted
; Carry = 1 if the number is invalid.
;----------------------------------------
proc StrToFasmNum, .hString
.str rb 256
.res rd 2
begin
        push    esi edi ebx

        xor     eax, eax
        mov     [.res], eax
        mov     [.res+4], eax

        stdcall StrLen, [.hString]
        cmp     eax, 255
        jge     .finish

        movzx   ecx, al
        mov     [.str], $1a
        mov     [.str+1], al
        lea     edi, [.str+2]

        stdcall StrPtr, [.hString]
        mov     esi, eax
        rep movsb

        lea     esi, [.str]
        lea     edi, [.res]
        push    ebp
        call    get_number
        pop     ebp

.finish:
        mov     eax, [.res]
        mov     edx, [.res+4]

        pop     ebx edi esi
        return
endp



iglobal
  Window  frmAlign, $3, $0, 'TForm', '', $96000000, $0, 0, 232, 217, 200, 128, ProcAlignEditor
  Window  NONE, $0, $4, 'Button', 'waBottom', $50008000, $0, 104, 0, 71, 194, 31, NULL
  Window  NONE, $0, $5, 'Button', 'waClient', $50008000, $0, 105, 64, 31, 66, 40, NULL
  Window  NONE, $0, $3, 'Button', 'waTop',    $50008000, $0, 103, 0, 0, 194, 31, NULL
  Window  NONE, $0, $1, 'Button', 'waLeft',   $50008000, $0, 101, 0, 31, 64, 40, NULL
  Window  NONE, $2, $2, 'Button', 'waRight',  $50008000, $0, 102, 130, 31, 64, 40, NULL

  Window  frmTextEdit, $3, $0, 'TForm', 'Control text', $96810000, $0, 0, 209, 195, 320, 216, frmTextEditWinProc
  Window  NONE, $0, $3, 'Edit', '', $503010C4, $200, 1000, 0, 0, 314, 176, NULL
  Window  NONE, $0, $0, 'Button', 'Cancel', $50000000, $0, MR_CANCEL, 168, 184, 64, 21, NULL
  Window  NONE, $2, $0, 'Button', 'OK', $50000001, $0, MR_OK, 80, 184, 64, 21, NULL
endg


proc ProcAlignEditor, .hwnd, .wmsg, .wparam, .lparam
begin
        cmp     [.wmsg], WM_COMMAND
        je      .command

.default:
        stc
        return

.command:
        cmp     word [.wparam+2], BN_CLICKED
        jne     .default

        movzx   esi, word [.wparam]
        sub     esi, 100

        invoke  GetWindowLongW, [.hwnd], GWL_HWNDPARENT
        invoke  GetDlgItem, eax, 101
        mov     ebx, eax

        stdcall NumToStr, esi, ntsDec
        push    eax
        stdcall StrPtr, eax
        invoke  SendMessageW, ebx, PEM_SETEDITORTEXT, 0, eax
        stdcall StrDel

        invoke  SetPropW, [.hwnd], [propModalResult], MR_OK
        clc
        return
endp



proc frmTextEditWinProc, .hwnd, .wmsg, .wparam, .lparam
begin
        cmp     [.wmsg], WM_SHOWWINDOW
        je      .show

.default:
        stc
        return

.show:
        cmp     [.wparam], FALSE
        jne     .default

        invoke  GetWindowLongW, [.hwnd], GWL_HWNDPARENT
        mov     ebx, eax

        stdcall GetDlgItemTextUtf8, [.hwnd], 1000
        push    eax

        stdcall StrPtr, eax
        invoke  SendMessageW, ebx, PEM_SETEDITORTEXT, 0, eax

        stdcall StrDel ; from the stack
        jmp     .default
endp