;*************************************************************
;* coolmenu.asm
;*
;* This file contains coolmenu/toolbars/accelerators engine code.
;* You must include in executable section of the program.
;*
;* You can use this work for any purpos, only don't forget to
;* write somewhere in docs: "Based on work of John Found" or
;* something similar. :)
;*
;* '2003 John Found
;*************************************************************

iglobal
  cToolbarClassName   du  TOOLBAR_CLASS, 0
endg


CoolCode:

;************************************************************
; Function DrawMenuIcon
; Parameters:
;       DC - device context
;       x,y - coordinates of upper left corner of the item
;       height - height of the menu item
;       State - state of the menu item
;************************************************************
proc DrawMenuIcon, .DC, .x, .y, .height, .Image, .State

.v      dd      0
.Brush1 LOGBRUSH
.Brush2 LOGBRUSH

begin

        mov     [.Brush1.lbStyle], BS_SOLID
        mov     [.Brush2.lbStyle], BS_SOLID

        invoke  GetSysColor, COLOR_BTNHIGHLIGHT
        test    [.State], ODS_CHECKED
        jnz     @f
        invoke  GetSysColor, COLOR_MENU
@@:
        invoke  ImageList_SetBkColor, [hIml], eax

; Compute coordinates
        mov     eax,[.y]
        shl     eax,1
        add     eax,[.height]
        sub     eax,18          ; the height of the image. THE IMAGE MUST BE 16x16 pixels.
        sar     eax,1
        mov     [.v],eax
        mov     ecx,[.x]

        inc     eax
        inc     ecx

; Select image list
        mov     ebx,[hIml]
        test    [.State],ODS_GRAYED
        jz      @f
        mov     ebx,[hImlGray]
@@:
        cmp     [.Image],-1
        jne     @f

        test    [.State],ODS_CHECKED
        jz      .qfalse               ; if the item is not checked and there is no Image (Image = -1) then return false

        mov     [.Image], ImageChecked
@@:
        invoke  ImageList_Draw, ebx, [.Image], [.DC], ecx, eax, ILD_NORMAL
        invoke  GetSysColor, COLOR_MENU
        invoke  ImageList_SetBkColor, ebx, eax

; Draw border
        mov     [.Brush1.lbColor],$ffffff
        mov     [.Brush2.lbColor],$808080

        test    [.State], ODS_GRAYED
        jz      @f

        invoke  GetSysColor,COLOR_GRAYTEXT
        mov     [.Brush1.lbColor],eax
        mov     [.Brush2.lbColor],eax

@@:
        test    [.State],ODS_SELECTED or ODS_CHECKED
        jnz     @f

        invoke  GetSysColor,COLOR_MENU
        mov     [.Brush1.lbColor],eax
        mov     [.Brush2.lbColor],eax

@@:
        test    [.State],ODS_CHECKED
        jz      @f

        mov     [.Brush1.lbColor], $808080
        mov     [.Brush2.lbColor], $ffffff

@@:
        lea     eax, [.Brush1]
        invoke  ExtCreatePen,PS_COSMETIC,1,eax,0,NULL
        invoke  SelectObject,[.DC],eax
        push    eax

        mov     esi,[.v]
        mov     ebx,[.x]
        add     esi,18
        add     ebx,18

        invoke  MoveToEx,[.DC],[.x],esi,NULL
        invoke  LineTo,[.DC],[.x],[.v]
        invoke  LineTo,[.DC],ebx,[.v]

;        pop     eax
        invoke  SelectObject,[.DC]       ;,eax   The eax is in stack, don't pop/push again
        invoke  DeleteObject,eax

        lea     eax, [.Brush2]
        invoke  ExtCreatePen, PS_COSMETIC, 1, eax, 0, NULL
        invoke  SelectObject,[.DC],eax
        push    eax

        dec     [.x]
        invoke  LineTo,[.DC],ebx,esi
        invoke  LineTo,[.DC],[.x],esi

;        pop     eax
        invoke  SelectObject,[.DC]       ;,eax
        invoke  DeleteObject,eax

        xor     eax,eax
        dec     eax
        jmp     .finish

.qfalse:
        xor     eax,eax

.finish:
        return
endp


;************************************************************
; Function DrawMenuText                                     *
; Parameters:                                               *
;       DC          - device context                        *
;       x1,y1,x2,y2 - rectangle to draw text                *
;       xOfs        - offset of text in rectangle           *
;       Caption     - pointer to the caption string         *
;       CapLen      - length of the caption string          *
;       State       - state of the menu item                *
;       ID          - ID of the menu function.              *
;************************************************************
proc DrawMenuText, .DC, .x1, .y1, .x2, .y2, .xOfs, .Caption, .CapLen, .State, .ID, .ptrAccel

