; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "Fresh artistic license."         |
;|_______________________________________________________________________________________|
;
;  Description: TScrollWindow object class
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: TScrollWindow is window that may have scrollers to scroll the client area.
;_________________________________________________________________________________________
module "TScrollWindow library"

ObjectClass ScrollWindow,               \
            Window,                     \
            TScrollWindow.Create,       \
            0,                          \
            TScrollWindow.Get,          \
            TScrollWindow.Set,          \
            TScrollWindow.ExecCmd,      \
            TScrollWindow.SysEventHandler


struct TScrollbar
  .Max  dd  ?
  .Page dd  ?
  .Pos  dd  ?

  ._Drag dd ?
ends



object TScrollWindow, TWindow
  ._HScroller TScrollbar
  ._VScroller TScrollbar

  method .HScrollSet, .Pos, .Max, .Page   ; only params >=0 are set. If<0 ignored.
  method .VScrollSet, .Pos, .Max, .Page
endobj


ScrollBarWidth = 12
MinScrollBarWidth = 12

;_________________________________________________________________________________________

proc TScrollWindow.Create, .pobj
begin
;        push    eax
;        mov     eax, [.pobj]
;
;        mov     [eax+TScrollWindow._HScroller.Max], 255
;        mov     [eax+TScrollWindow._HScroller.Page], 1
;        mov     [eax+TScrollWindow._HScroller.Pos], 10
;
;        mov     [eax+TScrollWindow._VScroller.Max], 1000
;        mov     [eax+TScrollWindow._VScroller.Page], 1
;        mov     [eax+TScrollWindow._VScroller.Pos], 450
;
;        pop     eax
        return
endp



;_________________________________________________________________________________________


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


;_________________________________________________________________________________________


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

        stc
        return
endp


;_________________________________________________________________________________________


proc TScrollWindow.SysEventHandler, .pobj, .pEvent
  .event TScrollEvent
begin
        push    eax ebx edx esi edi
        mov     esi, [.pobj]
        mov     ecx, [.pEvent]

        cmp     [ecx+TSysEvent.event], sePaint
        je      .paint

        cmp     [ecx+TSysEvent.event], seMouseBtnPress
        je      .btnpress

        cmp     [ecx+TSysEvent.event], seMouseBtnRelease
        je      .btnrelease

        cmp     [ecx+TSysEvent.event], seMouseMove
        je      .mousemove

.continue:
        stc
.finish:
        pop     edi esi edx ebx eax
        return


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

.paint:
locals
  .recth TBounds
  .rectv TBounds
  .rectx TBounds
endl
        stdcall Get, esi, TScrollWindow.FreeArea
        mov     ebx, eax

        mov     eax, [ebx+TBounds.x]
        mov     edx, [ebx+TBounds.y]
        mov     [.recth.x], eax
        mov     [.rectv.y], edx

        add     eax, [ebx+TBounds.width]
        add     edx, [ebx+TBounds.height]
        mov     [.rectv.x], eax
        mov     [.recth.y], edx

        inc     edx
        inc     eax
        mov     [.rectx.y], edx
        mov     [.rectx.x], eax

        mov     [.recth.height], ScrollBarWidth
        mov     [.rectv.width], ScrollBarWidth
        mov     [.rectx.width], ScrollBarWidth-1
        mov     [.rectx.height], ScrollBarWidth-1

        mov     eax, [ebx+TBounds.width]
        mov     edx, [ebx+TBounds.height]
        inc     eax
        inc     edx
        mov     [.recth.width], eax
        mov     [.rectv.height], edx

        lea     eax, [.rectx]
        stdcall [DrawBox], [ecx+TPaintEvent.context], eax, [clDialogBk], bxNone

        lea     eax, [.recth]
        stdcall [DrawBox], [ecx+TPaintEvent.context], eax, [clScrollBk], bxSunken or bxNoFill

        lea     eax, [.rectv]
        stdcall [DrawBox], [ecx+TPaintEvent.context], eax, [clScrollBk], bxSunken or bxNoFill

        lea     eax, [esi+TScrollWindow._HScroller]
        stdcall _SliderPixels, eax, [.recth.width]

        push    esi
        stdcall DrawFillRect, [ecx+TPaintEvent.context], [.recth.x], [.recth.y], eax, [.recth.height], [clScrollBk]
        mov     esi, [.recth.width]
        sub     esi, eax
        sub     esi, edx

        add     eax, [.recth.x]
        lea     edi, [eax+edx]
        stdcall DrawFillRect, [ecx+TPaintEvent.context], edi, [.recth.y], esi, [.recth.height], [clScrollBk]
        pop     esi

        mov     [.rectx.x], eax

        mov     [.rectx.width], edx
        push    [.recth.y] [.recth.height]
        pop     [.rectx.height] [.rectx.y]

        lea     eax, [.rectx]
        stdcall [DrawSlider], [ecx+TPaintEvent.context], eax, [clScrollSlider], sliderHorizontal

        lea     eax, [esi+TScrollWindow._VScroller]
        stdcall _SliderPixels, eax, [.rectv.height]

        push    esi
        stdcall DrawFillRect, [ecx+TPaintEvent.context], [.rectv.x], [.rectv.y], [.rectv.width], eax, [clScrollBk]
        mov     esi, [.rectv.height]
        sub     esi, eax
        sub     esi, edx

        add     eax, [.rectv.y]
        lea     edi, [eax+edx]
        stdcall DrawFillRect, [ecx+TPaintEvent.context], [.rectv.x], edi, [.rectv.width], esi, [clScrollBk]
        pop     esi

        mov     [.rectx.y], eax

        mov     [.rectx.height], edx
        push    [.rectv.x] [.rectv.width]
        pop     [.rectx.width] [.rectx.x]

        lea     eax, [.rectx]
        stdcall [DrawSlider], [ecx+TPaintEvent.context], eax, [clScrollSlider], sliderVertical

        clc
        jmp     .finish

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

