; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: TGrid object class
;
;  Target OS: Any
;
;  Dependencies:
;  Notes:
;_________________________________________________________________________________________
module "TGrid library"


;ObjectClass Grid,                   \
;            ScrollWindow,           \
;            TGrid.Create,           \
;            TGrid.Destroy,          \
;            TGrid.Get,              \
;            TGrid.Set,              \
;            TGrid.ExecCmd,          \
;            TGrid.SysEventHandler




object TGrid, TScrollWindow
  .hwnd      dd  ?      ; handle to the instance window.

; private fields

  ._captured dd ?

; properties
  .ColCount  dd ?
  .RowCount  dd ?
  .ColWidth  dd ?               ; default column width - every column can have different width though.
  .RowHeight dd ?               ; default row height - every row can have diferent height.

  .FixedCols dd ?               ; count of the fixed columns [0..FixedCols-1]
  .FixedRows dd ?               ; count of the fixed rows [0..FixedRows-1]
  .Flags     dd ?               ; grid flags ( dgf* flags)

  .ptrGridData dd ?             ; one pointer for the user.

  .focused   POINT              ; coordinates of the focused cell.

; These 2 arrays contains respectively ColCount+1 and RowCount+1 elements.
  .ptrColX dd ?                 ; pointer to array of dwords with X coordinates of the columns.
  .ptrRowY dd ?                 ; pointer to array of dwords with Y coordinates of the rows.

  .selection RECT               ; rectangle with current selection.
  .selflags  dd ?               ; 1 - column selection

; User events handlers.

  .OnGetPopupMenu dd ?          ; proc GetPopupMenu, .ptrGrid, .Col, .Row
  .OnDrawItem dd ?              ; proc DrawItem, .hdc, .ptrRect, .Col, .Row, .ptrData, .fSelected, .fFixed
  .OnGetString  dd ?            ; proc GetData, .Col, .Row, .ptrData, .flags, .fFixed

; Methods

endobj


gfGridlines = 1
gfGridLinesFixed = 2

htwXFixed = 1
htwXData  = 2
htwXBoth  = 3

htwYFixed = 1
htwYData  = 2
htwYBoth  = 3




proc TGrid.Create, .pobj
begin
        push    eax esi

        mov     esi, [.pobj]

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

; Default values for sizes.
        mov     [esi+TGrid.ColWidth], 64
        mov     [esi+TGrid.RowHeight], 16
        mov     [esi+TGrid.FixedCols], 1
        mov     [esi+TGrid.FixedRows], 1
        mov     [esi+TGrid.Flags], gfGridLinesFixed or gfGridlines
        mov     [esi+TGrid.focused.x], -1
        mov     [esi+TGrid.focused.y], -1
        mov     [esi+TGrid.selflags], -1

        stdcall TGrid.SetCounts, esi, 0, 0

        pop     esi eax
        return
endp



proc TGrid.Destroy, .pobj
begin

        return
endp




proc TGrid.Get, .pobj, .paramID
begin
        stc
        return
endp




proc TGrid.Set, .pobj, .paramID, .value
begin

        stc
        return
endp




proc TGrid.ExecCmd, .pobj, .method
begin
        cmp     [.method], TGrid._RedrawShadow
        je      .redraw

.continue:
        stc
        return

.redraw:
        pushad
        mov     esi, [.pobj]
        stdcall TGrid.__RedrawShadow
        stc
        popad
        return
endp



dproc TGrid.SysEventHandler, .pobj, .pEvent
begin
        pushad

        mov     esi, [.pobj]
        mov     ebx, [.pEvent]

        dispatch [ebx+TSysEvent.event]

.continue:
        stc
        popad
        return

;..................................................................................................................

oncase seResize
        stdcall Get, esi, TGrid.FreeArea
        stdcall TGrid.__AdjustScrollbars, esi
        jmp     .continue

;..................................................................................................................

oncase seScroll
        cmp     [ebx+TScrollEvent.ScrollCmd], scTrack
        je      .refresh

        lea     edi, [esi+TGrid._VScroller]

        mov     ecx, [ebx+TScrollEvent.Value]
        imul    ecx, [esi+TGrid.RowHeight]

        cmp     [ebx+TScrollEvent.ScrollCmd], scWheelUp
        jne     @f
        neg     ecx
@@:
        mov     eax, [edi+TScrollbar.Pos]
        add     eax, ecx
        jns     @f
        xor     eax, eax
@@:
        cmp     eax, [edi+TScrollbar.Max]
        jbe     @f
        mov     eax, [edi+TScrollbar.Max]
@@:
        mov     [edi+TScrollbar.Pos], eax

.refresh:
        mov     [esi+TGrid._fShadowReady], 0
        execute esi, TGrid.Refresh

        stc
        popad
        return

;..................................................................................................................


oncase seMouseMove

        cmp     [esi+TGrid._captured], 0
        je      .continue

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

        mov     eax, htwXData
        mov     ecx, htwYData

        cmp     edi, 1
        jne     @f
        mov     eax, htwXFixed