.str    rb      64

        begin

; Draw background rectangle
        mov     esi,COLOR_HIGHLIGHT
        mov     edi,COLOR_HIGHLIGHTTEXT

        test    [.State],ODS_SELECTED
        jnz     @f

        mov     esi,COLOR_MENU
        mov     edi,COLOR_MENUTEXT
@@:
        test    [.State],ODS_GRAYED
        jz      @f

        mov     edi,COLOR_GRAYTEXT

@@:
        invoke  GetSysColor,esi
        invoke  SetBkColor,[.DC],eax
        invoke  GetSysColor,edi
        invoke  SetTextColor,[.DC],eax

        inc     esi

        lea     ebx,[.x1]
        invoke  FillRect,[.DC],ebx,esi
        sub     [.x2],12

; Draw text
        mov     ecx,[.xOfs]
        add     [.x1],ecx

        stdcall utf8ToWideChar, [.Caption]
        push    eax

        invoke  DrawTextW, [.DC], eax, [.CapLen], ebx,    \
                          DT_LEFT or DT_SINGLELINE or DT_VCENTER

        stdcall FreeMem ; from the stack

        lea     esi,[.str]
        stdcall AccelToStr, [.ptrAccel], esi
        mov     dword [esi+eax], 0

        stdcall utf8ToWideChar, esi
        push    eax

        invoke  DrawTextW, [.DC], eax, -1, ebx, DT_RIGHT or DT_SINGLELINE or DT_VCENTER
        stdcall FreeMem ; from the stack

        pop     eax
        invoke  SelectObject, [.DC], eax
        return
endp

;*************************************************************
;  Draws the entire menu item (icon and text)
;*************************************************************
proc DoDrawItem, .DC, .x1, .y1, .x2, .y2, .Caption, .CapLen, .State, .Image, .ID, .ptrAccel
begin
        mov     eax, [.y2]
        sub     eax, [.y1]
        stdcall DrawMenuIcon, [.DC], [.x1], [.y1], eax, [.Image], [.State]
        mov     ebx, 20
        test    eax, eax
        jz      @f              ; if no image, don't move the left side of the rectangle.
        add     [.x1], ebx
        xor     ebx, ebx
@@:
        inc     ebx
; Print the text.
        stdcall DrawMenuText, [.DC], [.x1], [.y1], [.x2], [.y2], ebx,           \
                              [.Caption], [.CapLen], [.State], [.ID], [.ptrAccel]
        return
endp


;************************************************************
;  On draw item.
;************************************************************
proc OnDrawItem, .lparam
begin
        mov     esi, [.lparam]    ; address of TDrawItemStruct
        cmp     [esi+DRAWITEMSTRUCT.CtlType], ODT_MENU
        jne     .qfalse

        mov     edi,[esi+DRAWITEMSTRUCT.itemData]
        test    edi,edi
        jz      .qfalse

        movzx   eax, [edi+TAction.TextLen]
        movsx   ecx, [edi+TAction.Image]
        stdcall DoDrawItem, [esi+DRAWITEMSTRUCT.hDC],             \
                            [esi+DRAWITEMSTRUCT.rcItem.left],   \
                            [esi+DRAWITEMSTRUCT.rcItem.top],    \
                            [esi+DRAWITEMSTRUCT.rcItem.right],  \
                            [esi+DRAWITEMSTRUCT.rcItem.bottom], \
                            [edi+TAction.ptrText],                 \
                            eax,                                   \
                            [esi+DRAWITEMSTRUCT.itemState],       \
                            ecx,                                   \
                            [esi+DRAWITEMSTRUCT.CtlID],           \
                            [edi+TAction.ptrAccel]

        xor     eax,eax
        inc     eax
        jmp     .finish
.qfalse:
        xor     eax,eax
.finish:
        return
endp


;************************************************************
;  On measure item.
;************************************************************
proc OnMeasureItem, .hwnd, .lparam
.ARect  RECT
.DC     dd      ?
.str    rb      64
.sz     SIZE

