comment $
//-----------------------------------------------------------------------------------//
//  Source code                                                                      //
//                                                                                   //
//  Written      by  Chib, amdtm                     			                     //
//                                                                                   //
//  FileName   : XXMenu.asm                                                          //
//  Description: libraries of functions for work with menu                           //
//  Version    : 1.00.000, January 1, 2004                                           //
//-----------------------------------------------------------------------------------//
$ 

    include XXButton.inc
    
;--------------------------------------------------------------------------
.code

; #########################################################################
;                                                                         ;
; Процедура субкласирования для родительского окна кнопки.                ;
;                                                                         ;
; #########################################################################    

WndProc_Parent_Btn proc uses ebx esi edi hWin:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    LOCAL OldWndProc :DWORD

    invoke GetProp, hWin, ADDR szProp_OldWndProc
    mov OldWndProc, eax
    
    .if uMsg == WM_DRAWITEM
;        PrintDec wParam, "DRAWITEM"
   
        invoke DrawItem_Btn, lParam

        jmp d_c
        
    .elseif uMsg == WM_DESTROY

;        PrintDec uMsg, "DESTROY"
        jmp d_c

    .else

d_c:        
        invoke CallWindowProc,OldWndProc,hWin,uMsg,wParam,lParam
        ret
    .endif           
 
    xor eax,eax
    ret
WndProc_Parent_Btn endp

; #########################################################################
;                                                                         ;
; Процедура субкласирования для кнопки.                                   ;
;                                                                         ;
; #########################################################################    

WndProc_Btn proc uses ebx edi esi hBtn:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    LOCAL OldWndProc :DWORD
    LOCAL Rct        :RECT
    LOCAL lpBtnStruct:DWORD
    LOCAL tme        :TRACKMOUSEEVENT
    LOCAL XXBtnStr   :XXBUTTON

    invoke GetProp, hBtn, ADDR szProp_OldWndProc
    mov OldWndProc, eax
    
    .if uMsg == WM_MOUSEMOVE       
      
        .if hBtnMouseHover == FALSE
        
;        PrintDec hBtn, "Над кнопкой"
        
            invoke GetProp, hBtn, ADDR szProp_Struct
            
            mov     esi, eax
            lea     eax, XXBtnStr
            mov     edi, eax
            mov     ecx, sizeof XXBtnStr
            rep     movsb
        	
        	.if XXBtnStr.hCursor_hover
                invoke SetClassLong, hBtn, GCL_HCURSOR, XXBtnStr.hCursor_hover
                mov OldCursor, eax
            .endif
           
           m2m hBtnMouseHover, hBtn
           
           ; перерисовываем только если -|
           .if XXBtnStr.hIcon_hover || XXBtnStr.m_flat || XXBtnStr.hover_clr || XXBtnStr.hFont_hover || XXBtnStr.hBitmap_hover
       	        invoke InvalidateRect, hBtn, NULL, FALSE
           .endif 
            
           .if XXBtnStr.Sound_hover 
                invoke PlaySound, XXBtnStr.Sound_hover, NULL, SND_MEMORY+SND_NOSTOP+SND_NOWAIT       
           .endif
        	
        	mov		tme.cbSize, sizeof(TRACKMOUSEEVENT)
        	mov		tme.dwFlags, TME_LEAVE
        	mov		tme.dwHoverTime, HOVER_DEFAULT
        	m2m		tme.hwndTrack, hBtn
        	invoke	TrackMouseEvent,addr tme   	
        	
        .endif      
        
        jmp d_c
 
    .elseif uMsg == WM_MOUSELEAVE 
 
 
        invoke GetProp, hBtn, ADDR szProp_Struct
        
        mov     esi, eax
        lea     eax, XXBtnStr
        mov     edi, eax
        mov     ecx, sizeof XXBtnStr
        rep     movsb
    	
    	.if OldCursor ;XXBtnStr.hCursor_hover && 
            invoke SetClassLong, hBtn, GCL_HCURSOR, OldCursor
        .endif  
 
