;-----------------------------------------------------------
; DataGrid control.
; This is conrol, that displays data in the form of Grid.
; The control does not handle the data itself, but calls
; callback functions in order to get the data and other
; properties.
; It is intended to be a base for handling of complex data
; sets, such as database tables, memory dump tables, etc.
;
; This control is part of "FreshLib"
; This source is distributed under BSD 2-clause license
; Check for details at: http://fresh.flatassembler.net
;
;
;
; (c)2004 John Found (Fresh developement team)
;-----------------------------------------------------------

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

        mov     [.wc.style], CS_OWNDC
        mov     [.wc.lpfnWndProc], TDataGrid.DataGridWinProc
        mov     eax, [hInstance]
        mov     [.wc.hInstance], eax
        invoke  LoadCursorW, 0, IDC_ARROW
        mov     [.wc.hCursor], eax
        mov     [.wc.hbrBackground], NULL;COLOR_WINDOW+1
        mov     [.wc.lpszClassName], cDataGridClassName

        invoke  RegisterClassW, esi
        return
endp


iglobal
  cDataGridClassName du 'TDataGrid', 0
  property propCtrlData, 'CtrlData'
endg


;--------------------------------------------
; This procedure executes very common task,
; extracting control data assigned to the
; window property.
; Maybe it have to resides somewhere else and
; to be used in more common way for all controls.
;--------------------------------------------
proc GetCtrlData, .hwnd, .datasize
begin
        invoke  GetPropW, [.hwnd], [propCtrlData]
        push    eax
        test    eax, eax
        jz      .finish

        invoke  IsBadReadPtr, eax, [.datasize]
        test    eax, eax
        jz      .finish

        and     dword [esp], 0

.finish:
        pop     eax
        return
endp


;----------------------------------------------------
; Window procedure of the TDataGrid control.
;----------------------------------------------------
proc TDataGrid.DataGridWinProc, .hwnd, .wmsg, .wparam, .lparam
begin
        push    esi edi ebx

        invoke  GetPropW, [.hwnd], [propCtrlData]
        mov     edi, eax

        dispatch [.wmsg]

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

.finish:
       pop      ebx edi esi
       return


oncase WM_SETFONT
        invoke  SelectObject, [edi+TDataGrid.hShadowDC], [.wparam]
        jmp     .finish


oncase WM_CREATE
; TDataGrid structure.
        mov     eax, [.lparam]
        mov     esi, [eax+CREATESTRUCT.lpCreateParams]
        invoke  IsBadWritePtr, esi, sizeof.TDataGrid
        test    eax, eax
        jz      .pointerOK

        or      eax, -1
        jmp     .finish

.pointerOK:
        invoke  SetPropW, [.hwnd], [propCtrlData], esi

        invoke  CreateCompatibleDC, NULL
        mov     [esi+TDataGrid.hShadowDC], eax

        stdcall GetMem, 4
        mov     [esi+TDataGrid.ptrColX], eax
        stdcall GetMem, 4
        mov     [esi+TDataGrid.ptrRowY], eax

        mov     eax, [.hwnd]
        mov     [esi+TDataGrid.hwnd], eax

; Default values for sizes.
        mov     [esi+TDataGrid.ColWidth], 64
        mov     [esi+TDataGrid.RowHeight], 16
        mov     [esi+TDataGrid.FixedCols], 1
        mov     [esi+TDataGrid.FixedRows], 1
        mov     [esi+TDataGrid.Flags], dgfGridLinesFixed or dgfGridlines
        mov     [esi+TDataGrid.focused.x], 2
        mov     [esi+TDataGrid.focused.y], 2
        mov     [esi+TDataGrid.selflags], -1

        invoke  GetSysColor, COLOR_BTNFACE
        mov     [esi+TDataGrid.clFixed], eax
        invoke  GetSysColor, COLOR_WINDOW
        mov     [esi+TDataGrid.clCell], eax
        invoke  GetSysColor, COLOR_HIGHLIGHT
        mov     [esi+TDataGrid.clSelected], eax
        invoke  GetSysColor, COLOR_WINDOWTEXT
        mov     [esi+TDataGrid.clTextCell], eax
        invoke  GetSysColor, COLOR_BTNTEXT
        mov     [esi+TDataGrid.clTextFixed], eax
        invoke  GetSysColor, COLOR_HIGHLIGHTTEXT
        mov     [esi+TDataGrid.clTextSelected], eax
        invoke  GetSysColor, COLOR_HOTLIGHT
        mov     [esi+TDataGrid.clMarked], eax

        stdcall TDataGrid.SetCounts, esi, 0, 0

        xor     eax, eax
        jmp     .finish

oncase WM_SIZE
; creates new bitmap
        movsx   eax, word [.lparam+2] ; height
        movsx   ecx, word [.lparam]   ; width

        push    eax
        push    ecx

        invoke  GetDC, [.hwnd]
        mov     ebx, eax
        invoke  CreateCompatibleBitmap, ebx ; size from the stack
        mov     [edi+TDataGrid.hShadow], eax
        invoke  SelectObject, [edi+TDataGrid.hShadowDC], eax
        invoke  DeleteObject, eax
        invoke  ReleaseDC, [.hwnd], ebx

; invalidate shadow buffer.
        mov     [edi+TDataGrid.fShadowReady], 0

        stdcall TDataGrid.__AdjustScrollbars, edi
        xor     eax, eax
        jmp     .finish

oncase WM_DESTROY
        invoke  DeleteDC, [edi+TDataGrid.hShadowDC]
        invoke  DeleteObject, [edi+TDataGrid.hSelected]

        stdcall FreeMem, [edi+TDataGrid.ptrColX]
        stdcall FreeMem, [edi+TDataGrid.ptrRowY]
        mov     [edi+TDataGrid.hwnd], 0

        xor     eax, eax
        jmp     .finish