.btnpress:
        stdcall __ScrollSetCursor, esi, [ecx+TMouseButtonEvent.x], [ecx+TMouseButtonEvent.y]

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

        stdcall _WhatScrollbar, esi, [ecx+TMouseButtonEvent.x], [ecx+TMouseButtonEvent.y]
        jc      .continue         ; not found

; here ecx=height of the scrollbar
;      edi= ptr to TScrollbar structure
;      ebx= coordinate of the mouse in the scrollbar

        stdcall _SliderPixels, edi, ecx
        sub     ebx, eax
        jl      .before
        cmp     ebx, edx
        jg      .after

; inside the slider - capture the mouse.
        mov     [edi+TScrollbar._Drag], ebx
        stdcall MouseCapture, [esi+TWindow.handle]
        jmp     .continue

.before:
.after:
        jmp     .continue

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

.btnrelease:
        stdcall __ScrollSetCursor, esi, [ecx+TMouseButtonEvent.x], [ecx+TMouseButtonEvent.y]

        xor     eax, eax
        mov     [esi+TScrollWindow._HScroller._Drag], eax
        mov     [esi+TScrollWindow._VScroller._Drag], eax

        stdcall MouseCapture, eax
        jmp     .continue

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

.mousemove:
        mov     ecx, [.pEvent]
        stdcall __ScrollSetCursor, esi, [ecx+TMouseMoveEvent.x], [ecx+TMouseMoveEvent.y]

        mov     ecx, [.pEvent]
        lea     edi, [esi+TScrollWindow._HScroller]
        mov     eax, [ecx+TMouseMoveEvent.x]
        mov     ecx, [esi+TScrollWindow._width]
        mov     [.event.ScrollBar], scrollX
        cmp     [edi+TScrollbar._Drag], 0
        jne     .found

        mov     ecx, [.pEvent]
        lea     edi, [esi+TScrollWindow._VScroller]
        mov     eax, [ecx+TMouseMoveEvent.y]
        mov     ecx, [esi+TScrollWindow._height]
        mov     [.event.ScrollBar], scrollY
        cmp     [edi+TScrollbar._Drag], 0

        je      .continue


.found:
        mov     [.event.event], seScroll
        mov     [.event.ScrollCmd], scTrack

        sub     ecx, ScrollBarWidth
        sub     eax, 2
        sub     eax, [edi+TScrollbar._Drag]
        stdcall _SetSliderPos, edi, eax, ecx
        jc      .endmove

        mov     eax, [edi+TScrollbar.Pos]
        mov     [.event.Value], eax

        lea     eax, [.event]
        stdcall ExecEvent, esi, eax
        jnc     .finish

; ?????? Do we need this?
;        execute esi, TScrollWindow.Refresh

.endmove:
        clc
        jmp     .finish

endp