begin
        mov     esi,[.lparam]     ; address of MEASUREITEMSTRUCT
        cmp     [esi+MEASUREITEMSTRUCT.CtlType], ODT_MENU
        jne     .qfalse

        mov     edi,[esi+MEASUREITEMSTRUCT.itemData]
        test    edi,edi
        jz      .qfalse

        stdcall GetDefaultFont, gdfMenu
        push    eax

        invoke  GetDC,[.hwnd]           ; try with current window DC
        mov     [.DC],eax

        invoke  SelectObject, eax ; the font is already in the stack.
        push    eax

        xor     eax,eax
        mov     [.ARect.left],eax
        mov     [.ARect.right],eax
        mov     [.ARect.top],eax
        mov     [.ARect.bottom],eax
        lea     ebx,[.ARect]

        movzx   eax, [edi+TAction.TextLen]
        lea     ebx, [esi+MEASUREITEMSTRUCT.itemWidth]

        stdcall utf8ToWideChar, [edi+TAction.ptrText]
        push    eax
        dec     ecx

        invoke  GetTextExtentPoint32W, [.DC], eax, ecx, ebx
        stdcall FreeMem ; from the stack

        cmp     [esi+MEASUREITEMSTRUCT.itemHeight],19          ; this is height of the icon image
        jae     @f
        mov     [esi+MEASUREITEMSTRUCT.itemHeight],19
@@:

        lea     ebx,[.str]
        stdcall AccelToStr, [edi+TAction.ptrAccel], ebx
        test    eax, eax
        jz      .NoAccel

        mov     dword [ebx+eax], 0
        stdcall utf8ToWideChar, ebx
        push    eax

        lea     edx,[.sz]
        dec     ecx

        invoke  GetTextExtentPoint32W, [.DC], eax, ecx, edx
        stdcall FreeMem ; from the stack

        mov     ebx,[.sz.cx]      ; height is in eax
        add     [esi+MEASUREITEMSTRUCT.itemWidth],ebx

        cmp     [edi+TAction.Image], -1
        jne     .NoAccel

        add     [esi+MEASUREITEMSTRUCT.itemWidth],12

.NoAccel:
        add     [esi+MEASUREITEMSTRUCT.itemWidth], 24    ; the distance between the label and the accelerator.

        invoke  SelectObject,[.DC]      ; the last arg is in stack.
        invoke  DeleteObject, eax

        invoke  ReleaseDC, [.hwnd], [.DC]

        xor     eax, eax
        inc     eax
        return

.qfalse:
        xor     eax,eax
        return
endp

uglobal
  buffMenuitem   MENUITEMINFO
endg


;*********************************************************
; On Menu char  WM_MENUCHAR message
;*********************************************************
proc OnMenuChar, .wparam, .hmenu
.count  dd ?
.widechar rw 512
begin
        test     word [.wparam+2], MF_SYSMENU
        jnz      .default

        invoke  GetMenuItemCount, [.hmenu]
        cmp     eax,0
        jle     .default
        mov     [.count],eax

        xor     ebx,ebx
        mov     [buffMenuitem.cbSize],sizeof.MENUITEMINFO
        mov     [buffMenuitem.fMask], MIIM_DATA or MIIM_TYPE or MIIM_SUBMENU

.searchloop:
        invoke  GetMenuItemInfoW, [.hmenu], ebx, TRUE, buffMenuitem
        test    eax, eax
        jz      .notthis

        cmp     [buffMenuitem.hSubMenu], 0
        jne     .notthis ; windows will handle this

        test   [buffMenuitem.fType], MFT_OWNERDRAW
        jz     .nod

        mov     edi, [buffMenuitem.dwItemData]

        stdcall StrPtr, [edi+TAction.ptrText]

        lea     ecx, [.widechar]
        invoke  MultiByteToWideChar, CP_UTF8, 0, eax, -1, ecx, 512

        mov     ecx, eax
        lea     edi, [.widechar]
        jmp     .OK

.nod:
        cmp     [buffMenuitem.fType], MFT_STRING
        jne     .notthis

        mov     edi, [buffMenuitem.dwTypeData]
        mov     ecx, [buffMenuitem.cch]
      ;  test    edi, edi
      ;  jz      .notthis
.OK:
        jecxz  .notthis

        mov     dx, word [.wparam]
        cmp     dx, $61
        jb      .search
        cmp     dx, $7a
        ja      .search
        sub     dx, 20h

.search:
        mov     eax, [edi]
        cmp     ax, '&'
        jne     .next

        shr     eax, 16
        cmp     ax, $61
        jb      @f
        cmp     ax, $7a
        ja      @f
        sub     ax, 20h
@@:
        cmp     ax, dx
        je      .this

.next:
        add     edi, 2
        loop   .search

.notthis:
        inc     ebx
        cmp     ebx, [.count]
        jne     .searchloop

;not found
.default:
        stc
        return

.this:
        mov     ax, 2
        shl     eax, 16
        mov     ax, bx

.finish:
        clc
        return
endp