;        PrintDec hBtn, "За кнопкой"
        mov hBtnMouseHover, FALSE
        
        .if XXBtnStr.hIcon_hover || XXBtnStr.m_flat || XXBtnStr.hover_clr || XXBtnStr.hFont_hover || XXBtnStr.hBitmap_hover                      
            invoke InvalidateRect, hBtn, NULL, FALSE
       .endif 
       
        mov		tme.cbSize, sizeof(TRACKMOUSEEVENT)
        mov		tme.dwFlags, TME_CANCEL
        mov		tme.dwHoverTime, HOVER_DEFAULT
        m2m		tme.hwndTrack, hBtn
        invoke	TrackMouseEvent,addr tme            
            
        jmp d_c
  
    .elseif uMsg == WM_LBUTTONDBLCLK
        invoke SendMessage, hBtn, WM_LBUTTONDOWN, wParam, lParam
         
        jmp d_c
        
    .elseif uMsg == WM_DESTROY || uMsg == WM_CLOSE

;        PrintDec uMsg, "DESTROY"

        mov		tme.cbSize, sizeof(TRACKMOUSEEVENT)
        mov		tme.dwFlags, TME_CANCEL
        mov		tme.dwHoverTime, HOVER_DEFAULT
        m2m		tme.hwndTrack, hBtn
        invoke	TrackMouseEvent,addr tme
        
        mov hBtnMouseHover, FALSE

        invoke FreeXXStruct, hBtn
        jmp d_c        
        
    .else

d_c:        
        invoke CallWindowProc,OldWndProc,hBtn,uMsg,wParam,lParam
        ret
    .endif           
 
    xor eax,eax
    ret
WndProc_Btn endp

; #########################################################################
;                                                                         ;
; Процедура обработки сообщения WM_DRAWITEM.                              ;
;                                                                         ;
; #########################################################################

DrawItem_Btn proc uses ebx lParam:LPARAM

    LOCAL XXBtnStr  :DWORD

mov ebx, lParam
assume ebx: ptr DRAWITEMSTRUCT

  .if [ebx].CtlType == ODT_BUTTON

    invoke SaveDC, [ebx].hdc
    invoke SetBkMode, [ebx].hdc, TRANSPARENT
     
    call DrawItem_Btn_Normal
    
    invoke RestoreDC, [ebx].hdc, -1  
  
  .endif  
    
e_t:

assume ebx: nothing
assume edx: nothing
    mov eax,1
    ret
DrawItem_Btn endp

; #########################################################################
;                                                                         ;
; Процедура обработки WM_DRAWITEM (стиль Normal)                          ;
;                                                                         ;
; #########################################################################

DrawItem_Btn_Normal proc uses ebx

LOCAL TxtLenght :DWORD
LOCAL TextBtn   :DWORD
LOCAL RctBtn    :RECT
LOCAL RctIcn    :RECT
LOCAL RctTxt    :RECT
LOCAL XXBtnStr  :XXBUTTON
LOCAL State     :BYTE
LOCAL hIcon     :HICON
LOCAL hPenDash  :HPEN
LOCAL OldhPen   :HPEN
LOCAL tmp       :DWORD
LOCAL update_text:BYTE
LOCAL update_icon:BYTE
LOCAL update_text_clr:BYTE
LOCAl hBmpDDB   :HBITMAP
LOCAL btnState  :DWORD


; ======================================================================