oncase WM_MOUSEWHEEL
        movsx   eax, word [.wparam+2]
        cdq
        mov     ecx, 120
        idiv    ecx
        imul    eax, [edi+TDataGrid.RowHeight]
        mov     ebx, eax
        invoke  GetScrollPos, [.hwnd], SB_VERT
        sub     eax, ebx
        invoke  SetScrollPos, [.hwnd], SB_VERT, eax, TRUE
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke  InvalidateRect, [.hwnd], NULL, FALSE
        xor     eax, eax
        jmp     .finish


oncase WM_PAINT
locals
  .ps PAINTSTRUCT
endl
        lea     esi, [.ps]
        invoke  BeginPaint, [.hwnd], esi
        test    eax, eax
        jz      .finish

        cmp     [edi+TDataGrid.fShadowReady], 0
        jne     .shadowok

        lea     eax, [esi+PAINTSTRUCT.rcPaint]
        stdcall TDataGrid.RedrawShadow, eax
        inc     [edi+TDataGrid.fShadowReady]

.shadowok:

        mov     ecx, [esi+PAINTSTRUCT.rcPaint.right]
        mov     edx, [esi+PAINTSTRUCT.rcPaint.bottom]
        sub     ecx, [esi+PAINTSTRUCT.rcPaint.left]
        sub     edx, [esi+PAINTSTRUCT.rcPaint.top]

        invoke  BitBlt, [esi+PAINTSTRUCT.hdc],                  \
                        [esi+PAINTSTRUCT.rcPaint.left],         \
                        [esi+PAINTSTRUCT.rcPaint.top],          \
                        ecx, edx,                               \
                        [edi+TDataGrid.hShadowDC],              \
                        [esi+PAINTSTRUCT.rcPaint.left],         \
                        [esi+PAINTSTRUCT.rcPaint.top],          \
                        SRCCOPY

        invoke  EndPaint, [.hwnd], esi
        xor     eax, eax
        jmp     .finish


oncase DGM_SETPARAM
        cmp     [.wparam], sizeof.TDataGrid - 4
        jg      .finish

        mov     eax, [.wparam]
        pushd    [.lparam]
        popd     [edi+eax]

        stdcall  TDataGrid.__AdjustScrollbars, edi
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke   InvalidateRect, [.hwnd], NULL, FALSE
        jmp      .finish

oncase DGM_GETPARAM
        cmp     [.wparam], sizeof.TDataGrid - 4
        jg      .finish

        mov     eax, [.wparam]
        mov     eax, [edi+eax]
        jmp      .finish

oncase WM_HSCROLL
        mov      eax, [edi+TDataGrid.ColWidth]
        jmp      .oncommonscroll
oncase WM_VSCROLL
        mov      eax, [edi+TDataGrid.RowHeight]

.oncommonscroll:
        stdcall  ScrollBarMessage, [.hwnd], [.wmsg], [.wparam], NULL, eax
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke   InvalidateRect, [.hwnd], NULL, FALSE
        jmp      .finish



oncase WM_GETDLGCODE
        mov     eax, DLGC_WANTARROWS or DLGC_WANTCHARS
        jmp     .finish


oncase WM_KEYDOWN
        mov     eax, [.wparam]
        cmp     eax, VK_DOWN
        je      .down
        cmp     eax, VK_UP
        je      .up
        cmp     eax, VK_LEFT
        je      .left
        cmp     eax, VK_RIGHT
        je      .right
        cmp     eax, VK_PGUP
        je      .pgup
        cmp     eax, VK_PGDN
        je      .pgdn
        cmp     eax, VK_HOME
        je      .home
        cmp     eax, VK_END
        je      .end
        jmp     .ondefault


.up:
        mov     eax, [edi+TDataGrid.focused.y]
        dec     eax
        cmp     eax, [edi+TDataGrid.FixedRows]
        jb      .endkeydown
        mov     [edi+TDataGrid.focused.y], eax
        jmp     .ensurevisible

.down:
        mov     eax, [edi+TDataGrid.focused.y]
        inc     eax
        cmp     eax, [edi+TDataGrid.RowCount]
        jae     .endkeydown
        mov     [edi+TDataGrid.focused.y], eax
        jmp     .ensurevisible

.left:
        mov     eax, [edi+TDataGrid.focused.x]
        dec     eax
        cmp     eax, [edi+TDataGrid.FixedCols]
        jl      .endkeydown
        mov     [edi+TDataGrid.focused.x], eax
        jmp     .ensurevisible

.right:
        mov     eax, [edi+TDataGrid.focused.x]
        inc     eax
        cmp     eax, [edi+TDataGrid.ColCount]
        jae     .endkeydown
        mov     [edi+TDataGrid.focused.x], eax
        jmp     .ensurevisible

.pgup:
        mov     ebx, -1
        jmp     .pgupdn

.pgdn:
        mov     ebx, 1

.pgupdn:
locals
  .si SCROLLINFO
endl
        mov     [.si.cbSize], sizeof.SCROLLINFO
        mov     [.si.fMask], SIF_PAGE or SIF_POS or SIF_RANGE
        lea     eax, [.si]
        invoke  GetScrollInfo, [.hwnd], SB_VERT, eax
        mov     eax, [.si.nPage]
        xor     edx, edx
        div     [edi+TDataGrid.RowHeight]
        imul    eax, ebx
        add     eax, [edi+TDataGrid.focused.y]

        cmp     eax, [edi+TDataGrid.FixedRows]
        jge     @f
        mov     eax, [edi+TDataGrid.FixedRows]
@@:
        cmp     eax, [edi+TDataGrid.RowCount]
        jl      @f
        mov     eax, [edi+TDataGrid.RowCount]
        dec     eax
@@:
        mov     [edi+TDataGrid.focused.y], eax
        jmp     .ensurevisible

.home:
        mov     eax, [edi+TDataGrid.FixedCols]
        mov     ecx, [edi+TDataGrid.FixedRows]
        jmp     .setfocused

.end:
        mov     eax, [edi+TDataGrid.ColCount]
        mov     ecx, [edi+TDataGrid.RowCount]
        dec     eax
        dec     ecx

.setfocused:
        mov     [edi+TDataGrid.focused.x], eax
        mov     [edi+TDataGrid.focused.y], ecx
        jmp     .ensurevisible