;****************************************************************
;  Creates menu from CoolMenu structure (See CoolMenu macro).
;  Returns handle to the menu.
;
;  ptrMenuItems: pointer to the CoolMenu array
;  fPopup:       if <>0, creates popup menu instead of main menu
;****************************************************************
proc CreateCoolMenu, .ptrMenuItems, .fPopup
.item   MENUITEMINFO
.level  dd ?
begin
        push    esi edi ebx

        mov     esi, [.ptrMenuItems]
        mov     [.level], 0

        mov     eax, [.fPopup]
        test    eax, eax
        jnz     .popup

        invoke  CreateMenu
        jmp     .cont

.popup:
        invoke  CreatePopupMenu

.cont:
        mov     ebx,eax

        mov     [.item.cbSize],sizeof.MENUITEMINFO
        mov     [.item.fMask], MIIM_ID or MIIM_STATE or MIIM_SUBMENU or MIIM_TYPE or MIIM_DATA
        mov     [.item.fState],MFS_ENABLED
        mov     [.item.hbmpChecked],0
        mov     [.item.hbmpUnchecked],0

.loopitems:
        xor     eax,eax
        mov     [.item.hSubMenu],eax
        mov     [.item.wID],eax
        mov     [.item.dwItemData],eax

        test    [esi+TCoolMenuItem.Flags], mfEnd
        jnz     .leveldn

        test    [esi+TCoolMenuItem.Flags], mfSeparator
        jnz     .separator

        test    [esi+TCoolMenuItem.Flags], mfSubmenu
        jnz     .submenu

        mov     [.item.fState],MFS_ENABLED
        test    [esi+TCoolMenuItem.Flags], mfChecked
        jz      @f
        or      [.item.fState], MFS_CHECKED
@@:
        mov     [.item.fType],MFT_OWNERDRAW

        movzx   edi, [esi+TCoolMenuItem.Action]
        imul    edi, sizeof.TAction
        add     edi, MainActionList

        movzx   eax,[edi+TAction.ID]
        mov     [.item.wID],eax

        mov     [.item.dwItemData], edi
        jmp     .ready

.separator:
        mov     [.item.fType], MFT_SEPARATOR
        jmp     .ready

.submenu:
        invoke  CreatePopupMenu

        mov     [.item.hSubMenu],eax
        mov     [.item.fType], MFT_STRING
        movzx   edi, [esi+TCoolMenuItem.Action]  ; Action is the offset to the string
        add     edi, esi

        stdcall utf8ToWideChar, edi
        mov     [.item.dwTypeData], eax
        lea     eax,[.item]
        invoke  InsertMenuItemW, ebx, -1, TRUE, eax
        stdcall FreeMem, [.item.dwTypeData]

        inc     [.level]
        push    ebx
        mov     ebx,[.item.hSubMenu]
        jmp     .next

.leveldn:
        cmp     [.level],0
        je      .next

        pop     ebx
        dec     [.level]
        jmp     .next

.ready:
        lea     eax, [.item]
        invoke  InsertMenuItemW, ebx, -1, TRUE, eax

.next:
        add     esi,sizeof.TCoolMenuItem
        cmp     word [esi],-2                   ; end of structure
        jne     .loopitems

        mov     eax,ebx
        pop     ebx edi esi
        return
endp



proc GetHintText, .ActList, .action
begin
        mov     eax, [.action]
        imul    eax, sizeof.TAction
        add     eax, [.ActList]

        mov     eax, [eax+TAction.ptrHint]
        return
endp


;***********************************************************************
;  Creates toolbar from CoolToolbar structure (See CoolToolbar macro).
;  Returns handle to the toolbar.
;***********************************************************************
proc CreateCoolToolbar, .ptrActionList, .ptrToolbarItems, .ParentWindow, .ToolbarID, .ImagesNormal, .ImagesDisabled
.item   TToolbarButton
.win WINDOWPOS
begin
; Create toolbar
        xor     esi,esi
        invoke  GetModuleHandleW, esi
        mov     edi, eax
        invoke  CreateWindowExW, esi, cToolbarClassName, NULL,          \
                                WS_CHILD or                             \
                                WS_VISIBLE or WS_CLIPSIBLINGS or        \
                                TBSTYLE_TRANSPARENT or                  \
                                TBSTYLE_FLAT or                         \
                                TBSTYLE_TOOLTIPS or                     \
                                TBSTYLE_WRAPABLE or                     \
                                CCS_NOPARENTALIGN or CCS_NORESIZE,      \
                                esi, esi, 10000, esi,                     \
                                [.ParentWindow], [.ToolbarID],          \
                                edi, NULL
        mov     ebx,eax
        stdcall SubclassWindow, ebx, CoolToolbarProc

        invoke  SendMessageW, ebx, TB_BUTTONSTRUCTSIZE, TToolbarButton.size, esi

