uglobal
  hCCList         dd 0
endg

idCCList = 763


proc CreateCCList, .parent
begin
        push    ebx

        invoke  CreateWindowExA, 0, cFormClassName, 0, WS_POPUP or WS_SIZEBOX or WS_CLIPSIBLINGS or WS_CLIPCHILDREN,   \
                                0, 0, 300, 120, 0, 0, [hInstance], 0
        mov     [hCCList], eax
        invoke  SetWindowLongA, [hCCList], GWL_HWNDPARENT, [.parent]
        stdcall SubclassWindow, [hCCList], CCParentProc

        invoke  CreateWindowExA, 0,  cListboxClassName,  0,              \
                WS_CHILD or WS_VISIBLE or WS_VSCROLL or LBS_NOINTEGRALHEIGHT or LBS_OWNERDRAWFIXED or LBS_NODATA, \
                0, 0, 300, 120, [hCCList], idCCList, [hInstance], 0
        mov     ebx, eax
        stdcall SubclassWindow, ebx, CCListProc
        stdcall SetAlign, ebx, waClient

        invoke  GetStockObject, DEFAULT_GUI_FONT
        invoke  SendMessageA, ebx, WM_SETFONT, eax, TRUE

        pop     ebx
        return
endp



proc AddCCListItem, .hStr, .ptrLabel
begin
        push    eax ecx edx

        invoke  GetWindowLongA, [hCCList], GWL_USERDATA
        stdcall AddArrayItems, eax, 1
        pushd    [.ptrLabel]
        popd     [eax]
        invoke   SetWindowLongA, [hCCList], GWL_USERDATA, edx

        pop     edx ecx eax
        return
endp


; returns pointer to the string in ebx
proc GetCCListSelected
begin
        push    eax ecx edx

        invoke  SendMessageA, [hCCList], LB_GETCURSEL, 0, 0
        add     eax, 1
        jc      .err

        mov     ebx, eax

        invoke  GetWindowLongA, [hCCList], GWL_USERDATA
        cmp     [eax+TArray.count], ebx
        jb      .err

        mov     ebx, [eax+TArray.array+4*ebx-4]
        mov     ebx, [ebx+TLabel.iName]
        add     ebx, [ptrNames]

        clc
.err:
        pop     edx ecx eax
        return
endp



proc ClearCCList
begin
        invoke  SendMessageA, [hCCList], LB_SETCOUNT, 0, 0
        invoke  GetWindowLongA, [hCCList], GWL_USERDATA

        stdcall FreeMem, eax
        stdcall CreateArray, 4  ; dword array.

        invoke  SetWindowLongA, [hCCList], GWL_USERDATA, eax
        return
endp




proc SelectCCList, .hStr
begin
        push    ebx esi edi

        stdcall StrPtr, [.hStr]
        mov     edi, eax

        invoke  GetWindowLongA, [hCCList], GWL_USERDATA
        mov     ebx, [eax+TArray.count]
        lea     esi, [eax+TArray.array]
        or      eax, -1

.matchloop:
        inc     eax
        cmp     eax, ebx
        je      .notfound

        mov     ecx, [esi]
        lea     esi, [esi+4]

        mov     ecx, [ecx+TLabel.iName]
        add     ecx, [ptrNames]
        stdcall StringMatch, ecx, edi

        jnc     .matchloop

        invoke  SendMessageA, [hCCList], LB_SETCURSEL, eax, 0
        clc
        return

.notfound:
        invoke  SendMessageA, [hCCList], LB_SETCURSEL, 0, 0
        stc
        return
endp






winproc CCParentProc
begin
        cmp     ebx, WM_KEYDOWN
        je      .relay

        cmp     ebx, LB_ADDSTRING
        jl      .process
        cmp     ebx, LB_ITEMFROMPOINT
        ja      .process

.relay:
        invoke  SendDlgItemMessageA, [.hwnd], idCCList, [.wmsg], [.wparam], [.lparam]
        clc
        return

.process:

ondefault
        stc
        return


onmessage AM_INITWINDOW
        stdcall CreateArray, 4  ; dword array.
        invoke  SetWindowLongA, [.hwnd], GWL_USERDATA, eax
        clc
        return


onmessage WM_SIZE
        invoke  GetDlgItem, [.hwnd], idCCList
        invoke  InvalidateRect, eax, 0, 0
        xor     eax, eax
        clc
        return


onmessage WM_CLOSE
        invoke  GetWindowLongA, [.hwnd], GWL_HWNDPARENT
        invoke  SetFocus, eax

        invoke  DestroyWindow, [.hwnd]
        xor     eax, eax
        mov     [hCCList], 0
        clc
        return


onmessage WM_DESTROY
        invoke  GetWindowLongA, [.hwnd], GWL_USERDATA
        stdcall FreeMem, eax
        stc
        return



onmessage WM_MEASUREITEM
locals
  .ccrect RECT