.ensurevisible:
; Clear existing selection
        invoke  DeleteObject, [edi+TDataGrid.hSelected]
        mov     [edi+TDataGrid.hSelected], 0

        stdcall TDataGrid.EnsureVisible, edi, [edi+TDataGrid.focused.x], [edi+TDataGrid.focused.y]
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke  InvalidateRect, [.hwnd], 0, FALSE

.endkeydown:
        xor     eax, eax
        jmp     .finish


oncase WM_LBUTTONDOWN
        invoke  SetFocus, [.hwnd]
        invoke  SetCapture, [.hwnd]

        movsx   eax, word [.lparam]
        movsx   ecx, word [.lparam+2]
        stdcall TDataGrid.HitTest3, edi, eax, ecx, htwXBoth, htwYBoth

        mov     [edi+TDataGrid.selflags], 0
        mov     [edi+TDataGrid.selection.left], eax
        mov     [edi+TDataGrid.selection.top], edx
        mov     [edi+TDataGrid.selection.right], eax
        mov     [edi+TDataGrid.selection.bottom], edx

        cmp     eax, [edi+TDataGrid.FixedCols]
        jge     .notleft

; if the cursor is on the left fixed cells
        or      [edi+TDataGrid.selflags], 1
        mov     ebx, [edi+TDataGrid.FixedCols]  ; xbeg
        mov     ecx, [edi+TDataGrid.ColCount]   ; xend
        mov     [edi+TDataGrid.selection.left], ebx
        mov     [edi+TDataGrid.selection.right], ecx

.notleft:
        cmp     edx, [edi+TDataGrid.FixedRows]
        jge     .nottop

; if the cursor is on the top fixed cells
        or      [edi+TDataGrid.selflags], 2
        mov     ebx, [edi+TDataGrid.FixedRows]  ; ybeg
        mov     ecx, [edi+TDataGrid.RowCount]   ; yend
        mov     [edi+TDataGrid.selection.top], ebx
        mov     [edi+TDataGrid.selection.bottom], ecx

.nottop:
        cmp     [edi+TDataGrid.selflags], 0
        jne     .clearcurrent

        mov     [edi+TDataGrid.focused.x], eax
        mov     [edi+TDataGrid.focused.y], edx

.clearcurrent:
        test    [.wparam], MK_CONTROL
        jnz     .invalidate

        invoke  DeleteObject, [edi+TDataGrid.hSelected]
        mov     [edi+TDataGrid.hSelected], 0

.invalidate:
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke  InvalidateRect, [.hwnd], 0, FALSE
        xor     eax, eax
        jmp     .finish


oncase WM_MOUSEMOVE
        invoke  GetCapture
        cmp     eax, [.hwnd]
        jne     .ondefault       ; temporary...

        mov     esi, [edi+TDataGrid.selflags]
        cmp     esi, 3
        je      .ondefault    ; nothing to select.

        mov     eax, htwXData
        mov     ebx, htwYData

        cmp     esi, 1
        jne     @f
        mov     eax, htwXFixed
@@:
        cmp     esi, 2
        jne     @f
        mov     ebx, htwYFixed
@@:
        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]
        stdcall TDataGrid.HitTest3, edi, ecx, edx, eax, ebx

        cmp     esi, 1
        jne     .maybecol

        mov     [edi+TDataGrid.selection.bottom], edx
        mov     eax, [edi+TDataGrid.selection.left]
        jmp     .endmove

.maybecol:
        cmp     esi, 2
        jne     .cellselect

        mov     [edi+TDataGrid.selection.right], eax
        mov     edx, [edi+TDataGrid.selection.top]
        jmp     .endmove

; select normal cells
.cellselect:
        mov     [edi+TDataGrid.selection.right], eax
        mov     [edi+TDataGrid.selection.bottom], edx

.endmove:
        stdcall TDataGrid.EnsureVisible, edi, eax, edx
        mov     [edi+TDataGrid.fShadowReady], 0
        invoke  InvalidateRect, [.hwnd], 0, FALSE

        xor     eax, eax
        jmp     .finish


oncase WM_LBUTTONUP
        invoke  GetCapture
        cmp     eax, [.hwnd]
        jne     .ondefault

        invoke  ReleaseCapture

        mov     [edi+TDataGrid.selflags], -1

        lea     esi, [edi+TDataGrid.selection]
        call    TDataGrid.SortRect

        stdcall TDataGrid.SelectRect, edi, [edi+TDataGrid.selection.left],   \
                                           [edi+TDataGrid.selection.top],    \
                                           [edi+TDataGrid.selection.right],  \
                                           [edi+TDataGrid.selection.bottom], \
                                           soMerge

        mov     [edi+TDataGrid.fShadowReady], 0
        invoke  InvalidateRect, [.hwnd], 0, FALSE

        xor     eax, eax
        jmp     .finish

        enddispatch
endp



proc TDataGrid.SortRect
begin
        mov     eax, [esi+RECT.left]
        mov     ecx, [esi+RECT.right]
        cmp     ecx, eax
        jge     @f
        xchg    eax,ecx
@@:
        mov     [esi+RECT.left],eax
        mov     [esi+RECT.right], ecx

        mov     eax, [esi+RECT.top]
        mov     ecx, [esi+RECT.bottom]
        cmp     ecx, eax
        jge     @f
        xchg    eax,ecx
@@:
        mov     [esi+RECT.top],eax
        mov     [esi+RECT.bottom], ecx
        return
endp


;-----------------------------------------------------------------------
; TDataGrid public functions.
;
;-----------------------------------------------------------------------
proc TDataGrid.RedrawShadow, .ptrRect
.rcFixedTL RECT
.rcFixedT  RECT
.rcFixedL  RECT
.rcFloat   RECT
.rcClient  RECT
begin
        lea     eax, [.rcClient]
        invoke  GetClientRect, [edi+TDataGrid.hwnd], eax

        mov     eax, [.rcClient.right]
        mov     ecx, [.rcClient.bottom]
        xor     edx, edx

        mov     [.rcFixedT.right], eax
        mov     [.rcFixedL.bottom], ecx
        mov     [.rcFloat.right], eax
        mov     [.rcFloat.bottom], ecx

        mov     [.rcFixedTL.left], edx
        mov     [.rcFixedTL.top], edx
        mov     [.rcFixedT.top], edx
        mov     [.rcFixedL.left], edx

        mov     ecx, [edi+TDataGrid.FixedCols]
        mov     eax, [edi+TDataGrid.ptrColX]
        test    eax, eax
        jnz     .colsizeok

        imul    ecx, [edi+TDataGrid.ColWidth]   ; default width if there is no size array
        jmp     .x1ok