assume ebx: ptr DRAWITEMSTRUCT  
   
    invoke GetProp, [ebx].hwndItem, ADDR szProp_Struct
    
    mov     esi, eax
    lea     eax, XXBtnStr
    mov     edi, eax
    mov     ecx, sizeof XXBtnStr
    rep     movsb
    
    invoke GetWindowTextLength, [ebx].hwndItem
    inc eax
    mov TxtLenght, eax
    
    invoke GlobalAlloc, GPTR, TxtLenght         ;возьмём память
    mov TextBtn, eax
    invoke GetWindowText, [ebx].hwndItem, TextBtn, TxtLenght

    invoke CopyRect, ADDR RctBtn, ADDR [ebx].rcItem
    invoke CopyRect, ADDR RctIcn, ADDR [ebx].rcItem
    invoke CopyRect, ADDR RctTxt, ADDR [ebx].rcItem

    mov update_text, FALSE
    mov update_icon, FALSE
    mov update_text_clr, FALSE
    mov hIcon, 0
    mov State, NULL
    mov btnState, BTN_STATE_NORMAL

    .if [ebx].itemAction & ODA_DRAWENTIRE       ; первоначальный вид кнопки

        .if [ebx].itemState & ODS_DISABLED    
            mov State, GRAY
            invoke GetSysColor,COLOR_GRAYTEXT
            invoke SetTextColor,[ebx].hdc, eax
            mov update_text_clr, TRUE
        .endif

        mov eax, hBtnMouseHover
        .if eax == [ebx].hwndItem               ; Мышь над кнопкой, от WM_MOUSEMOVE
            
            mov btnState, BTN_STATE_HOVER
            
            .if XXBtnStr.hBitmap_hover
                mov update_text, TRUE
            .endif
            
            .if XXBtnStr.m_border               ; рисуем границы кнопки
            
                .if XXBtnStr.m_flat == TRUE     ; кнопка плоская
                    invoke DrawEdge, [ebx].hdc, ADDR RctBtn, EDGE_RAISED,BF_RECT                    
                .endif
            
            .endif
            
            .if XXBtnStr.hFont_hover && !State
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_hover
                mov update_text, TRUE
            .endif
            
            .if XXBtnStr.hIcon_hover
                m2m hIcon, XXBtnStr.hIcon_hover
                mov update_icon, TRUE
            .elseif XXBtnStr.hIcon_normal
                m2m hIcon, XXBtnStr.hIcon_normal
                mov update_icon, TRUE
            .endif
            
            .if XXBtnStr.hover_clr       
                invoke SetTextColor, [ebx].hdc, XXBtnStr.hover_clr
                mov update_text_clr, TRUE
            .endif
            
        .else                                   ; Мышь не над кнопкой (изначальный вид)
            
            mov btnState, BTN_STATE_NORMAL
            
            .if XXBtnStr.hFont_normal
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_normal                
            .endif
            mov update_text, TRUE       ; в любом случае обновляем
            
           .if XXBtnStr.hIcon_normal
                m2m hIcon, XXBtnStr.hIcon_normal
                mov update_icon, TRUE
           .endif           
           
           .if XXBtnStr.normal_clr && !State
                invoke SetTextColor, [ebx].hdc, XXBtnStr.normal_clr                
           .elseif !State
                invoke GetSysColor,COLOR_BTNTEXT
                invoke SetTextColor, [ebx].hdc, eax
           .endif
           mov update_text_clr, TRUE            
            
            .if XXBtnStr.m_border               ; рисуем границы кнопки
            
                .if XXBtnStr.m_flat == FALSE        ; кнопка не плоская                   
                    
                    invoke DrawEdge, [ebx].hdc, ADDR RctBtn, EDGE_RAISED,BF_RECT                
                .else    
                    ; стереть DrawEdge, если мышь ушла с кнопки
                    invoke GetSysColorBrush, COLOR_BTNFACE
                    mov tmp, eax 
                    invoke FrameRect, [ebx].hdc, ADDR RctBtn, tmp
                    invoke InflateRect, ADDR RctBtn, -1, -1
                    invoke FrameRect, [ebx].hdc, ADDR RctBtn, tmp 
                    invoke InflateRect, ADDR RctBtn, 1, 1                 
                .endif
            
            .elseif
            
                    invoke FillRect, [ebx].hdc, ADDR RctBtn, COLOR_BTNFACE+1
               
            .endif
            
        .endif        

    .elseif [ebx].itemAction & ODA_SELECT       ; PUSH (DOWN and UP) BUTTON

        .if [ebx].itemState & ODS_SELECTED      ; SELECTED (DOWN) BUTTON 
        
            mov btnState, BTN_STATE_PUSH
            .if XXBtnStr.hBitmap_push
                mov update_text, TRUE
            .endif        
        
            .if XXBtnStr.hFont_hover
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_hover
                mov update_text, TRUE
            .elseif XXBtnStr.hFont_normal
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_normal
                mov update_text, TRUE                
            .endif        
        
            .if XXBtnStr.hIcon_hover
                m2m hIcon, XXBtnStr.hIcon_hover
                mov update_icon, TRUE
            .elseif XXBtnStr.hIcon_normal
                m2m hIcon, XXBtnStr.hIcon_normal
                mov update_icon, TRUE
            .endif
        
            .if XXBtnStr.m_border
                invoke DrawEdge, [ebx].hdc, ADDR RctBtn, EDGE_SUNKEN,BF_RECT
            .else
                ;invoke InflateRect, ADDR RctBtn, -5, -5
                invoke FillRect, [ebx].hdc, ADDR RctBtn, COLOR_BTNFACE+1
                ;invoke InflateRect, ADDR RctBtn, 5, 5
                sub RctIcn.bottom, 2               ; бордюр не прорисовываем
                sub RctIcn.right, 4                ; иконка приподымаеться
       
            .endif 
            
            .if XXBtnStr.push_clr        
                invoke SetTextColor, [ebx].hdc, XXBtnStr.push_clr
                mov update_text_clr, TRUE
            .endif       
        
        .else                                   ; SELECTED (UP) BUTTON
        
            mov btnState, BTN_STATE_HOVER
            .if XXBtnStr.hBitmap_hover
                mov update_text, TRUE
            .endif
            
            .if XXBtnStr.hFont_hover
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_hover
                mov update_text, TRUE
            .elseif XXBtnStr.hFont_normal
                invoke SelectObject, [ebx].hdc, XXBtnStr.hFont_normal
                mov update_text, TRUE                
            .endif
            
             .if XXBtnStr.hover_clr       
                invoke SetTextColor, [ebx].hdc, XXBtnStr.hover_clr
                mov update_text_clr, TRUE
            .else
                .if XXBtnStr.normal_clr
                    invoke SetTextColor, [ebx].hdc, XXBtnStr.normal_clr
                    mov update_text_clr, TRUE                
                .elseif XXBtnStr.push_clr
                    invoke GetSysColor,COLOR_BTNTEXT
                    invoke SetTextColor, [ebx].hdc, eax
                    mov update_text_clr, TRUE
                .endif
            .endif
            
             .if XXBtnStr.hIcon_hover
                m2m hIcon, XXBtnStr.hIcon_hover
                mov update_icon, TRUE
            .elseif XXBtnStr.hIcon_normal
                m2m hIcon, XXBtnStr.hIcon_normal
                mov update_icon, TRUE
            .endif            
            
            .if XXBtnStr.m_border
                invoke DrawEdge, [ebx].hdc, ADDR RctBtn, EDGE_RAISED,BF_RECT
            .else
                ;invoke InflateRect, ADDR RctBtn, -5, -5
                invoke FillRect, [ebx].hdc, ADDR RctBtn, COLOR_BTNFACE+1
                ;invoke InflateRect, ADDR RctBtn, 5, 5
                
            .endif
                
        .endif
        
    .endif