;_________________________________________________________________________________________


proc TScrollWindow.ExecCmd, .self, .method
begin
        cmp     [.method], TScrollWindow._ComputeFreeArea
        je      .computefreearea

        stc
        return


.computefreearea:
        mov     ecx, [.self]
        sub     [ecx+TScrollWindow._FreeArea.width], ScrollBarWidth
        jns     @f
        mov     [ecx+TScrollWindow._FreeArea.width], 0
@@:
        sub     [ecx+TScrollWindow._FreeArea.height], ScrollBarWidth
        jns     @f
        mov     [ecx+TScrollWindow._FreeArea.height], 0
@@:
        stc
        return
endp


;_________________________________________________________________________________________



proc _SetSliderPos, .pScrollbar, .x, .lengthpx
begin
        push    eax ecx edx

        mov     ecx, [.pScrollbar]
        stdcall _SliderPixels, [.pScrollbar], [.lengthpx]

        sub     [.lengthpx], edx
        jz      .zero

        mov     eax, [ecx+TScrollbar.Max]
        imul    [.x]
        idiv    [.lengthpx]

        cmp     eax, 0
        jge     .ok
        mov     eax, 0
.ok:
        cmp     eax, [ecx+TScrollbar.Max]
        jle     .ok2
        mov     eax, [ecx+TScrollbar.Max]
.ok2:
        cmp     [ecx+TScrollbar.Pos], eax
        stc
        je      @f
        clc
@@:
        mov     [ecx+TScrollbar.Pos], eax
        pop     edx ecx eax
        return

.zero:
        xor     eax, eax
        jmp     .ok2
endp



; returns:
;   eax - position in pixels
;   edx - size in pixels

proc _SliderPixels, .pScrollbar, .length
begin
        push    ecx esi

        mov     ecx, [.pScrollbar]

; page size in pixels.
        mov     esi, [ecx+TScrollbar.Max]
        mov     eax, [.length]
        add     esi, [ecx+TScrollbar.Page]
        test    esi, esi
        jz      .lengthok

        imul     [ecx+TScrollbar.Page]
        idiv     esi

.lengthok:
        cmp     eax, MinScrollBarWidth
        jge     @f
        mov     eax, MinScrollBarWidth
@@:
        cmp     eax, [.length]
        jle     @f
        mov     eax, [.length]
@@:
        push    eax

; position in pixels

        sub     eax, [.length]
        mov     esi, [ecx+TScrollbar.Max]
        test    esi, esi
        jnz     @f
        inc     esi
@@:
        neg     eax
        mul     [ecx+TScrollbar.Pos]
        div     esi

        pop     edx   ; width

        pop     esi ecx
        return
endp


proc __ScrollSetCursor, .window, .x, .y
begin
        pushad

        mov     esi, [.window]
        mov     eax, [esi+TWindow._cursor]
        stdcall _WhatScrollbar, esi, [.x], [.y]
        jc      @f

        mov     eax, mcArrow
@@:
        test    eax, $ffffff00
        jnz     @f
        stdcall GetStockCursor, eax
@@:
        stdcall SetMouseCursor, eax

        popad
        return
endp



; returns:
;      ecx = height of the scrollbar
;      edi = ptr to TScrollbar structure
;      ebx = coordinate of the mouse in the scrollbar
;
; CF=1 if the cursor is not in scrollbar

proc _WhatScrollbar, .window, .x, .y
begin
        push    eax edx esi

        mov     esi, [.window]


        mov     eax, [.x]
        mov     edx, [.y]

        mov     eax, [esi+TWindow._width]
        mov     edx, [esi+TWindow._height]

        sub     eax, ScrollBarWidth
        sub     edx, ScrollBarWidth

        cmp     [.x], eax
        jl      .checkh

        cmp     [.y], edx
        jg      .notfound     ; we are in the dead square.

; mouse in vscrollbar
        mov     ebx, [.y]
        mov     ecx, edx
        lea     edi, [esi+TScrollWindow._VScroller]
        jmp     .found

.checkh:
        cmp     [.y], edx
        jl      .notfound     ; in the client area

; mouse in hscrollbar
        mov     ebx, [.x]
        mov     ecx, eax
        lea     edi, [esi+TScrollWindow._HScroller]

.found:
        clc
        pop     esi edx eax
        return

.notfound:
        stc
        pop     esi edx eax
        return
endp



endmodule