.colsizeok:
        mov     ecx, [eax+4*ecx]
.x1ok:
        mov     edx, [edi+TDataGrid.FixedRows]
        mov     eax, [edi+TDataGrid.ptrRowY]
        test    eax, eax
        jnz     .rowsizeok

        imul    edx, [edi+TDataGrid.ColWidth]   ; default width if there is no size array
        jmp     .y1ok

.rowsizeok:
        mov     edx, [eax+4*edx]
.y1ok:
        mov     [.rcFixedTL.right], ecx
        mov     [.rcFixedTL.bottom], edx
        mov     [.rcFixedT.left], ecx
        mov     [.rcFixedT.bottom], edx
        mov     [.rcFixedL.right], ecx
        mov     [.rcFixedL.top], edx
        mov     [.rcFloat.left], ecx
        mov     [.rcFloat.top], edx

; Here all 4 rectangles are ready. Some of them can be empty of course.
; Lets make intersections with the invalidated rectangle.

.drawFixedTL:
        lea     eax, [.rcFixedTL]
        invoke  IntersectRect, eax, eax, [.ptrRect]
        test    eax, eax
        jz      .drawFixedT

        lea     eax, [.rcFixedTL]
        stdcall TDataGrid.__DrawCellArea, eax, ffHorizontal or ffVertical

.drawFixedT:
        lea     eax, [.rcFixedT]
        invoke  IntersectRect, eax, eax, [.ptrRect]
        test    eax, eax
        jz      .drawFixedL

        lea     eax, [.rcFixedT]
        stdcall TDataGrid.__DrawCellArea, eax, ffVertical

.drawFixedL:
        lea     eax, [.rcFixedL]
        invoke  IntersectRect, eax, eax, [.ptrRect]
        test    eax, eax
        jz      .drawFloat

        lea     eax, [.rcFixedL]
        stdcall TDataGrid.__DrawCellArea, eax, ffHorizontal

.drawFloat:
        lea     eax, [.rcFloat]
        invoke  IntersectRect, eax, eax, [.ptrRect]
        test    eax, eax
        jz      .finish

        lea     eax, [.rcFloat]
        stdcall TDataGrid.__DrawCellArea, eax, 0

.finish:
        return
endp


; .fFixed contains flags:
ffHorizontal = 1
ffVertical   = 2

proc TDataGrid.__DrawCellArea, .ptrClipRect, .fFixed
.Cell  RECT
.si    SCROLLINFO
.xofs  dd ?
.yofs  dd ?
.clip  RECT
.brush dd ?
.brushsel dd ?
.flags dd ?
.sel   RECT
begin
        push    ebx esi

        lea     esi, [.sel]
        lea     eax, [edi+TDataGrid.selection]
        invoke  CopyRect, esi, eax
        call    TDataGrid.SortRect
        inc     [esi+RECT.right]
        inc     [esi+RECT.bottom]

        mov     eax, [edi+TDataGrid.FixedRows]
        mov     ecx, [edi+TDataGrid.FixedCols]

; [esi+RECT.top] := max([esi+RECT.top], [edi+TDataGrid.FixedRows])
        sub     eax, [esi+RECT.top]
        sbb     edx, edx
        not     edx
        and     edx, eax
        add     [esi+RECT.top], edx

; [esi+RECT.left] := max([esi+RECT.left], [edi+TDataGrid.FixedCols])
        sub    ecx, [esi+RECT.left]
        sbb    edx, edx
        not    edx
        and    edx, ecx
        add    [esi+RECT.left], edx

        mov     esi, [.ptrClipRect]

; Create the brush for selected cells background.
        invoke  CreateSolidBrush, [edi+TDataGrid.clSelected]
        mov     [.brush], eax
        invoke  CreateSolidBrush, [edi+TDataGrid.clMarked]
        mov     [.brushsel], eax

; Set the cliping region for the shadow DC.
        invoke  CreateRectRgnIndirect, [.ptrClipRect]
        push    eax
        invoke  SelectClipRgn, [edi+TDataGrid.hShadowDC], eax
        invoke  DeleteObject ; from the stack

; erase the background
        mov     eax, [edi+TDataGrid.clCell]
        cmp     [.fFixed], 0
        je      .bgok
        mov     eax, [edi+TDataGrid.clFixed]

.bgok:
        invoke  CreateSolidBrush, eax
        push    eax
        invoke  FillRect, [edi+TDataGrid.hShadowDC], [.ptrClipRect], eax
        invoke  DeleteObject ; from the stack

        invoke  SetBkMode, [edi+TDataGrid.hShadowDC], TRANSPARENT

; If the cells are not fixed, compute the real coordinates of the rectangle.
        mov    [.si.cbSize], sizeof.SCROLLINFO
        mov    [.si.fMask], SIF_POS or SIF_RANGE
        lea    ebx, [.si]

        xor     eax, eax
        mov     [.xofs], eax
        mov     [.yofs], eax

        test    [.fFixed], ffHorizontal
        jnz     .horizOK

        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_HORZ, ebx
        mov     eax, [ebx+SCROLLINFO.nPos]
        sub     eax, [ebx+SCROLLINFO.nMin]
        mov     [.xofs], eax
        add     [esi+RECT.left], eax
        add     [esi+RECT.right], eax

.horizOK:
        test    [.fFixed], ffVertical
        jnz     .vertOK

        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_VERT, ebx
        mov     eax, [ebx+SCROLLINFO.nPos]
        sub     eax, [ebx+SCROLLINFO.nMin]
        mov     [.yofs], eax
        add     [esi+RECT.top], eax
        add     [esi+RECT.bottom], eax