endl
        mov     esi, [.lparam]
        cmp     [esi+MEASUREITEMSTRUCT.CtlType], ODT_LISTBOX
        jne     .ondefault

        cmp     [esi+MEASUREITEMSTRUCT.CtlID], idCCList
        jne     .ondefault

        invoke  GetDlgItem, [.hwnd], idCCList
        mov     ebx, eax

        lea     ecx, [.ccrect]
        invoke  GetClientRect, ebx, ecx
        push    [.ccrect.right]
        pop     [esi+MEASUREITEMSTRUCT.itemWidth]

        or      eax, -1
        clc
        return


onmessage WM_DRAWITEM
locals
  .rectL RECT
  .rectR RECT
  .bkbrush dd ?
  .buffer rb 32
endl
        cmp     [.wparam], idCCList
        jne      .ondefault

        mov     esi, [.lparam]
        mov     eax, [esi+DRAWITEMSTRUCT.hwndItem]

        mov     eax, COLOR_HIGHLIGHT
        mov     ebx, COLOR_HIGHLIGHTTEXT
        test    [esi+DRAWITEMSTRUCT.itemState], ODS_SELECTED or ODS_FOCUS
        jnz     @f
        mov     eax, COLOR_WINDOW
        mov     ebx, COLOR_WINDOWTEXT
@@:
        invoke  GetSysColor, eax

        invoke  CreateSolidBrush, eax
        mov     [.bkbrush], eax

        invoke  GetSysColor, ebx
        invoke  SetTextColor, [esi+DRAWITEMSTRUCT.hDC], eax
        invoke  SetBkMode, [esi+DRAWITEMSTRUCT.hDC], TRANSPARENT

; First fill the background
        lea     eax, [esi+DRAWITEMSTRUCT.rcItem]
        invoke  FillRect, [esi+DRAWITEMSTRUCT.hDC], eax, [.bkbrush]

; then compute the rectangles
        mov     eax, [esi+DRAWITEMSTRUCT.rcItem.left]
        mov     ecx, [esi+DRAWITEMSTRUCT.rcItem.right]
        mov     [.rectL.left], eax
        mov     [.rectL.right], ecx
        mov     [.rectR.left], ecx
        mov     [.rectR.right], ecx

        sub     ecx, eax
        shr     ecx, 2          ; 1/4 of the field for the value.
        sub     [.rectL.right], ecx
        sub     [.rectR.left], ecx

        mov     eax, [esi+DRAWITEMSTRUCT.rcItem.top]
        mov     ecx, [esi+DRAWITEMSTRUCT.rcItem.bottom]
        mov     [.rectL.top], eax
        mov     [.rectL.bottom], ecx
        mov     [.rectR.top], eax
        mov     [.rectR.bottom], ecx

        add     [.rectL.left], 4
        add     [.rectR.left], 4

        invoke  GetSysColor, COLOR_BTNFACE
        invoke  CreatePen, PS_SOLID, 0, eax
        invoke  SelectObject, [esi+DRAWITEMSTRUCT.hDC], eax
        push    eax

        invoke  MoveToEx, [esi+DRAWITEMSTRUCT.hDC], [.rectL.right], [.rectL.top], NULL
        invoke  LineTo, [esi+DRAWITEMSTRUCT.hDC], [.rectL.right], [.rectL.bottom]

        mov     edi, [.rectL.bottom]
        dec     edi
        invoke  MoveToEx, [esi+DRAWITEMSTRUCT.hDC], [esi+DRAWITEMSTRUCT.rcItem.left], edi, NULL
        invoke  LineTo, [esi+DRAWITEMSTRUCT.hDC],   [.rectR.right], edi

        invoke  SelectObject, [esi+DRAWITEMSTRUCT.hDC]  ; from the stack
        invoke  DeleteObject, eax

; then write the text
        mov     edi, [esi+DRAWITEMSTRUCT.itemID]
        cmp     edi, -1
        je      .cleanup

        invoke  GetWindowLongA, [.hwnd], GWL_USERDATA
        cmp     edi, [eax+TArray.count]
        jae     .cleanup

        mov     edi, [eax+TArray.array + 4*edi]

        mov     eax, [edi+TLabel.iName]
        add     eax, [ptrNames]
        lea     ecx, [.rectL]
        invoke  DrawTextA, [esi+DRAWITEMSTRUCT.hDC], eax, -1, ecx, DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_END_ELLIPSIS

  cNotDefined text 'undefined'
  cVirtual    text 'virtual'

; then write the value

        test    [edi+TLabel.flags], lfDefined
        jz      .notdefined

        cmp     [edi+TLabel.SIBEx], 0
        jne     .virtual

        stdcall NumToStr, [edi+TLabel.ValueLo], ntsHex or ntsSigned
        jmp     .printvalue

.notdefined:
        stdcall StrDup, cNotDefined
        jmp     .printvalue

.virtual:
        stdcall StrDup, cVirtual

.printvalue:
        push    eax
        stdcall StrPtr, eax
        lea     ecx, [.rectR]
        invoke  DrawTextA, [esi+DRAWITEMSTRUCT.hDC], eax, -1, ecx, DT_SINGLELINE or DT_LEFT or DT_VCENTER
        stdcall StrDel ; from the stack.