; make arrows for dropdown buttons
        invoke  SendMessageW, ebx, TB_GETEXTENDEDSTYLE, esi, esi
        or      eax,TBSTYLE_EX_DRAWDDARROWS
        invoke  SendMessageW, ebx, TB_SETEXTENDEDSTYLE, esi, eax

; Set image lists
        invoke  SendMessageW, ebx, TB_SETIMAGELIST, esi, [.ImagesNormal]
        invoke  SendMessageW, ebx, TB_SETDISABLEDIMAGELIST, esi, [.ImagesDisabled]

; make tooltip to appear on not active main window.
        invoke  SendMessageW, ebx, TB_GETTOOLTIPS, 0, 0
        mov     esi, eax
        invoke  GetWindowLongW, esi, GWL_STYLE
        or      eax, TTS_ALWAYSTIP or TTS_NOPREFIX
        invoke  SetWindowLongW, esi, GWL_STYLE, eax
; Set tooltip delay time.
        invoke  SendMessageW, esi, TTM_SETDELAYTIME, TTDT_AUTOMATIC, 600

; Create buttons
        mov     esi, [.ptrToolbarItems]
        xor     eax, eax
        mov     [.item.dwData], eax
        mov     [.item.iString], -1

.itemloop:
        cmp     [esi+TCoolButton.Action],-2        ; end of structure
        je      .finish

        test    [esi+TCoolButton.Flags],bfSeparator
        jnz     .separator

        movzx   edi,[esi+TCoolButton.Action]
        imul    edi,sizeof.TAction
        add     edi,[.ptrActionList]

        movsx   eax,[edi+TAction.Image]
        movzx   ecx,[edi+TAction.ID]
        mov     [.item.iBitmap],eax
        mov     [.item.idCommand],ecx

        mov     al,[esi+TCoolButton.Flags]
        mov     [.item.fsStyle],al
        and     [.item.fsStyle], $1f

        mov     [.item.fsState],TBSTATE_ENABLED
        test    al, bfDisabled
        jz      @f
        mov     [.item.fsState],0
@@:
        test    al, bfChecked
        jz      @f
        or      [.item.fsState], TBSTATE_CHECKED
@@:
        test    al, bfWrap
        jz      .ready
        or      [.item.fsState], TBSTATE_WRAP or TBSTATE_ENABLED
        jmp     .ready

.separator:
        xor     eax,eax
        mov     [.item.iBitmap],eax
        mov     [.item.idCommand],eax
        mov     [.item.fsState],al
        mov     [.item.fsStyle],TBSTYLE_SEP

.ready:
        lea     edi,[.item]
        invoke  SendMessageW, ebx, TB_ADDBUTTONS, 1, edi

        add     esi, sizeof.TCoolButton
        jmp     .itemloop

.finish:
        mov     eax,ebx
        return
endp




proc CoolToolbarProc, .hwnd, .wmsg, .wparam, .lparam
begin

        dispatch [.wmsg]

.ondefault:

        stc
        return

oncase SGM_AUTOSIZE

;        invoke  SendMessageW, [.hwnd], TB_AUTOSIZE, 0, 0
        stdcall __ComputeToolbarSize, [.hwnd]

        push    eax edx

        invoke  SetWindowPos, [.hwnd], 0, 0, 0, eax, edx, SWP_NOZORDER



        pop     edx eax
        and     eax, $ffff
        shl     edx, 16
        or      eax, edx

        clc
        return

        enddispatch

endp




proc GetNCSize, .hwnd
.winrect RECT
.client  RECT
begin
        lea     eax, [.winrect]
        invoke  GetWindowRect, [.hwnd], eax
        lea     eax, [.client]
        invoke  GetClientRect, [.hwnd], eax

        mov     eax, [.winrect.right]
        mov     edx, [.winrect.bottom]

        sub     eax, [.winrect.left]
        sub     edx, [.winrect.top]

        sub     eax, [.client.right]
        sub     edx, [.client.bottom]
        return
endp



proc __ComputeToolbarSize, .hToolbar
  .winbounds TBounds
  .winrect   RECT
begin
        pushad

        mov     [.winbounds.width], $4
        mov     [.winbounds.height], $4

        xor     esi,esi
        lea     ebx, [.winrect]

.whiletoolbar:
        invoke  SendMessageW, [.hToolbar], TB_GETITEMRECT, esi, ebx
        test    eax,eax
        jz      .endauto