; Determine the cell range for the given clip rectangle.
.vertOK:
        stdcall TDataGrid.__SearchCellRange, [esi+RECT.left], [esi+RECT.right], [edi+TDataGrid.ptrColX], [edi+TDataGrid.ColCount]
        mov     [.Cell.left], eax
        mov     [.Cell.right], edx

        stdcall TDataGrid.__SearchCellRange, [esi+RECT.top], [esi+RECT.bottom], [edi+TDataGrid.ptrRowY], [edi+TDataGrid.RowCount]
        mov     [.Cell.top], eax
        mov     [.Cell.bottom], edx

        mov     ebx, [.Cell.top]

.yloop:
        cmp     ebx, [.Cell.bottom]
        jge     .endyloop

        mov     eax, [edi+TDataGrid.ptrRowY]
        mov     ecx, [4*ebx+eax+4]              ; bottom
        mov     eax, [4*ebx+eax]                ; top
        sub     ecx, [.yofs]
        sub     eax, [.yofs]

        mov     [.clip.bottom], ecx
        mov     [.clip.top], eax

        mov     esi, [.Cell.left]

.xloop:
        cmp     esi, [.Cell.right]
        jge     .endxloop

        mov     eax, [edi+TDataGrid.ptrColX]
        mov     ecx, [4*esi+eax+4]
        mov     eax, [4*esi+eax]
        sub     ecx, [.xofs]
        sub     eax, [.xofs]

        mov     [.clip.right], ecx
        mov     [.clip.left], eax

; draw the background if the cell is selected
        mov     eax, [edi+TDataGrid.hSelected]
        test    eax, eax
        jz      .bkgroundok

        invoke  PtInRegion, eax, esi, ebx
        test    eax, eax
        jz      .bkgroundok

        lea     eax, [.clip]
        invoke  FillRect, [edi+TDataGrid.hShadowDC], eax, [.brush]

.bkgroundok:
        mov     [.flags], eax   ; true is the cell is selected

        cmp     [edi+TDataGrid.selflags], -1
        je      .notselection

        lea     eax, [.sel]
        invoke  PtInRect, eax, esi, ebx
        test    eax, eax
        jz      .notselection

        lea     eax, [.clip]
        invoke  FillRect, [edi+TDataGrid.hShadowDC], eax, [.brushsel]
        inc     [.flags]

.notselection:
        cmp     [edi+TDataGrid.OnDrawItem], 0
        je      .drawgridlines

        lea     eax, [.clip]
        stdcall [edi+TDataGrid.OnDrawItem], [edi+TDataGrid.hShadowDC], eax, esi, ebx, [edi+TDataGrid.ptrGridData], [.flags], [.fFixed]

; draw the gridlines:
.drawgridlines:
        cmp     [.fFixed], FALSE
        je      .datagridline

        test    [edi+TDataGrid.Flags], dgfGridLinesFixed
        jz      .gridok

        lea     eax, [.clip]
        invoke  DrawEdge, [edi+TDataGrid.hShadowDC], eax, BDR_RAISEDINNER, BF_RECT
        jmp     .gridok

.datagridline:
        test    [edi+TDataGrid.Flags], dgfGridlines
        jz      .gridok

        mov     eax, [.clip.bottom]
        mov     edx, [.clip.top]
        mov     ecx, [.clip.right]
        dec     eax
        dec     edx
        dec     ecx

        push    edx ecx
        push    eax ecx
        push    0 eax [.clip.left]

        invoke  MoveToEx, [edi+TDataGrid.hShadowDC]
        invoke  LineTo, [edi+TDataGrid.hShadowDC]
        invoke  LineTo, [edi+TDataGrid.hShadowDC]

.gridok:
        cmp     esi, [edi+TDataGrid.focused.x]
        jne     .endcell
        cmp     ebx, [edi+TDataGrid.focused.y]
        jne     .endcell

        lea     eax, [.clip]
        dec     [.clip.right]
        dec     [.clip.bottom]
        invoke  DrawFocusRect, [edi+TDataGrid.hShadowDC], eax
        inc     [.clip.right]
        inc     [.clip.bottom]

.endcell:
        inc     esi
        jmp     .xloop

.endxloop:
        inc     ebx
        jmp     .yloop

.endyloop:
        invoke  DeleteObject, [.brush]
        invoke  DeleteObject, [.brushsel]

        pop     esi ebx
        return
endp



;------------------------------------------------------------------
; If .Col, .Row is inside the visible client area, does nothing.
; , if .Col, .Row is outside this area, provides scrolling to
; make it visible to the center of the client area.
;------------------------------------------------------------------
proc TDataGrid.EnsureVisible, .ptrGrid, .Col, .Row
.si  SCROLLINFO
.client RECT
begin
        push    ebx edi
        mov     edi, [.ptrGrid]

        lea     eax, [.client]
        invoke  GetClientRect, [edi+TDataGrid.hwnd], eax

        mov     [.si.cbSize], sizeof.SCROLLINFO
        mov     [.si.fMask], SIF_RANGE or SIF_POS

        mov     eax, [.Col]
        cmp     eax, [edi+TDataGrid.FixedCols]
        jb      .posvertical                    ; fixed columns are always inside the screen

        lea     eax, [.si]
        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_HORZ, eax

        mov     eax, [.client.right]
        mov     ecx, [.si.nPos]
        sub     eax, [.si.nMin]
        add     eax, ecx
        mov     [.client.left], ecx
        mov     [.client.right], eax

        mov     edx, [edi+TDataGrid.ptrColX]
        mov     ecx, [.Col]
        mov     ebx, [edx+4*ecx]
        mov     ecx, [edx+4*ecx+4]

        cmp     ecx, [.client.right]
        jle     .rightok

        sub     ecx, [.client.right]
        add     ecx, [.client.left]
        mov     [.si.nPos], ecx
        lea     ecx, [.si]
        invoke  SetScrollInfo, [edi+TDataGrid.hwnd], SB_HORZ, ecx, TRUE
        jmp     .posvertical