@@:
        cmp     edi, 2
        jne     @f
        mov     ecx, htwYFixed
@@:
        stdcall TGrid.HitTest3, esi, [ebx+TMouseMoveEvent.x], [ebx+TMouseMoveEvent.y], eax, ecx

        cmp     edi, 1
        jne     .maybecol

        xchg     [esi+TGrid.selection.bottom], edx
        mov     eax, [esi+TGrid.selection.left]
        jmp     .endmove

.maybecol:
        cmp     edi, 2
        jne     .cellselect

        xchg    [esi+TGrid.selection.right], eax
        mov     edx, [esi+TGrid.selection.top]
        jmp     .endmove

; select normal cells
.cellselect:
        xchg     [esi+TGrid.selection.right], eax
        xchg     [esi+TGrid.selection.bottom], edx

.endmove:
        cmp     eax, [esi+TGrid.selection.right]
        jne     .ensure
        cmp     edx, [esi+TGrid.selection.bottom]
        je      .end_mouse_move

.ensure:
        stdcall TGrid.EnsureVisible, esi, eax, edx

.invalidate:
        mov     [esi+TGrid._fShadowReady], 0
        execute esi, TGrid.Refresh


.end_mouse_move:
        stc
        popad
        return


;..................................................................................................................


oncase seMouseBtnPress

        cmp     [ebx+TMouseButtonEvent.Button], mbLeft
        jne     .continue

        mov     eax, [ebx+TMouseButtonEvent.x]
        mov     ecx, [ebx+TMouseButtonEvent.y]
        cmp     eax, [esi+TGrid._FreeArea.x]
        jb      .continue
        cmp     ecx, [esi+TGrid._FreeArea.y]
        jb      .continue

        sub     eax, [esi+TGrid._FreeArea.x]
        sub     ecx, [esi+TGrid._FreeArea.y]

        cmp     eax, [esi+TGrid._FreeArea.width]
        jae     .continue
        cmp     ecx, [esi+TGrid._FreeArea.height]
        jae     .continue

        stdcall _SetFocus, [esi+TGrid.handle]
        stdcall MouseCapture, [esi+TGrid.handle]
        mov     [esi+TGrid._captured], 1

        stdcall TGrid.HitTest3, esi, [ebx+TMouseButtonEvent.x], [ebx+TMouseButtonEvent.y], htwXBoth, htwYBoth

        mov     [esi+TGrid.selflags], 0
        mov     [esi+TGrid.selection.left], eax
        mov     [esi+TGrid.selection.top], edx
        mov     [esi+TGrid.selection.right], eax
        mov     [esi+TGrid.selection.bottom], edx

        cmp     eax, [esi+TGrid.FixedCols]
        jge     .notleft

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

.notleft:
        cmp     edx, [esi+TGrid.FixedRows]
        jge     .nottop

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

.nottop:
        cmp     [esi+TGrid.selflags], 0
        jne     .invalidate

        mov     [esi+TGrid.focused.x], eax
        mov     [esi+TGrid.focused.y], edx
        jmp     .invalidate


;..................................................................................................................


oncase seMouseBtnRelease

        cmp     [esi+TGrid._captured], 0
        je      .continue

        stdcall MouseCapture, 0
        mov     [esi+TGrid._captured], 0

        mov     [esi+TGrid.selflags], -1
        lea     edi, [esi+TGrid.selection]
        stdcall RectSort, edi
        jmp     .invalidate

enddp