;    .endif
    
    ; draw icon and text

    .if hIcon && TxtLenght > 1         ; ICON + TEXT
    
        .if XXBtnStr.m_IconAlign == BS_LEFT
        
bsleft:    
                mov eax, RctBtn.right
                sub eax, RctBtn.left    
                invoke IntDiv, eax, 3
    
                mov RctIcn.right, eax               ; icon = 1/3                
                
                mov eax, RctBtn.right
                sub eax, RctBtn.left
                shr eax, 2    
                ;invoke IntDiv, eax, 4
                
                add RctTxt.left, eax                ; text = 3/4
            
        .elseif XXBtnStr.m_IconAlign == BS_RIGHT
        
                mov eax, RctBtn.right
                sub eax, RctBtn.left    
                invoke IntDiv, eax, 3
          
                m2m RctIcn.left, RctIcn.right
                sub RctIcn.left, eax                ; icon = 1/3
                
                mov eax, RctBtn.right
                sub eax, RctBtn.left
                shr eax, 2    
                ;invoke IntDiv, eax, 4                
                
                sub RctTxt.right, eax               ; text = 3/4
        
        .elseif XXBtnStr.m_IconAlign == BS_TOP
        
                mov eax, RctBtn.bottom
                sub eax, RctBtn.top   
                invoke IntDiv, eax, 3
        
                sub RctIcn.bottom, eax      ; icon = 2/3
                
                mov eax, RctBtn.bottom
                sub eax, RctBtn.top
                shr eax, 1    
                ;invoke IntDiv, eax, 2
                
                mov RctTxt.top, eax         ; text = 1/2
                
        .else
                
                jmp bsleft        
        .endif
        