.cleanup:
        invoke  DeleteObject, [.bkbrush]
        or      eax, -1
        clc
        return

endwp







winproc CCListProc
begin

ondefault
        stc
        return

; This makes CCList to never get the focus.
;onmessage WM_SETFOCUS
;        invoke GetParent, [.hwnd]
;        invoke SetFocus, eax
;        xor    eax, eax
;        clc
;        return

;-----------------------------------------------------------------------
onmessage WM_LBUTTONDBLCLK

        invoke  SendMessageA, [hEditorsHost], EHM_GETCURRENTASMEDIT, 0, 0
        invoke  PostMessageA, eax, WM_KEYDOWN, VK_RETURN, 0
        clc
        return

;-----------------------------------------------------------------------
onmessage WM_KEYDOWN

        mov     ebx, [.wparam]
        call    JumpTo
        MessageList                                     \
          VK_DOWN,   .ondefault,                        \
          VK_UP,     .ondefault,                        \
          VK_PRIOR,  .ondefault,                        \
          VK_NEXT,   .ondefault,                        \
          VK_HOME,   .ondefault,                        \
          VK_END,    .ondefault

        invoke  SendMessageA, [hEditorsHost], EHM_GETCURRENTASMEDIT, 0, 0
        invoke  PostMessageA, eax, [.wmsg], [.wparam], [.lparam]
        clc
        return

endwp




proc UpdateCCPosition
.aepos AEPOS
.caret AECARETXY
.rect  RECT
.workrect RECT
.word rb $100
.aedit dd ?
begin
        cmp     [hCCList], 0
        je      .finish

        invoke  GetWindowLongA, [hCCList], GWL_HWNDPARENT
        mov     [.aedit], eax

        lea     ebx, [.caret]
        invoke  SendMessageA, [.aedit], AEM_GETCARETXY, ebx, 0

        invoke  ClientToScreen, [.aedit], ebx
        add     ebx, 8
        invoke  ClientToScreen, [.aedit], ebx
        sub     ebx, 8

        lea     eax, [.rect]
        invoke  GetWindowRect, [hCCList], eax
        mov     eax, [.rect.right]
        mov     ecx, [.rect.bottom]
        sub     eax, [.rect.left]
        sub     ecx, [.rect.top]
        mov     [.rect.right], eax
        mov     [.rect.bottom], ecx

        lea     ebx, [.workrect]
        invoke  SystemParametersInfoA, SPI_GETWORKAREA,0,ebx,0

        mov     eax, [.workrect.right]
        sub     eax, [.rect.right]
        mov     ecx, [.caret.x0]
        cmp     eax, ecx
        jle     @f
        mov     eax, ecx
@@:
        mov     edx, [.workrect.bottom]
        sub     edx, [.rect.bottom]
        mov     ecx, [.caret.y1]
        add     ecx, 1

        cmp     edx, ecx
        jg      @f

        mov     ecx, [.caret.y0]
        sub     ecx, [.rect.bottom]
        sub     ecx, 2
@@:
        invoke  SetWindowPos, [hCCList], NULL, eax, ecx, 0, 0, SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOCOPYBITS

        lea     ebx, [.word]
        invoke  SendMessageA, [.aedit], AEM_GETWORDATCARET, $100, ebx

        stdcall ClearCCList

        stdcall SearchTree, ebx, ptrLabels
        test    eax, eax
        jz      .destroycc

        mov     esi, [eax]
        test    esi, esi
        jz      .destroycc

        mov     ebx, [esi+TArray.count]
        lea     esi, [esi+TArray.array]

        test    ebx, ebx
        jz      .destroycc

        mov     edi, edx        ; The last word
        xor     ecx, ecx

.fillloop:
        mov     eax, [ptrNames]
        add     eax, [esi+TLabel.iName]
        stdcall StringMatch, eax, edi
        jnc     .next

        stdcall AddCCListItem, eax, esi
        inc     ecx

.next:
        add     esi, sizeof.TLabel
        dec     ebx
        jnz     .fillloop

        test    ecx, ecx
        jz      .destroycc

        invoke  SendMessageA, [hCCList], LB_SETCOUNT, ecx

        stdcall SelectCCList, edi
        stdcall StrDel, edi

        invoke  SetWindowPos, [hCCList], NULL, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_SHOWWINDOW or SWP_NOACTIVATE

.finish:
        return

.destroycc:
        invoke  PostMessageA, [hCCList], WM_CLOSE, 0, 0
        return
endp


proc StringMatch, .str1, .str2
begin
        push    esi edi eax
        stdcall StrPtr, [.str1]
        mov     esi, eax

        stdcall StrPtr, [.str2]
        mov     edi, eax
.loop:
        mov     al, [esi]
        inc     esi
        mov     ah, [edi]
        inc     edi

        test    ah,ah
        jz      .match

        test    al,al
        jz      .notmatch

        or      eax, $2020
        cmp     al,ah
        je      .loop

.notmatch:
        clc
        pop     eax edi esi
        return

.match:
        stc
        pop     eax edi esi
        return
endp