;;----------------------------------------------------
;; Window procedure of the TGrid control.
;;----------------------------------------------------
;winproc TGrid.DataGridWinProc
;begin
;        push    esi edi ebx
;
;        invoke  GetPropA, [.hwnd], [propCtrlData]
;        mov     edi, eax
;
;        mov     ebx, [.wmsg]
;
;ondefault
;       invoke   DefWindowProcA, [.hwnd], [.wmsg], [.wparam], [.lparam]
;
;.finish:
;       pop      ebx edi esi
;       return
;
;
;onmessage WM_SETFONT
;        invoke  SelectObject, [edi+TGrid.hShadowDC], [.wparam]
;        jmp     .finish
;
;
;onmessage WM_CREATE
;; TGrid structure.
;        mov     eax, [.lparam]
;        mov     esi, [eax+CREATESTRUCT.lpCreateParams]
;        invoke  IsBadWritePtr, esi, sizeof.TGrid
;        test    eax, eax
;        jz      .pointerOK
;
;        or      eax, -1
;        jmp     .finish
;
;.pointerOK:
;        invoke  SetPropA, [.hwnd], [propCtrlData], esi
;
;        invoke  CreateCompatibleDC, NULL
;        mov     [esi+TGrid.hShadowDC], eax
;
;        stdcall GetMem, 4
;        mov     [esi+TGrid.ptrColX], eax
;        stdcall GetMem, 4
;        mov     [esi+TGrid.ptrRowY], eax
;
;        mov     eax, [.hwnd]
;        mov     [esi+TGrid.hwnd], eax
;
;; Default values for sizes.
;        mov     [esi+TGrid.ColWidth], 64
;        mov     [esi+TGrid.RowHeight], 16
;        mov     [esi+TGrid.FixedCols], 1
;        mov     [esi+TGrid.FixedRows], 1
;        mov     [esi+TGrid.Flags], dgfGridLinesFixed or dgfGridlines
;        mov     [esi+TGrid.focused.x], 2
;        mov     [esi+TGrid.focused.y], 2
;        mov     [esi+TGrid.selflags], -1
;
;        invoke  GetSysColor, COLOR_BTNFACE
;        mov     [esi+TGrid.clFixed], eax
;        invoke  GetSysColor, COLOR_WINDOW
;        mov     [esi+TGrid.clCell], eax
;        invoke  GetSysColor, COLOR_HIGHLIGHT
;        mov     [esi+TGrid.clSelected], eax
;        invoke  GetSysColor, COLOR_WINDOWTEXT
;        mov     [esi+TGrid.clTextCell], eax
;        invoke  GetSysColor, COLOR_BTNTEXT
;        mov     [esi+TGrid.clTextFixed], eax
;        invoke  GetSysColor, COLOR_HIGHLIGHTTEXT
;        mov     [esi+TGrid.clTextSelected], eax
;        invoke  GetSysColor, COLOR_HOTLIGHT
;        mov     [esi+TGrid.clMarked], eax
;
;        stdcall TGrid.SetCounts, esi, 0, 0
;
;        xor     eax, eax
;        jmp     .finish
;
;onmessage 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+TGrid.hShadow], eax
;        invoke  SelectObject, [edi+TGrid.hShadowDC], eax
;        invoke  DeleteObject, eax
;        invoke  ReleaseDC, [.hwnd], ebx
;
;; invalidate shadow buffer.
;        mov     [edi+TGrid.fShadowReady], 0
;
;        stdcall TGrid.__AdjustScrollbars, edi
;        xor     eax, eax
;        jmp     .finish
;
;onmessage WM_DESTROY
;        invoke  DeleteDC, [edi+TGrid.hShadowDC]
;        invoke  DeleteObject, [edi+TGrid.hSelected]
;
;        stdcall FreeMem, [edi+TGrid.ptrColX]
;        stdcall FreeMem, [edi+TGrid.ptrRowY]
;        mov     [edi+TGrid.hwnd], 0
;
;        xor     eax, eax
;        jmp     .finish
;
;onmessage WM_MOUSEWHEEL
;        movsx   eax, word [.wparam+2]
;        cdq
;        mov     ecx, 120
;        idiv    ecx
;        imul    eax, [edi+TGrid.RowHeight]
;        mov     ebx, eax
;        invoke  GetScrollPos, [.hwnd], SB_VERT
;        sub     eax, ebx
;        invoke  SetScrollPos, [.hwnd], SB_VERT, eax, TRUE
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke  InvalidateRect, [.hwnd], NULL, FALSE
;        xor     eax, eax
;        jmp     .finish
;
;
;onmessage WM_PAINT
;locals
;  .ps PAINTSTRUCT
;endl
;        lea     esi, [.ps]
;        invoke  BeginPaint, [.hwnd], esi
;        test    eax, eax
;        jz      .finish
;
;        cmp     [edi+TGrid.fShadowReady], 0
;        jne     .shadowok
;
;        lea     eax, [esi+PAINTSTRUCT.rcPaint]
;        stdcall TGrid.RedrawShadow, eax
;        inc     [edi+TGrid.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+TGrid.hShadowDC],              \
;                        [esi+PAINTSTRUCT.rcPaint.left],         \
;                        [esi+PAINTSTRUCT.rcPaint.top],          \
;                        SRCCOPY
;
;        invoke  EndPaint, [.hwnd], esi
;        xor     eax, eax
;        jmp     .finish
;
;
;onmessage DGM_SETPARAM
;        cmp     [.wparam], sizeof.TGrid - 4
;        jg      .finish
;
;        mov     eax, [.wparam]
;        pushd    [.lparam]
;        popd     [edi+eax]
;
;        stdcall  TGrid.__AdjustScrollbars, edi
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke   InvalidateRect, [.hwnd], NULL, FALSE
;        jmp      .finish
;
;onmessage DGM_GETPARAM
;        cmp     [.wparam], sizeof.TGrid - 4
;        jg      .finish
;
;        mov     eax, [.wparam]
;        mov     eax, [edi+eax]
;        jmp      .finish
;
;onmessage WM_HSCROLL
;        mov      eax, [edi+TGrid.ColWidth]
;        jmp      .oncommonscroll
;onmessage WM_VSCROLL
;        mov      eax, [edi+TGrid.RowHeight]
;
;.oncommonscroll:
;        stdcall  ScrollBarMessage, [.hwnd], [.wmsg], [.wparam], NULL, eax
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke   InvalidateRect, [.hwnd], NULL, FALSE
;        jmp      .finish
;
;
;
;onmessage WM_GETDLGCODE
;        mov     eax, DLGC_WANTARROWS or DLGC_WANTCHARS
;        jmp     .finish
;
;
;onmessage 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+TGrid.focused.y]
;        dec     eax
;        cmp     eax, [edi+TGrid.FixedRows]
;        jb      .endkeydown
;        mov     [edi+TGrid.focused.y], eax
;        jmp     .ensurevisible
;
;.down:
;        mov     eax, [edi+TGrid.focused.y]
;        inc     eax
;        cmp     eax, [edi+TGrid.RowCount]
;        jae     .endkeydown
;        mov     [edi+TGrid.focused.y], eax
;        jmp     .ensurevisible
;
;.left:
;        mov     eax, [edi+TGrid.focused.x]
;        dec     eax
;        cmp     eax, [edi+TGrid.FixedCols]
;        jl      .endkeydown
;        mov     [edi+TGrid.focused.x], eax
;        jmp     .ensurevisible
;
;.right:
;        mov     eax, [edi+TGrid.focused.x]
;        inc     eax
;        cmp     eax, [edi+TGrid.ColCount]
;        jae     .endkeydown
;        mov     [edi+TGrid.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+TGrid.RowHeight]
;        imul    eax, ebx
;        add     eax, [edi+TGrid.focused.y]
;
;        cmp     eax, [edi+TGrid.FixedRows]
;        jge     @f
;        mov     eax, [edi+TGrid.FixedRows]
;@@:
;        cmp     eax, [edi+TGrid.RowCount]
;        jl      @f
;        mov     eax, [edi+TGrid.RowCount]
;        dec     eax
;@@:
;        mov     [edi+TGrid.focused.y], eax
;        jmp     .ensurevisible
;
;.home:
;        mov     eax, [edi+TGrid.FixedCols]
;        mov     ecx, [edi+TGrid.FixedRows]
;        jmp     .setfocused
;
;.end:
;        mov     eax, [edi+TGrid.ColCount]
;        mov     ecx, [edi+TGrid.RowCount]
;        dec     eax
;        dec     ecx
;
;.setfocused:
;        mov     [edi+TGrid.focused.x], eax
;        mov     [edi+TGrid.focused.y], ecx
;        jmp     .ensurevisible
;
;
;.ensurevisible:
;; Clear existing selection
;        invoke  DeleteObject, [edi+TGrid.hSelected]
;        mov     [edi+TGrid.hSelected], 0
;
;        stdcall TGrid.EnsureVisible, edi, [edi+TGrid.focused.x], [edi+TGrid.focused.y]
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke  InvalidateRect, [.hwnd], 0, FALSE
;
;.endkeydown:
;        xor     eax, eax
;        jmp     .finish
;
;
;onmessage WM_LBUTTONDOWN
;        invoke  SetFocus, [.hwnd]
;        invoke  SetCapture, [.hwnd]
;
;        movsx   eax, word [.lparam]
;        movsx   ecx, word [.lparam+2]
;        stdcall TGrid.HitTest3, edi, eax, ecx, htwXBoth, htwYBoth
;
;        mov     [edi+TGrid.selflags], 0
;        mov     [edi+TGrid.selection.left], eax
;        mov     [edi+TGrid.selection.top], edx
;        mov     [edi+TGrid.selection.right], eax
;        mov     [edi+TGrid.selection.bottom], edx
;
;        cmp     eax, [edi+TGrid.FixedCols]
;        jge     .notleft
;
;; if the cursor is on the left fixed cells
;        or      [edi+TGrid.selflags], 1
;        mov     ebx, [edi+TGrid.FixedCols]  ; xbeg
;        mov     ecx, [edi+TGrid.ColCount]   ; xend
;        mov     [edi+TGrid.selection.left], ebx
;        mov     [edi+TGrid.selection.right], ecx
;
;.notleft:
;        cmp     edx, [edi+TGrid.FixedRows]
;        jge     .nottop
;
;; if the cursor is on the top fixed cells
;        or      [edi+TGrid.selflags], 2
;        mov     ebx, [edi+TGrid.FixedRows]  ; ybeg
;        mov     ecx, [edi+TGrid.RowCount]   ; yend
;        mov     [edi+TGrid.selection.top], ebx
;        mov     [edi+TGrid.selection.bottom], ecx
;
;.nottop:
;        cmp     [edi+TGrid.selflags], 0
;        jne     .clearcurrent
;
;        mov     [edi+TGrid.focused.x], eax
;        mov     [edi+TGrid.focused.y], edx
;
;.clearcurrent:
;        test    [.wparam], MK_CONTROL
;        jnz     .invalidate
;
;        invoke  DeleteObject, [edi+TGrid.hSelected]
;        mov     [edi+TGrid.hSelected], 0
;
;.invalidate:
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke  InvalidateRect, [.hwnd], 0, FALSE
;        xor     eax, eax
;        jmp     .finish
;
;
;onmessage WM_MOUSEMOVE
;        invoke  GetCapture
;        cmp     eax, [.hwnd]
;        jne     .ondefault       ; temporary...
;
;        mov     esi, [edi+TGrid.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 TGrid.HitTest3, edi, ecx, edx, eax, ebx
;
;        cmp     esi, 1
;        jne     .maybecol
;
;        mov     [edi+TGrid.selection.bottom], edx
;        mov     eax, [edi+TGrid.selection.left]
;        jmp     .endmove
;
;.maybecol:
;        cmp     esi, 2
;        jne     .cellselect
;
;        mov     [edi+TGrid.selection.right], eax
;        mov     edx, [edi+TGrid.selection.top]
;        jmp     .endmove
;
;; select normal cells
;.cellselect:
;        mov     [edi+TGrid.selection.right], eax
;        mov     [edi+TGrid.selection.bottom], edx
;
;.endmove:
;        stdcall TGrid.EnsureVisible, edi, eax, edx
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke  InvalidateRect, [.hwnd], 0, FALSE
;
;        xor     eax, eax
;        jmp     .finish
;
;
;onmessage WM_LBUTTONUP
;        invoke  GetCapture
;        cmp     eax, [.hwnd]
;        jne     .ondefault
;
;        invoke  SetCapture, NULL
;
;        mov     [edi+TGrid.selflags], -1
;
;        lea     esi, [edi+TGrid.selection]
;        call    TGrid.SortRect
;
;        stdcall TGrid.SelectRect, edi, [edi+TGrid.selection.left],   \
;                                           [edi+TGrid.selection.top],    \
;                                           [edi+TGrid.selection.right],  \
;                                           [edi+TGrid.selection.bottom], \
;                                           soMerge
;
;        mov     [edi+TGrid.fShadowReady], 0
;        invoke  InvalidateRect, [.hwnd], 0, FALSE
;
;        xor     eax, eax
;        jmp     .finish
;endwp
;
;
;



;-----------------------------------------------------------------------
; TGrid public functions.
;
;-----------------------------------------------------------------------
proc TGrid.__RedrawShadow
.rcFixedTL RECT
.rcFixedT  RECT
.rcFixedL  RECT
.rcFloat   RECT
.rcClient  RECT
.context   dd ?
.ptrRect   dd ?
begin

        lea     eax, [.rcClient]
        mov     [.ptrRect], eax

        mov     eax, [esi+TGrid._pShadow]
        stdcall AllocateContext, [eax+TBackBuffer.raster]
        mov     [.context], eax

        mov     eax, [esi+TGrid._FreeArea.width]
        mov     ecx, [esi+TGrid._FreeArea.height]

        xor     edx, edx

        mov     [.rcClient.left], edx
        mov     [.rcClient.top], edx
        mov     [.rcClient.right], eax
        mov     [.rcClient.bottom], ecx

        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, [esi+TGrid.FixedCols]
        mov     eax, [esi+TGrid.ptrColX]
        test    eax, eax
        jnz     .colsizeok

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

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

        imul    edx, [esi+TGrid.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]
        stdcall RectIntersect, eax, eax, [.ptrRect]
        jc      .drawFixedT

        stdcall TGrid.__DrawCellArea, [.context], eax, ffHorizontal or ffVertical

.drawFixedT:
        lea     eax, [.rcFixedT]
        stdcall RectIntersect, eax, eax, [.ptrRect]
        jc      .drawFixedL

        stdcall TGrid.__DrawCellArea, [.context], eax, ffVertical

.drawFixedL:
        lea     eax, [.rcFixedL]
        stdcall RectIntersect, eax, eax, [.ptrRect]
        jc      .drawFloat

        stdcall TGrid.__DrawCellArea, [.context], eax, ffHorizontal

.drawFloat:
        lea     eax, [.rcFloat]
        stdcall RectIntersect, eax, eax, [.ptrRect]
        jc      .finish

        lea     eax, [.rcFloat]
        stdcall TGrid.__DrawCellArea, [.context], eax, 0

.finish:
        stdcall ReleaseContext, [.context]
        return
endp


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

proc TGrid.__DrawCellArea, .context, .ptrClipRect, .fFixed
  .Cell  RECT
  .xofs  dd ?
  .yofs  dd ?
  .clip  RECT
  .flags dd ?
  .sel   RECT

  .bnd   TBounds
begin
        push    ebx edi

        lea     edi, [.sel]
        lea     eax, [esi+TGrid.selection]
        stdcall RectCopy, edi, eax
        stdcall RectSort, edi
        inc     [.sel.right]
        inc     [.sel.bottom]

;        OutputValue "Selection Left:", [.sel.left], 10, 0
;        OutputValue "           Top:", [.sel.top], 10, 0
;        OutputValue "         Right:", [.sel.right], 10, 0
;        OutputValue "        Bottom:", [.sel.bottom], 10, 0

        mov     eax, [esi+TGrid.FixedRows]
        mov     ecx, [esi+TGrid.FixedCols]

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

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

; Set the cliping region for the shadow DC.

        lea     eax, [.bnd]
        stdcall RectToBounds, eax, [.ptrClipRect]
        stdcall SetClipRectangle, [.context], eax

; erase the background
        mov     eax, [clGridCellBk]
        cmp     [.fFixed], 0
        je      .bgok
        mov     eax, [clGridFixedBk]
.bgok:

        mov     edi, [.ptrClipRect]

        mov     ecx, [edi+RECT.right]
        mov     edx, [edi+RECT.bottom]
        sub     ecx, [edi+RECT.left]
        sub     edx, [edi+RECT.top]
        stdcall DrawFillRect, [.context], [edi+RECT.left], [edi+RECT.top], ecx, edx, eax

; If the cells are not fixed, compute the real coordinates of the rectangle.
        xor     eax, eax
        mov     [.xofs], eax
        mov     [.yofs], eax

        test    [.fFixed], ffHorizontal
        jnz     .horizOK

        mov     eax, [esi+TGrid._HScroller.Pos]

        mov     [.xofs], eax
        add     [edi+RECT.left], eax
        add     [edi+RECT.right], eax

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

        mov     eax, [esi+TGrid._VScroller.Pos]

        mov     [.yofs], eax
        add     [edi+RECT.top], eax
        add     [edi+RECT.bottom], eax

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

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

        mov     ebx, [.Cell.top]

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

        mov     eax, [esi+TGrid.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     edi, [.Cell.left]

.xloop:
        cmp     edi, [.Cell.right]
        jae     .endxloop

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

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

        mov     [.flags], 0

; draw the background if the cell is selected
        lea     eax, [.sel]
        stdcall PointInRect, eax, edi, ebx
        jnc     .bkgroundok

        mov     eax, [.clip.right]
        mov     ecx, [.clip.bottom]
        sub     eax, [.clip.left]
        sub     ecx, [.clip.top]
        stdcall DrawFillRect, [.context], [.clip.left], [.clip.top], eax, ecx, [clGridSelBk]
        mov     [.flags], 1

.bkgroundok:
        cmp     [esi+TGrid.OnDrawItem], 0
        je      .drawgridlines

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


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

        test    [esi+TGrid.Flags], gfGridLinesFixed
        jz      .gridok

        mov     eax, [.clip.left]
        mov     ecx, [.clip.top]
        mov     [.bnd.x], eax
        mov     [.bnd.y], ecx
        mov     eax, [.clip.right]
        mov     ecx, [.clip.bottom]
        sub     eax, [.clip.left]
        sub     ecx, [.clip.top]
        mov     [.bnd.width], eax
        mov     [.bnd.height], ecx

        lea     eax, [.bnd]
        stdcall DrawBoxDefault, [.context], eax, [clGridFixedBk], bxRaised or bxNoFill
        jmp     .gridok


.datagridline:
        test    [esi+TGrid.Flags], gfGridlines
        jz      .gridok

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

        stdcall SetSimpleLine, [.context], [clGridLines]
        stdcall DrawLine, [.context], [.clip.left], eax, ecx, eax
        stdcall DrawLine, [.context], ecx, eax, ecx, edx

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

        sub     [.clip.right], 2
        sub     [.clip.bottom], 2

; focus rectangle. TODO: dotted line

iglobal
  FocusRectLineStyle LineStyle 1, 0, 0, $ff0000, 3, 3
endg

        mov     eax, [clGridLines]
        mov     [FocusRectLineStyle+TLineStyle.color], eax
        mov     eax, [clGridCellBk]
        mov     [FocusRectLineStyle+TLineStyle.clSpace], eax

        stdcall SetLineStyle, [.context], FocusRectLineStyle

        stdcall DrawLine, [.context], [.clip.left], [.clip.top], [.clip.right], [.clip.top]
        stdcall DrawLine, [.context], [.clip.right], [.clip.top], [.clip.right], [.clip.bottom]
        stdcall DrawLine, [.context], [.clip.right], [.clip.bottom], [.clip.left], [.clip.bottom]
        stdcall DrawLine, [.context], [.clip.left], [.clip.bottom], [.clip.left], [.clip.top]

        add     [.clip.right], 2
        add     [.clip.bottom], 2

.endcell:
        inc     edi
        jmp     .xloop

.endxloop:
        inc     ebx
        jmp     .yloop

.endyloop:

        pop     edi 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 TGrid.EnsureVisible, .ptrGrid, .Col, .Row
.client RECT
begin
        push    ebx edi
        mov     edi, [.ptrGrid]

        mov     eax, [edi+TGrid._FreeArea.x]
        mov     ecx, [edi+TGrid._FreeArea.y]
        mov     [.client.left], eax
        mov     [.client.top], ecx
        add     eax, [edi+TGrid._FreeArea.width]
        add     ecx, [edi+TGrid._FreeArea.height]
        mov     [.client.right], eax
        mov     [.client.bottom], ecx

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

        mov     eax, [.client.right]
        mov     ecx, [edi+TGrid._HScroller.Pos]
        sub     eax, 0          ; ??? hscroll.min
        add     eax, ecx
        mov     [.client.left], ecx
        mov     [.client.right], eax

        mov     edx, [edi+TGrid.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     [edi+TGrid._HScroller.Pos], ecx
        jmp     .posvertical

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

        mov     [edi+TGrid._HScroller.Pos], ebx

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

        mov     eax, [.client.bottom]
        mov     ecx, [edi+TGrid._VScroller.Pos]
        sub     eax, 0  ; ??? vScroller.Min
        add     eax, ecx
        mov     [.client.top], ecx
        mov     [.client.bottom], eax

        mov     edx, [edi+TGrid.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     [edi+TGrid._VScroller.Pos], ecx
        jmp     .endpos

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

        mov     [edi+TGrid._VScroller.Pos], ebx

.endpos:
        pop     edi ebx
        return
endp



;-----------------------------------------------------------------------
; Returns the cell coordinates of the cell, where pixel [.x,.y] is located.
; eax : Column
; edx : Row
;--------------------------------------------------------------------------

proc TGrid.HitTest3, .ptrGrid, .x, .y, .fWhereX, .fWhereY
begin
        push    edi esi ebx

        mov     edi, [.ptrGrid]

        mov     ecx, [edi+TGrid._HScroller.Pos]
        xor     eax, eax

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

        cmp     [.x], eax
        jl      .xfixed

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

.xfixed:
        xor     eax, eax
        mov     ecx, [edi+TGrid._VScroller.Pos]

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

        cmp     [.y], eax
        jl      .yfixed

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

.yfixed:
        stdcall TGrid.__SearchCellRange, [.x], [.x], [edi+TGrid.ptrColX], [edi+TGrid.ColCount]
        push    eax
        stdcall TGrid.__SearchCellRange, [.y], [.y], [edi+TGrid.ptrRowY], [edi+TGrid.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 TGrid.SelectRect, .ptrGrid, .BegCol, .BegRow, .EndCol, .EndRow, .operation
;begin
;        push    edi ebx
;
;        mov     edi, [.ptrGrid]
;
;        mov     eax, [edi+TGrid.FixedRows]
;        mov     ecx, [edi+TGrid.FixedCols]
;
;; [.BegRow] := max([.BegRow], [edi+TGrid.FixedRows])
;        sub     eax, [.BegRow]
;        sbb     edx, edx
;        not     edx
;        and     edx, eax
;        add     [.BegRow], edx
;
;; [.BegCol] := max([.BegCol], [edi+TGrid.FixedCols])
;        sub    ecx, [.BegCol]
;        sbb    edx, edx
;        not    edx
;        and    edx, ecx
;        add    [.BegCol], edx
;
;        mov     eax, [edi+TGrid.RowCount]
;        mov     ecx, [edi+TGrid.ColCount]
;        cmp     [.BegRow], eax
;        jae     .finish
;        cmp     [.BegCol], ecx
;        jae     .finish
;
;        dec     eax
;        dec     ecx
;
;; [.EndRow] := min([.EndRow], [edi+TGrid.RowCount])
;        sub     eax, [.EndRow]
;        sbb     edx, edx
;        and     edx, eax
;        add     [.EndRow], edx
;
;; [.EndCol] := min([.EndCol], [edi+TGrid.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+TGrid.hSelected], 0
;        je      .newselection
;
;        invoke  CombineRgn, [edi+TGrid.hSelected],  \
;                            ebx,                        \
;                            [edi+TGrid.hSelected],  \
;                            [.operation]
;
;        cmp     eax, NULLREGION
;        jne     .free
;
;        invoke  DeleteObject, [edi+TGrid.hSelected]
;        mov     [edi+TGrid.hSelected], 0
;
;.free:
;        invoke  DeleteObject, ebx
;.finish:
;        pop     ebx edi
;        return
;
;.newselection:
;        mov     [edi+TGrid.hSelected], ebx
;        pop     ebx edi
;        return
;endp



proc TGrid.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+TGrid.ptrColX]
        add     edx, [esi+TGrid.ptrRowY]
        mov     eax, [eax]
        mov     edx, [edx]

        pop     esi
        return
endp



proc TGrid.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+TGrid.ptrColX]
        mov     [.ptrNewColX], eax

        mov     eax, [.ColCount]
        cmp     eax, [ebx+TGrid.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+TGrid.ptrColX], eax
        mov     [.ptrNewColX], eax
        test    eax, eax
        jz      .finish

        mov     edi, eax

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

        mov     eax, [edi+4*ecx]

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

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

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

        mov     eax, [.RowCount]
        cmp     eax, [ebx+TGrid.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+TGrid.ptrRowY], eax
        mov     [.ptrNewRowY], eax
        test    eax, eax
        jz      .finish

        mov     edi, eax

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

        mov     eax, [edi+4*ecx]

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

        add     eax, [ebx+TGrid.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+TGrid.RowCount], ecx
        mov     [ebx+TGrid.ColCount], edx

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

        stdcall TGrid.__AdjustScrollbars, [.ptrGrid]

        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 TGrid.SetColWidth, .ptrGrid, .Col, .Width
begin
        mov     eax, [.ptrGrid]
        stdcall TGrid.__SetRowColSize, eax, [eax+TGrid.ptrColX], [eax+TGrid.ColCount], [.Col], [.Width]
        return
endp


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

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

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

        dec     eax
        mov     [.EndCol], eax

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

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



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





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

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

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

        dec     eax
        mov     [.EndRow], eax

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

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

.endloop:
        pop     edi ebx
        return
endp



;-----------------------------------------------------------------------
; TGrid example event handlers.
;
;-----------------------------------------------------------------------

;-----------------------------------------------------------------------
; Simple example handler for TGrid.OnDrawItem
; It uses TGrid.OnGetString to obtain a string handler.
; After printing, the string will be deleted.
;-----------------------------------------------------------------------
proc OnDrawStringCell, .context, .ptrRect, .Col, .Row, .ptrData, .fSelected, .fFixed
.color dd ?
.offsy dd ?
.x     dd ?
.y     dd ?
begin
        push    edi esi ebx ecx

; Choose text color
        mov     eax, [clGridFixedText]
        cmp     [.fFixed], 0
        jne     .colorok

        mov     eax, [clGridSelText]
        cmp     [.fSelected], FALSE
        jne     .colorok

        mov     eax, [clGridCellText]

.colorok:
        mov     [.color], eax

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

        mov     ebx, eax

        mov     edi, [.ptrRect]

        mov     eax, [edi+RECT.right]
        mov     ecx, [edi+RECT.bottom]
        sub     ecx, [edi+RECT.top]
        mov     [.x], eax
        mov     [.y], ecx

        stdcall StrLen, ebx
        mov     ecx, eax
        stdcall StrPtr, ebx
        stdcall GetTextBounds, [.context], eax, ecx, 0

        sub     [.x], eax
        sub     [.x], 4

        stdcall StrLen, ebx
        mov     ecx, eax
        stdcall StrPtr, ebx
        stdcall GetTextOffset, [.context], eax, ecx, 0
        add     [.x], eax

        mov     eax, [.y]
        sub     eax, edx
        sar     eax, 1
        add     eax, [edi+RECT.top]
        add     eax, edx
        mov     [.y], eax

        stdcall StrLen, ebx
        mov     ecx, eax
        stdcall StrPtr, ebx
        stdcall DrawString, [.context], eax, ecx, [.x], [.y], 0, [.color]

        stdcall StrDel, ebx

.drawok:
;        mov     edx, [.ptrRect]
;        stdcall SetSimpleLine, [.context], $ff0000
;        stdcall DrawLine, [.context], [edx+RECT.left], [edx+RECT.top], [edx+RECT.right], [edx+RECT.bottom]

        pop     ecx ebx esi edi
        return
endp



;------------------------------------------------------------------------
; TGrid private functions.
; These functions are for internal use. Don't call them outside TGrid
; 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 TGrid.__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
;---------------------------------------------------------

sbVert = 1
sbHorz = 0


proc TGrid.__AdjustScrollbars, .ptrGrid
begin
        push    ebx

        mov     ebx, [.ptrGrid]
        stdcall Get, ebx, TGrid.FreeArea
        stdcall TGrid.__AdjustOneScroller, ebx, sbVert, [ebx+TGrid.ptrRowY], [ebx+TGrid.RowCount], [ebx+TGrid.FixedRows], [ebx+TWindow._FreeArea.height], [ebx+TGrid.RowHeight]
        stdcall TGrid.__AdjustOneScroller, ebx, sbHorz, [ebx+TGrid.ptrColX], [ebx+TGrid.ColCount], [ebx+TGrid.FixedCols], [ebx+TWindow._FreeArea.width], [ebx+TGrid.ColWidth]

;        execute ebx, TGrid.Refresh

.finish:
        pop     ebx
        return
endp


proc TGrid.__AdjustOneScroller, .pobj, .kind, .ptrArray, .Count, .Fixed, .client, .meansize
begin
        pushad

        mov     ebx, [.pobj]

        mov     edi, [.kind]
        shl     edi, 4          ; sizeof.TScrollbar = $10

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

        mov     ecx, [.Count]
        mov     edx, [.Fixed]

        mov     ecx, [esi+4*ecx]        ; total height of all cells
        mov     edx, [esi+4*edx]        ; height of the fixed cells

        sub     ecx, [.client]
        jns     @f
        xor     ecx, ecx
@@:

        mov     eax, [.client]          ; scroller page size
        sub     edx, eax
        neg     edx
        jns     @f
        xor     edx, edx
@@:
        mov     eax, [ebx+TScrollWindow._HScroller.Pos+edi]
        cmp     eax, ecx
        jb      @f
        mov     eax, ecx
@@:
        mov     [ebx+TScrollWindow._HScroller.Pos+edi], eax
        mov     [ebx+TScrollWindow._HScroller.Max+edi], ecx
        mov     [ebx+TScrollWindow._HScroller.Page+edi], edx

.finish:
        popad
        return
endp



proc TGrid.__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 TGrid.__AdjustScrollbars, edi

.finish:
        pop     edi
        return
endp




endmodule