.rightok:
        cmp     ebx, [.client.left]
        jge     .posvertical

        mov     [.si.nPos], ebx
        lea     ecx, [.si]
        invoke  SetScrollInfo, [edi+TDataGrid.hwnd], SB_HORZ, ecx, TRUE

.posvertical:
        mov     eax, [.Row]
        cmp     eax, [edi+TDataGrid.FixedRows]
        jb      .endpos

        lea     eax, [.si]
        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_VERT, eax

        mov     eax, [.client.bottom]
        mov     ecx, [.si.nPos]
        sub     eax, [.si.nMin]
        add     eax, ecx
        mov     [.client.top], ecx
        mov     [.client.bottom], eax

        mov     edx, [edi+TDataGrid.ptrRowY]
        mov     ecx, [.Row]
        mov     ebx, [edx+4*ecx]
        mov     ecx, [edx+4*ecx+4]

        cmp     ecx, [.client.bottom]
        jle     .bottomok

        sub     ecx, [.client.bottom]
        add     ecx, [.client.top]
        mov     [.si.nPos], ecx
        lea     ecx, [.si]
        invoke  SetScrollInfo, [edi+TDataGrid.hwnd], SB_VERT, ecx, TRUE
        jmp     .endpos

.bottomok:
        cmp     ebx, [.client.top]
        jge     .endpos

        mov     [.si.nPos], ebx
        lea     ecx, [.si]
        invoke  SetScrollInfo, [edi+TDataGrid.hwnd], SB_VERT, ecx, TRUE

.endpos:
        pop     edi ebx
        return
endp



;-----------------------------------------------------------------------
; Returns the cell coordinates of the cell, where pixel [.x,.y] is located.
; eax : Column
; edx ; Row
;--------------------------------------------------------------------------
proc TDataGrid.HitTest3, .ptrGrid, .x, .y, .fWhereX, .fWhereY
.si SCROLLINFO
begin
        push    edi esi ebx

        mov     edi, [.ptrGrid]

        mov     [.si.cbSize], sizeof.SCROLLINFO
        mov     [.si.fMask], SIF_POS or SIF_RANGE

        lea     eax, [.si]
        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_HORZ, eax
        mov     eax, [.si.nMin]
        mov     ecx, [.si.nPos]

        cmp     [.fWhereX], htwXFixed
        je      .xfixed
        cmp     [.fWhereX], htwXData
        je      .fixx

        cmp     [.x], eax
        jl      .xfixed

.fixx:
        sub     [.x], eax
        add     [.x], ecx

.xfixed:
        mov     [.si.fMask], SIF_POS or SIF_RANGE
        lea     eax, [.si]
        invoke  GetScrollInfo, [edi+TDataGrid.hwnd], SB_VERT, eax
        mov     eax, [.si.nMin]
        mov     ecx, [.si.nPos]

        cmp     [.fWhereY], htwYFixed
        je      .yfixed
        cmp     [.fWhereY], htwYData
        je      .fixy

        cmp     [.y], eax
        jl      .yfixed

.fixy:
        sub     [.y], eax
        add     [.y], ecx

.yfixed:
        stdcall TDataGrid.__SearchCellRange, [.x], [.x], [edi+TDataGrid.ptrColX], [edi+TDataGrid.ColCount]
        push    eax
        stdcall TDataGrid.__SearchCellRange, [.y], [.y], [edi+TDataGrid.ptrRowY], [edi+TDataGrid.RowCount]

        mov     edx, eax
        pop     eax

        pop     ebx esi edi
        return
endp




;--------------------------------------------
; possible operations: all RGN_ operations.
;
  soMerge = RGN_OR
  soClearPrev = RGN_COPY
  soInvert    = RGN_XOR

proc TDataGrid.SelectRect, .ptrGrid, .BegCol, .BegRow, .EndCol, .EndRow, .operation
begin
        push    edi ebx

        mov     edi, [.ptrGrid]

        mov     eax, [edi+TDataGrid.FixedRows]
        mov     ecx, [edi+TDataGrid.FixedCols]

; [.BegRow] := max([.BegRow], [edi+TDataGrid.FixedRows])
        sub     eax, [.BegRow]
        sbb     edx, edx
        not     edx
        and     edx, eax
        add     [.BegRow], edx

; [.BegCol] := max([.BegCol], [edi+TDataGrid.FixedCols])
        sub    ecx, [.BegCol]
        sbb    edx, edx
        not    edx
        and    edx, ecx
        add    [.BegCol], edx

        mov     eax, [edi+TDataGrid.RowCount]
        mov     ecx, [edi+TDataGrid.ColCount]
        cmp     [.BegRow], eax
        jae     .finish
        cmp     [.BegCol], ecx
        jae     .finish

        dec     eax
        dec     ecx

; [.EndRow] := min([.EndRow], [edi+TDataGrid.RowCount])
        sub     eax, [.EndRow]
        sbb     edx, edx
        and     edx, eax
        add     [.EndRow], edx

; [.EndCol] := min([.EndCol], [edi+TDataGrid.ColCount])
        sub    ecx, [.EndCol]
        sbb    edx, edx
        and    edx, ecx
        add    [.EndCol], edx

        inc     [.EndCol]
        inc     [.EndRow]

        invoke  CreateRectRgn, [.BegCol], [.BegRow], [.EndCol], [.EndRow]
        mov     ebx, eax

        cmp     [edi+TDataGrid.hSelected], 0
        je      .newselection

        invoke  CombineRgn, [edi+TDataGrid.hSelected],  \
                            ebx,                        \
                            [edi+TDataGrid.hSelected],  \
                            [.operation]

        cmp     eax, NULLREGION
        jne     .free

        invoke  DeleteObject, [edi+TDataGrid.hSelected]
        mov     [edi+TDataGrid.hSelected], 0

.free:
        invoke  DeleteObject, ebx
.finish:
        pop     ebx edi
        return

.newselection:
        mov     [edi+TDataGrid.hSelected], ebx
        pop     ebx edi
        return
endp