; [.winbounds.width] = max ([.winbounds.width], [.winrect.right])
; [.winbounds.height] = max ([.winbounds.height], [.winrect.bottom])

        mov     eax, [.winbounds.width]
        mov     ecx, [.winbounds.height]

        cmp     eax, [.winrect.right]
        cmovl   eax, [.winrect.right]

        cmp     ecx, [.winrect.bottom]
        cmovl   ecx, [.winrect.bottom]

        mov     [.winbounds.width], eax
        mov     [.winbounds.height], ecx

        inc     esi
        jmp     .whiletoolbar

.endauto:

        stdcall GetNCSize, [.hToolbar]

        add     eax, [.winbounds.width]
        add     edx, [.winbounds.height]

        mov     [esp+4*regEAX], eax
        mov     [esp+4*regEDX], edx

        popad
        return
endp









struct TKeyName
  .VirtKey  dw ?
  .NextOfs  db ?
  .Text     db ?
ends


struc dk [code,txt] {
  forward
    local .TheEnd
      dw code
      db .TheEnd-$+2
      db txt,0
    .TheEnd = $
  common
    dw -1
}

iglobal
  VKeyArray dk                              \
          VK_BACK,      'BkSp',             \
          VK_TAB,       'Tab',              \
          VK_CLEAR,     'Clear',            \
          VK_RETURN,    'Enter',            \
          VK_SHIFT,     'Shift',            \
          VK_CONTROL,   'Ctrl',             \
          VK_MENU,      'Alt',              \
          VK_PAUSE,     'Pause',            \
          VK_CAPITAL,   'CapsLock',         \
          VK_ESCAPE,    'Esc',              \
          VK_SPACE,     'Space',            \
          VK_PGUP,      'PgUp',             \
          VK_PGDN,      'PgDn',             \
          VK_END,       'End',              \
          VK_HOME,      'Home',             \
          VK_LEFT,      'Left',             \
          VK_UP,        'Up',               \
          VK_RIGHT,     'Right',            \
          VK_DOWN,      'Down',             \
          VK_SELECT,    'Select',           \
          VK_EXECUTE,   'Execute',          \
          VK_SNAPSHOT,  'PrtScr',           \
          VK_INSERT,    'Ins',              \
          VK_DELETE,    'Del',              \
          VK_HELP,      'Help',             \
          VK_LWIN,      'LeftWin',          \
          VK_RWIN,      'RightWin',         \
          VK_APPS,      'Apps',             \
          VK_NUMPAD0,   'Num0',             \
          VK_NUMPAD1,   'Num1',             \
          VK_NUMPAD2,   'Num2',             \
          VK_NUMPAD3,   'Num3',             \
          VK_NUMPAD4,   'Num4',             \
          VK_NUMPAD5,   'Num5',             \
          VK_NUMPAD6,   'Num6',             \
          VK_NUMPAD7,   'Num7',             \
          VK_NUMPAD8,   'Num8',             \
          VK_NUMPAD9,   'Num9',             \
          VK_MULTIPLY,  'Num*',             \
          VK_ADD,       'Num+',             \
          VK_SUBTRACT,  'Num-',                \
          VK_DECIMAL,   'Num.',                \
          VK_DIVIDE,    'Num/',                \
          VK_F1,        'F1',               \
          VK_F2,        'F2',               \
          VK_F3,        'F3',               \
          VK_F4,        'F4',               \
          VK_F5,        'F5',               \
          VK_F6,        'F6',               \
          VK_F7,        'F7',               \
          VK_F8,        'F8',               \
          VK_F9,        'F9',               \
          VK_F10,       'F10',              \
          VK_F11,       'F11',              \
          VK_F12,       'F12',              \
          VK_F13,       'F13',              \
          VK_F14,       'F14',              \
          VK_F15,       'F15',              \
          VK_F16,       'F16',              \
          VK_F17,       'F17',              \
          VK_F18,       'F18',              \
          VK_F19,       'F19',              \
          VK_F20,       'F20',              \
          VK_F21,       'F21',              \
          VK_F22,       'F22',              \
          VK_F23,       'F23',              \
          VK_F24,       'F24',              \
          VK_NUMLOCK,   'NumLock',          \
          VK_SCROLL,    'ScrLock',          \
          VK_LSHIFT,    'LShift',           \
          VK_RSHIFT,    'RShift'
endg



;************************************************************************
; Returns string with text name of the virtual key in
; given accelerator.
;
; ptrAccel: Pointer to the ACCEL structure of the accelerator.
; ptrStr:   Pointer to the buffer for the string.
;
; Returns: eax - length of the string (only ASCII chars)
;
; Note: IMHO this is not the best method to convert virtual
;       key to string, but windows don't provide any other way.
;       using MapVirtualKey and then GetKeyNameText don't works
;       good, because of bad convertion VK -> scancode
; WARNING:
;       NO ERROR CHECK.
;       If you set some strange accelerator key /VK_LBUTTON for example/
;       the result string will be strange.
;************************************************************************