;    .elseif XXBtnStr.hIcon_normal && TxtLenght <= 1   ; ONLY ICON
;        
;    
;    .elseif !XXBtnStr.hIcon_normal && TxtLenght > 1   ; ONLY TEXT
   
    .endif
   
    .if update_text || update_icon 
            
            .if btnState == BTN_STATE_NORMAL && XXBtnStr.hBitmap_normal
            
                m2m hBmpDDB, XXBtnStr.hBitmap_normal
                jmp @f
                
            .elseif btnState == BTN_STATE_HOVER && XXBtnStr.hBitmap_hover 
            
                m2m hBmpDDB, XXBtnStr.hBitmap_hover
                jmp @f
                
            .elseif btnState == BTN_STATE_PUSH && XXBtnStr.hBitmap_push 

                m2m hBmpDDB, XXBtnStr.hBitmap_push
@@: 
                invoke DrawDDB, [ebx].hdc, ADDR RctBtn, hBmpDDB
                
            .else
                        
                invoke InflateRect, ADDR RctBtn, -5, -5 
                invoke FillRect, [ebx].hdc, ADDR RctBtn, COLOR_BTNFACE+1
                invoke InflateRect, ADDR RctBtn, 5, 5
                
            .endif
    

     .elseif update_text_clr
         
        jmp no_icon_update
     .else
        jmp no_text_update
     .endif
    
    .if hIcon
        invoke DrawIcnRect, [ebx].hdc, ADDR RctIcn, hIcon, State, FALSE   ; DRAW ICON
    .endif  

no_icon_update:  
    
    .if TxtLenght > 1
       dec TxtLenght
       invoke DrawText, [ebx].hdc, TextBtn, TxtLenght, ADDR RctTxt, DT_VCENTER + DT_SINGLELINE + DT_CENTER
    .endif
    
no_text_update: 

;    .if [ebx].itemAction & ODA_FOCUS
    
        ; Фокус прорисовываем только после последнего Fill rect
    
        .if [ebx].itemState & ODS_FOCUS
            invoke InflateRect, ADDR RctBtn, -4, -4
            invoke GetSysColor, COLOR_GRAYTEXT
            invoke CreatePen, PS_DOT, 1, eax
            mov hPenDash, eax 
            invoke SelectObject, [ebx].hdc, hPenDash
            mov OldhPen, eax
            invoke MoveToEx, [ebx].hdc, RctBtn.left, RctBtn.top, 0
            invoke LineTo, [ebx].hdc, RctBtn.right, RctBtn.top
            invoke LineTo, [ebx].hdc, RctBtn.right, RctBtn.bottom
            invoke LineTo, [ebx].hdc, RctBtn.left, RctBtn.bottom
            invoke LineTo, [ebx].hdc, RctBtn.left, RctBtn.top
            invoke SelectObject, [ebx].hdc, OldhPen
            invoke DeleteObject, hPenDash
            invoke InflateRect, ADDR RctBtn, 4, 4
        .else
            .if !XXBtnStr.hBitmap_normal
                invoke GetSysColorBrush, COLOR_BTNFACE
                mov tmp, eax
                invoke InflateRect, ADDR RctBtn, -4, -4 
                invoke FrameRect, [ebx].hdc, ADDR RctBtn, tmp
                invoke InflateRect, ADDR RctBtn, 4, 4
                invoke InflateRect, ADDR RctBtn, -3, -3 
                invoke FrameRect, [ebx].hdc, ADDR RctBtn, tmp
                invoke InflateRect, ADDR RctBtn, 3, 3
            .endif
        .endif
    

    invoke GlobalFree, TextBtn     ;освободим память