proc TDataGrid.GetCellXY, .ptrGrid, .Col, .Row
begin
        push    esi

        mov     esi, [.ptrGrid]
        mov     eax, [.Col]
        mov     edx, [.Row]
        shl     eax, 2
        shl     edx, 2
        add     eax, [esi+TDataGrid.ptrColX]
        add     edx, [esi+TDataGrid.ptrRowY]
        mov     eax, [eax]
        mov     edx, [edx]

        pop     esi
        return
endp



proc TDataGrid.SetCounts, .ptrGrid, .ColCount, .RowCount
.ycount dd ?
.ptrNewColX  dd ?
.ptrNewRowY  dd ?
begin
        push    esi ebx edi

        mov     [.ptrNewColX], 0
        mov     [.ptrNewRowY], 0
        mov     ebx, [.ptrGrid]

; resize column coordinates array...
        mov     eax, [ebx+TDataGrid.ptrColX]
        mov     [.ptrNewColX], eax

        mov     eax, [.ColCount]
        cmp     eax, [ebx+TDataGrid.ColCount]
        je      .resizey

        mov     eax, [.ColCount]
        lea     eax, [4*eax+4]          ; byte size of the array.
        test    eax, eax
        jg      @f
        mov     eax, 4
@@:
        stdcall ResizeMem, [ebx+TDataGrid.ptrColX], eax
        mov     [.ptrNewColX], eax
        test    eax, eax
        jz      .finish

        mov     edi, eax

        mov     ecx, [ebx+TDataGrid.ColCount]
        cmp     ecx, [.ColCount]
        jge     .resizey

        mov     eax, [edi+4*ecx]

.newsizesx:
        inc     ecx
        cmp     ecx, [.ColCount]
        jg      .resizey

        add     eax, [ebx+TDataGrid.ColWidth]
        mov     [edi+4*ecx], eax
        jmp     .newsizesx

.resizey:
; resize row coordinates array...
        mov     eax, [ebx+TDataGrid.ptrRowY]
        mov     [.ptrNewRowY], eax

        mov     eax, [.RowCount]
        cmp     eax, [ebx+TDataGrid.RowCount]
        je      .finish

        mov     eax, [.RowCount]
        lea     eax, [4*eax+4]          ; byte size of the array.
        test    eax, eax
        jg      @f
        mov     eax, 4
@@:
        stdcall ResizeMem,[ebx+TDataGrid.ptrRowY], eax
        mov     [.ptrNewRowY], eax
        test    eax, eax
        jz      .finish

        mov     edi, eax

        mov     ecx, [ebx+TDataGrid.RowCount]
        cmp     ecx, [.RowCount]
        jg      .finish

        mov     eax, [edi+4*ecx]

.newsizesy:
        inc     ecx
        cmp     ecx, [.RowCount]
        jg      .finish

        add     eax, [ebx+TDataGrid.RowHeight]
        mov     [edi+4*ecx], eax
        jmp     .newsizesy

.finish:
        xor     eax, eax
        cmp     eax, [.ptrNewColX]
        je      .error1
        cmp     eax, [.ptrNewRowY]
        je      .error2

        mov     ecx, [.RowCount]
        mov     edx, [.ColCount]
        mov     [ebx+TDataGrid.RowCount], ecx
        mov     [ebx+TDataGrid.ColCount], edx

        mov     ecx, [.ptrNewColX]
        mov     edx, [.ptrNewRowY]
        mov     [ebx+TDataGrid.ptrColX], ecx
        mov     [ebx+TDataGrid.ptrRowY], edx

        xor     eax, eax
        pop     edi ebx esi
        return

.error2:
        stdcall FreeMem, [.ptrNewColX]

.error1:
        xor     eax, eax
        inc     eax
        pop     edi ebx esi
        return
endp


proc TDataGrid.SetColWidth, .ptrGrid, .Col, .Width
begin
        mov     eax, [.ptrGrid]
        stdcall TDataGrid.__SetRowColSize, eax, [eax+TDataGrid.ptrColX], [eax+TDataGrid.ColCount], [.Col], [.Width]
        return
endp


proc TDataGrid.SetColumnsWidth, .ptrGrid, .BegCol, .EndCol, .Width
begin
        push    ebx edi

        mov     edi, [.ptrGrid]
        mov     ebx, [.BegCol]
        cmp     ebx, [edi+TDataGrid.ColCount]
        jae     .endloop

        mov     eax, [edi+TDataGrid.ColCount]
        cmp     [.EndCol], eax
        jl      .loop

        dec     eax
        mov     [.EndCol], eax

.loop:
        cmp     ebx, [.EndCol]
        ja      .endloop
        cmp     ebx, [edi+TDataGrid.ColCount]
        jae     .endloop

        stdcall TDataGrid.__SetRowColSize, edi, [edi+TDataGrid.ptrColX], [edi+TDataGrid.ColCount], ebx, [.Width]
        inc     ebx
        jmp     .loop
.endloop:
        pop     edi ebx
        return
endp



proc TDataGrid.SetRowHeight, .ptrGrid, .Row, .Height
begin
        mov     eax, [.ptrGrid]
        stdcall TDataGrid.__SetRowColSize, eax, [eax+TDataGrid.ptrRowY], [eax+TDataGrid.RowCount], [.Row], [.Height]
        return
endp


proc TDataGrid.SetRowsHeight, .ptrGrid, .BegRow, .EndRow, .Height
begin
        push    ebx edi

        mov     edi, [.ptrGrid]
        mov     ebx, [.BegRow]
        cmp     ebx, [edi+TDataGrid.RowCount]
        jae     .endloop

        mov     eax, [edi+TDataGrid.RowCount]
        cmp     [.EndRow], eax
        jl      .loop

        dec     eax
        mov     [.EndRow], eax

.loop:
        cmp     ebx, [.EndRow]
        ja      .endloop

        stdcall TDataGrid.__SetRowColSize, edi, [edi+TDataGrid.ptrRowY], [edi+TDataGrid.RowCount], ebx, [.Height]
        inc     ebx
        jmp     .loop

.endloop:
        pop     edi ebx
        return
endp



;-----------------------------------------------------------------------
; TDataGrid example event handlers.
;
;-----------------------------------------------------------------------