proc AccelToStr, .ptrAccel, .ptrStr

.Prefix rb 32
.str    rb 32

        begin
        push    esi edi ebx

        mov     edx, [.ptrAccel]
        test    edx, edx
        jz      .noaccel

        movzx   eax, [edx+TAccel.Key]
        mov     esi, VKeyArray

.search:
        cmp     [esi+TKeyName.VirtKey],-1
        je      .notfound
        cmp     [esi+TKeyName.VirtKey], ax
        je      .found
        movzx   ebx, [esi+TKeyName.NextOfs]
        add     esi, ebx
        jmp     .search

.notfound:
        and     ax, $00ff
        mov     word [.str], ax
        jmp     .prefix

.found:
        add     esi, 3
        lea     edi, [.str]
.copy:
        mov     al, [esi]
        inc     esi
        mov     [edi], al
        inc     edi
        test    al, al
        jnz     .copy

.prefix:
        lea     esi, [.str]

        test    [edx+TAccel.Flags], Alt
        jz      @f
        sub     esi, 4
        mov     dword [esi], 'Alt+'
@@:
        test    [edx+TAccel.Flags], Ctrl
        jz      @f
        sub     esi, 5
        mov     dword [esi], 'Ctrl'
        mov     byte  [esi+4], '+'
@@:
        test    [edx+TAccel.Flags], Shift
        jz      @f
        sub     esi, 6
        mov     dword [esi], 'Shif'
        mov     word  [esi+4],'t+'
@@:
        mov     edi, [.ptrStr]
        xor     eax, eax
        dec     eax
.copy2:
        mov     bl, [esi]
        inc     esi
        mov     [edi], bl
        inc     edi
        inc     eax             ; eax is counter of string length
        test    bl, bl
        jnz     .copy2
        jz      .exit

.noaccel:
        mov     edi,[.ptrStr]
        xor     eax,eax
        mov     [edi],eax

.exit:
        pop     ebx edi esi
        return
endp



proc DisableBitmap, .hBmp, .IconWidth, .clrTransparent, .clrGrayBlend, .clrBack
  .bmp   BITMAPINFOHEADER
  .b     BITMAP
  .ptrBits dd ?
  .hBmpRes dd ?
  .dc      dd ?
  .dc2     dd ?
  .clr     dd ?
begin
        push    esi edi ebx

        mov     eax, [.clrGrayBlend]
        bswap   eax
        shr     eax, 8
        mov     [.clrGrayBlend], eax

        mov     al, byte [.clrBack]
        xchg    al, byte [.clrBack+2]
        mov     byte [.clrBack], al

        xor     ebx, ebx
        dec     [.IconWidth]

        invoke  CreateCompatibleDC, ebx
        mov     [.dc], eax
        invoke  CreateCompatibleDC, ebx
        mov     [.dc2], eax

        lea     esi, [.b]
        invoke  GetObjectW, [.hBmp], sizeof.BITMAP, esi

        mov     [.bmp.biSize], sizeof.BITMAPINFOHEADER
        mov     ecx, [.b.bmWidth]
        mov     edx, [.b.bmHeight]
        mov     [.bmp.biWidth], ecx
        mov     [.bmp.biHeight], edx
        mov     [.bmp.biPlanes], 1
        mov     [.bmp.biBitCount], 32
        mov     [.bmp.biCompression], BI_RGB
        mov     [.bmp.biSizeImage], ebx
        mov     [.bmp.biClrUsed],ebx
        mov     [.bmp.biClrImportant], ebx

        lea     ecx, [.ptrBits]
        lea     esi, [.bmp]
        invoke  CreateDIBSection, ebx, esi, ebx, ecx, ebx, ebx
        mov     [.hBmpRes], eax

        invoke  SelectObject, [.dc2], [.hBmpRes]
        push    eax

        invoke  SelectObject, [.dc], [.hBmp]
        push    eax

        lea     eax, [.bmp]
        invoke  GetDIBits, [.dc], [.hBmp], ebx, [.bmp.biHeight], [.ptrBits], eax, ebx

        invoke  SelectObject, [.dc]
        invoke  SelectObject, [.dc2]
        invoke  DeleteDC, [.dc]
        invoke  DeleteDC, [.dc2]

        mov     edi, [.ptrBits]
        mov     ecx, [.bmp.biHeight]
        shl     ecx, 16

.outloop:
        xor     esi, esi        ; Counter from 0 to IconWidth-1
        mov     cx,  word [.bmp.biWidth]