assume ebx: nothing

   ret
DrawItem_Btn_Normal endp

; #########################################################################
;                                                                         ;
; Установливает для кнопки стиль OWNERDRAW                                ;
;                                                                         ;
; #########################################################################

BtnToOwnerdraw proc hWin:HWND

    invoke GetWindowLong, hWin, GWL_STYLE
    .if !(eax & BS_OWNERDRAW) || !(eax & BS_PUSHBUTTON)
        xor eax, BS_DEFPUSHBUTTON
        or eax, BS_OWNERDRAW        
        invoke SetWindowLong, hWin, GWL_STYLE, eax
    .endif    
    
    ret
BtnToOwnerdraw endp

; #########################################################################
;                                                                         ;
; Убирает для кнопки стиль OWNERDRAW                                      ;
;                                                                         ;
; #########################################################################

OwnerdrawToBtn proc hWin:HWND

    invoke GetWindowLong, hWin, GWL_STYLE
    .if eax & BS_OWNERDRAW
        xor eax, BS_OWNERDRAW    
        invoke SetWindowLong, hWin, GWL_STYLE, eax
    .endif    
    
    ret
OwnerdrawToBtn endp

; #########################################################################
;                                                                         ;
; Скопируем структуру XXBUTTON в виделенную для кнопки память.            ;
;                                                                         ;
; #########################################################################

SetStyleButton proc uses ebx hBtn :HWND, lpXXButton :DWORD

    LOCAL  hMem :DWORD

    invoke FreeXXStruct, hBtn

    invoke GlobalAlloc,GPTR, sizeof(XXBUTTON)         ;возьмём память
    mov hMem, eax
    invoke SetProp, hBtn, ADDR szProp_Struct, hMem
    invoke MemCopy, lpXXButton, hMem, sizeof(XXBUTTON)

	ret
SetStyleButton endp

; #########################################################################
;                                                                         ;
; Освобождает память занимаемую структурой XXBUTTON                       ;
;                                                                         ;
; #########################################################################

FreeXXStruct proc hBtn:HWND

    invoke GetProp, hBtn, ADDR szProp_Struct
    .if eax
        invoke GlobalFree, eax     ;освободим память
        invoke RemoveProp, hBtn, ADDR szProp_Struct    
    .endif

	ret
FreeXXStruct endp

; =========================================================================
;               Экспортируеме функции управления кнопкой
;
; #########################################################################
;                                                                         ;
; Процедура меняет стиль кнопки.                                          ;  
;                                                                         ;
; #########################################################################

PUBLIC RedrawButton

RedrawButton proc hBtn :HWND, lpXXButton :DWORD
    
    invoke IsWindow, hBtn
    .if !eax
        return FALSE
    .endif

    .if lpXXButton
    
        invoke BtnToOwnerdraw, hBtn
        invoke GetParent, hBtn    
        invoke SetNewWndProc, eax, ADDR WndProc_Parent_Btn    
        invoke SetNewWndProc, hBtn, ADDR WndProc_Btn
        
        invoke SetStyleButton, hBtn, lpXXButton
        
    .else
    
        invoke OwnerdrawToBtn, hBtn
        invoke SetOldWndProc, hBtn, ADDR WndProc_Btn        
        invoke FreeXXStruct, hBtn
        
    .endif
    
    mov eax, TRUE
    ret
RedrawButton endp