;-----------------------------------------------------------------------
; Simple example handler for TDataGrid.OnDrawCell
; It uses TDataGrid.OnGetString to obtain a string handler.
; After printing, the string will be deleted.
;-----------------------------------------------------------------------
proc OnDrawStringCell, .hdc, .ptrRect, .Col, .Row, .ptrData, .fSelected, .fFixed
begin
        push    esi ebx

; Choose text color
        mov     eax, [edi+TDataGrid.clTextFixed]
        cmp     [.fFixed], 0
        jne     .colorok

        mov     eax, [edi+TDataGrid.clTextSelected]
        cmp     [.fSelected], FALSE
        jne     .colorok

        mov     eax, [edi+TDataGrid.clTextCell]

.colorok:
        invoke  SetTextColor, [.hdc], eax

        stdcall [edi+TDataGrid.OnGetString], [.Col], [.Row], [.ptrData], [.fSelected], [.fFixed]
        test    eax, eax
        jz      .drawok

        mov     ebx, eax
        stdcall StrPtr, ebx
        invoke  DrawTextA, [.hdc], eax, -1, [.ptrRect], DT_CENTER or DT_VCENTER or DT_SINGLELINE or DT_NOPREFIX
        stdcall StrDel, ebx

.drawok:
        pop     ebx esi
        return
endp



;------------------------------------------------------------------------
; TDataGrid private functions.
; These functions are for internal use. Don't call them outside TDataGrid
; control.
;------------------------------------------------------------------------


; Returns eax - BegCell, edx - EndCell of the range that fits in the xMin..xMax coordinates.
; Returns ecx containinig the left coordinate of the BegCell.
; Binary search - note that the array is always sorted...
proc TDataGrid.__SearchCellRange, .xMin, .xMax, .ptrXArray, .Count
begin
        push    esi ebx
        mov     esi, [.ptrXArray]

        xor     eax, eax
        mov     edx, [.Count]
.minloop:
        mov     ecx, eax
        add     ecx, edx
        sar     ecx, 1
        cmp     ecx, eax
        je      .endminloop

        mov     ebx, [esi+4*ecx]
        cmp     [.xMin], ebx
        jl      .rightmin

        mov     eax, ecx
        jmp     .minloop

.rightmin:
        mov     edx, ecx
        jmp     .minloop

.endminloop:
        push    eax     ; left cell.

        xor     eax, eax
        mov     edx, [.Count]
.maxloop:
        mov     ecx, eax
        add     ecx, edx
        sar     ecx, 1
        cmp     ecx, eax
        je      .endmaxloop

        mov     ebx, [esi+4*ecx]
        cmp     [.xMax], ebx
        jl      .rightmax

        mov     eax, ecx
        jmp     .maxloop

.rightmax:
        mov     edx, ecx
        jmp     .maxloop

.endmaxloop:
        pop     eax     ; edx contains eax+1 -> right value.
        mov     ecx, [esi+4*eax]  ; absolute coordinate of the left cell.

        pop     ebx esi
        return
endp



;---------------------------------------------------------
; Auto adjusts the scrollbars
;---------------------------------------------------------
proc TDataGrid.__AdjustScrollbars, .ptrGrid
.client RECT
begin
        push    ebx

        mov     ebx, [.ptrGrid]

        lea     eax, [.client]
        invoke  GetClientRect, [ebx+TDataGrid.hwnd], eax

        stdcall TDataGrid.__AdjustOneScroller, [ebx+TDataGrid.hwnd], SB_VERT, [ebx+TDataGrid.ptrRowY], [ebx+TDataGrid.RowCount], [ebx+TDataGrid.FixedRows], [.client.bottom], [ebx+TDataGrid.RowHeight]
        stdcall TDataGrid.__AdjustOneScroller, [ebx+TDataGrid.hwnd], SB_HORZ, [ebx+TDataGrid.ptrColX], [ebx+TDataGrid.ColCount], [ebx+TDataGrid.FixedCols], [.client.right], [ebx+TDataGrid.ColWidth]

        invoke  InvalidateRect, [ebx+TDataGrid.hwnd], NULL, FALSE

.finish:
        pop     ebx
        return
endp


proc TDataGrid.__AdjustOneScroller, .hwnd, .kind, .ptrArray, .Count, .Fixed, .client, .meansize
.si    SCROLLINFO
begin
        push    esi

        invoke  GetScrollPos, [.hwnd], [.kind]
        mov     [.si.nPos], eax
        mov     [.si.nPage], -1

        mov     esi, [.ptrArray]
        test    esi, esi
        jz      .finish

        mov     [.si.cbSize], sizeof.SCROLLINFO
        mov     [.si.fMask], SIF_RANGE or SIF_PAGE or SIF_POS or SIF_TRACKPOS

        mov     ecx, [.Count]
        mov     edx, [.Fixed]
        mov     ecx, [esi+4*ecx]
        mov     edx, [esi+4*edx]
        mov     [.si.nMax], ecx
        mov     [.si.nMin], edx

        cmp     [.meansize], 0
        je      .skippg

        mov     eax, [.client]
        xor     edx, edx
        sub     eax, [.si.nMin]        ;remaining client area.
        jns     @f
        xor     eax, eax
@@:
        mov     [.si.nPage], eax

.skippg:
        lea     eax, [.si]
        invoke  SetScrollInfo, [.hwnd], [.kind], eax, TRUE

.finish:
        pop     esi
        return
endp



proc TDataGrid.__SetRowColSize, .ptrGrid, .ptrSizes, .Count, .x, .NewSize
begin
        push    edi
        mov     edi, [.ptrGrid]
        mov     eax, [.ptrSizes]
        mov     ecx, [.x]

        lea     eax, [eax+4*ecx+4]

        mov     edx, [.NewSize]
        sub     ecx, [.Count]
        jge     .finish         ; error, the index is greater than count.

        sub     edx, [eax]
        neg     ecx
        add     edx, [eax-4]

.loop:
        add     [eax], edx
        lea     eax, [eax+4]
        loop    .loop

        stdcall TDataGrid.__AdjustScrollbars, edi

.finish:
        pop     edi
        return
endp