.inloop:
        xor     ebx, ebx
        xor     eax, eax
        xor     ebx, ebx
        and     dword [edi], $ffffff
        mov     edx, [edi]
        cmp     edx, [.clrTransparent]
        jne     .gray
        mov     eax, [.clrBack]
        jmp     .next

.gray:
        movzx   eax, dl
        movzx   ebx, dh

        add     eax, eax        ; 2* Blue
        add     eax, ebx        ; + Green

        shr     edx, 16
        movzx   ebx, dl
        add     eax, ebx        ; + Red

        shr     eax, 2          ; div 4 -> Grayscale pixel.
                                ; This formula gives more realistic view.

        push    eax eax

; -----byte 0
        movzx   ebx, byte [.clrGrayBlend]
        imul    ebx, esi          ; clrBlend * xi

        mov     edx, eax
        imul    edx, esi          ; clrGray * xi

        imul    eax, [.IconWidth]  ; clrGray * xmax

        add     eax, ebx          ;
        sub     eax, edx          ; eax := clrBlend * xi + clrGray * xmax - clrGray * xi

        xor     edx, edx
        div     [.IconWidth]

        mov     byte [.clr], al

; -----byte 1
        pop     eax

        movzx   ebx, byte [.clrGrayBlend+1]
        imul    ebx, esi          ; clrBlend * xi

        mov     edx, eax
        imul    edx, esi          ; clrGray * xi

        imul    eax, [.IconWidth]  ; clrGray * xmax

        add     eax, ebx          ;
        sub     eax, edx          ; eax := clrBlend * xi + clrGray * xmax - clrGray * xi

        xor     edx, edx
        div     [.IconWidth]

        mov     byte [.clr+1], al

; -----byte 2
        pop     eax

        movzx   ebx, byte [.clrGrayBlend+2]
        imul    ebx, esi          ; clrBlend * xi

        mov     edx, eax
        imul    edx, esi          ; clrGray * xi

        imul    eax, [.IconWidth]  ; clrGray * xmax

        add     eax, ebx          ;
        sub     eax, edx          ; eax := clrBlend * xi + clrGray * xmax - clrGray * xi

        xor     edx, edx
        div     [.IconWidth]

        mov     byte [.clr+2], al

; combine it all
        mov     eax, [.clr]
        and     eax, $ffffff

.next:
        mov     [edi], eax
        lea     edi, [edi+4]

        inc     esi
        cmp     esi, [.IconWidth]
        jbe     .doloop
        xor     esi, esi

.doloop:
        dec     cx
        jnz     .inloop
        sub     ecx, $10000
        jnz     .outloop

.finish:
        mov     eax, [.hBmpRes]
        pop     ebx edi esi
        return
endp



proc ExecAction, .actionlist, .number, .wparam, .lparam
begin
        mov     ebx, [.actionlist]
        movzx   esi, word [.wparam]
        cmp     esi, FirstID
        jl      .ondefault

        sub     esi, FirstID
        cmp     esi, [.number]
        jge     .ondefault

        imul    esi,sizeof.TAction
        add     esi, [.actionlist]

        cmp     [esi+TAction.OnExecute], 0
        je      @f

        stdcall [esi+TAction.OnExecute], [.wparam], [.lparam]

@@:
        xor     eax, eax
        clc
        return

.ondefault:
        stc
        return
endp




gdfCaption      = 0
gdfSmallCaption = 1
gdfMenu         = 2
gdfStatus       = 3
gdfMessage      = 4

gdfMAX = 4


proc GetDefaultFont, .index
.ncm NONCLIENTMETRICSW
begin
        push    ecx edx

        mov     [.ncm.cbSize], sizeof.NONCLIENTMETRICSW
        lea     eax, [.ncm]
        invoke  SystemParametersInfoW, SPI_GETNONCLIENTMETRICS, sizeof.NONCLIENTMETRICSW, eax, 0
        test    eax, eax
        jz      .default

        mov     eax, [.index]
        cmp     eax, gdfMAX
        ja      .default

        movzx   eax, [.offsets+eax]
        lea     eax, [.ncm+4*eax]

        invoke  CreateFontIndirectW, eax
        test    eax, eax
        jnz     .finish


.default:
        int3
        invoke  GetStockObject, DEFAULT_GUI_FONT

.finish:
        pop     edx ecx
        return

.offsets db NONCLIENTMETRICSW.lfCaptionFont/4
         db NONCLIENTMETRICSW.lfSmCaptionFont/4
         db NONCLIENTMETRICSW.lfMenuFont/4
         db NONCLIENTMETRICSW.lfStatusFont/4
         db NONCLIENTMETRICSW.lfMessageFont/4
endp



DispSize 'actions engine', ($ - CoolCode)

