; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Objects template engine
;
;  Target OS: Any
;
;  Dependencies: objects.asm
;
;  Notes:
;_________________________________________________________________________________________
module "SplitGrid"


stOriginBR = $08    ; the origin of the cell splitter is on the right (or bottom)
                    ; edge of the cell.

stHoriz    = $00    ; the cell is splitted on left and right parts.
stVert     = $04    ; the cell is splitted on up and down parts.

stNone     = $80    ; the cell is not splitted.

stRelative = $4000  ; the pos field is a part of a whole (in 1/32768 units)
stJustGap  = $8000  ; the rectangle is splitted, but not resizeable.


struct TSplitRect
  .rect    RECT    ; the rectangle of the area.
  .spRect  RECT    ; rectangle of the splitter.
  .pWindow dd ?    ; pointer to TWindow descendent, bound to the grid cell.
  .pos     dd ?    ; in pixels or as part of the whole.
  .min     dd ?    ; minimal size
  .max     dd ?    ; maximal size
  .type    dd ?    ; one or more of stXXXXX flags defined above.
  .spWidth dd ?    ; width of the spliter
  .id      dd ?
ends


struct TSplitTemplate
  .type  dw ?      ; stXXXXXXXXX flags
  .width dw ?      ; width of the splitter
  .pos   dd ?      ; current position
  .min   dd ?      ; min
  .max   dd ?      ; max
ends


struct TCellTemplate
  .type  dw ?
  .id    dw ?
ends


macro SplitStart name {
  local ..expected, ..id
  ..expected = 1
  ..id = 0

  cell@expected equ ..expected
  cell@id equ ..id
  cell@name equ name

  label name byte

  macro Split type, width, pos, min, max \{
    dw type, width
    dd pos, min, max
    cell@id = cell@id + 1
    cell@expected = cell@expected + 1
  \}

  macro Cell name \{
    .\#name = cell@id
    dw stNone, cell@id
    cell@id = cell@id + 1
    cell@expected = cell@expected - 1
  \}

}


macro SplitEnd {
  match name, cell@name \{ cell@namestr equ `name  \}
  if cell@expected<>0
    disp 1, 'WARNING! "', cell@namestr, '" split template is not consistent! Automatically added  ', <cell@expected, 10>, " empty cells. FIXIT!", 13
    repeat cell@expected
      dw stNone, 0
    end repeat
  end if
  match name, cell@name \{ sizeof.\#name = $ - name \}
  purge Cell, Split
  restore cell@expected, cell@id, cell@name
}


struc SplitTemplate [name, type, width, pos, min, max] {
common
  .:
  local ..expected, ..index
  ..expected = 1
  ..index = 0
forward
  if ~name eq NONE
    name = ..index
  end if
     dw type
     dw width
     dd pos
     dd min
     dd max
  if (type <> stNone)
    ..expected = ..expected + 1
  else
    ..expected = ..expected - 1
  end if

  ..index = ..index + 1
common
  repeat ..expected
    dw  stNone, 0
    dd  0, 0, 0
  end repeat

  if ..expected <> 0
    disp 1, 'WARNING! "', `., '" split template is not consistent! Automatically added  ', <..expected, 10>, " empty cells. FIXIT!", 13
  end if
}



proc RealignGrid, .ptrSplitter, .ptrRect
.rect RECT
begin
        pushad

        mov     edi, [.ptrSplitter]
        mov     esi, [.ptrRect]
        call    _DoRealign

        popad
        return
endp


; edi is pointer to the current TSplitRect
; esi is pointer to the rect that have to be assigned to the current TSplitRect
proc _DoRealign
.rect1 RECT
.rect2 RECT
begin
; copy the rect to the target rectangles.
        mov     eax, [esi+RECT.left]
        mov     ecx, [esi+RECT.top]

        mov     [edi+TSplitRect.rect.left], eax
        mov     [edi+TSplitRect.rect.top], ecx

        mov     [edi+TSplitRect.spRect.left], eax
        mov     [edi+TSplitRect.spRect.top], ecx

        mov     [.rect1.left], eax
        mov     [.rect1.top], ecx
        mov     [.rect2.left], eax
        mov     [.rect2.top], ecx

        mov     eax, [esi+RECT.right]
        mov     ecx, [esi+RECT.bottom]

        mov     [edi+TSplitRect.rect.right], eax
        mov     [edi+TSplitRect.rect.bottom], ecx

        mov     [edi+TSplitRect.spRect.right], eax
        mov     [edi+TSplitRect.spRect.bottom], ecx
        mov     [.rect1.right], eax
        mov     [.rect1.bottom], ecx
        mov     [.rect2.right], eax
        mov     [.rect2.bottom], ecx

        mov     esi, edi        ; current TSplitRect
        add     edi, sizeof.TSplitRect    ; goto next one

        mov     ecx, [esi+TSplitRect.type]
        test    ecx, stNone
        jnz     .endsplit

; There is split, so compute it.
        and     ecx, stVert

        mov     ebx, [esi+ecx+TSplitRect.rect.right]
        sub     ebx, [esi+ecx+TSplitRect.rect.left]     ; size of rect (x or y depending on type)
        mov     edx, [esi+TSplitRect.spWidth]
        sub     ebx, edx

        mov     eax, [esi+TSplitRect.pos]

        cmp     eax, [esi+TSplitRect.min]
        cmovl   eax, [esi+TSplitRect.min]

        cmp     eax, [esi+TSplitRect.max]
        cmovg   eax, [esi+TSplitRect.max]

        mov     [esi+TSplitRect.pos],eax


        mov     ecx, [esi+TSplitRect.type]
        and     ecx, $ff

        test    [esi+TSplitRect.type], stRelative
        jz      .posready

        imul    eax, ebx
        sar     eax, 15         ; div $8000

.posready:
        test    ecx, stOriginBR
        jz      .readyofs       ; it is Left or Top

        neg     edx
        neg     eax

.readyofs:
        add     eax, [.rect1.left+ecx]

        mov     [.rect1.right+ecx], eax
        mov     [esi+ecx+TSplitRect.spRect], eax

        add     ecx, 8      ; right or bottom
        and     ecx, $0f

        add     eax, edx
        mov     [.rect1.right+ecx], eax
        mov     [esi+ecx+TSplitRect.spRect], eax

        push    esi

        lea     esi, [.rect1]
        call _DoRealign

        lea     esi, [.rect2]
        call _DoRealign

        pop     esi
        jmp     .endresize


.endsplit:
        mov     ebx, [esi+TSplitRect.pWindow]
        test    ebx, ebx
        jz      .endresize

        mov     eax, [esi+TSplitRect.rect.left]
        mov     ecx, [esi+TSplitRect.rect.top]

        exec    ebx, TWindow:Move, eax, ecx

        mov     eax, [esi+TSplitRect.rect.right]
        mov     ecx, [esi+TSplitRect.rect.bottom]
        sub     eax, [esi+TSplitRect.rect.left]
        sub     ecx, [esi+TSplitRect.rect.top]

        xor     edx, edx

        test    eax, eax
        cmovs   eax, edx

        test    ecx, ecx
        cmovs   ecx, edx

        exec    ebx, TWindow:Resize, eax, ecx

.endresize:
        return
endp



proc DrawSplitters, .pImage, .ptrSplitters
begin
        pushad
        mov     esi, [.ptrSplitters]
        mov     edi, [.pImage]
        call    _DoDrawSplitters
        popad
        return
endp


;esi - pointer to TSplitRect
; edi - TImage

proc _DoDrawSplitters
begin
        mov     ebx, esi
        add     esi, sizeof.TSplitRect

        test    [ebx+TSplitRect.type], stNone
        jnz     .exit

        test    [ebx+TSplitRect.type], stJustGap
        jnz     .next

        lea     eax, [ebx+TSplitRect.spRect]
        stdcall [DrawSplitter], edi, eax, [ebx+TSplitRect.type]

.next:
        call    _DoDrawSplitters  ; first
        call    _DoDrawSplitters  ; second

.exit:
        return
endp


; Returns:
;   CF=1; the splitter has been found. EAX=pointer to TSplitRect structure.
;   CF=0; the splitter has not been found. EAX not changed.

proc FindSplitter, .ptrSplitters, .ptrPoint
begin
        pushad

        mov     esi, [.ptrSplitters]
        mov     edi, [.ptrPoint]

        call    _DoFindSplitter
        jnc     .exit
        mov     [esp+4*regEAX], ebx
.exit:
        popad
        return
endp


proc _DoFindSplitter
begin
        mov     ebx, esi
        add     esi, sizeof.TSplitRect

        test    [ebx+TSplitRect.type], stNone
        jnz     .exit

        lea     eax, [ebx+TSplitRect.spRect]

        stdcall PointInRect, eax, [edi+POINT.x], [edi+POINT.y]
        jnc     .next
        return

.next:
        call    _DoFindSplitter  ; first
        jc      .exit

        call    _DoFindSplitter  ; second
.exit:
        return
endp



proc FindSplitterCell, .ptrSplitters, .ptrPoint
begin
        mov     esi, [.ptrSplitters]
        mov     ecx, 1                  ; one splitter expected.

.loop:
        add     ecx, 2
        test    [esi+TSplitRect.type], stNone
        jz      .next

        sub     ecx, 2

        lea     eax, [esi+TSplitRect.rect]
        stdcall PointInRect, esi, [.ptrPoint]
        jc      .found

.next:
        lea     esi, [esi+sizeof.TSplitRect]
        dec     ecx
        jnz     .loop
; here CF=0
        popad
        return

.found:
; here CF=1
        mov     [esp+regEAX*4], esi
        popad
        return
endp



proc ReadSplitGridTemplate, .ptrTemplate
begin
        pushad

        mov     edx, [.ptrTemplate]
        test    edx, edx
        jz      .finish

        mov     esi, edx

        stdcall CreateArray, sizeof.TSplitRect
        mov     edx, eax

        mov     ecx, 1

.read_loop:
        stdcall AddArrayItems, edx, 1

        push    eax ecx

        mov     edi, eax
        mov     ecx, sizeof.TSplitRect/4
        xor     eax, eax
        rep stosd

        pop     ecx edi

        xor     eax, eax
        lodsw   ; the stXXX values.

        mov     [edi+TSplitRect.type], eax

        test    eax, stNone
        jnz     .empty_cell

        lodsw   ; the width
        mov     [edi+TSplitRect.spWidth], eax

        lodsd
        mov     [edi+TSplitRect.pos], eax
        lodsd
        mov     [edi+TSplitRect.min], eax
        lodsd
        mov     [edi+TSplitRect.max], eax

        inc     ecx
        jmp     .read_loop

.empty_cell:
        lodsw
        mov     [edi+TSplitRect.id], eax
        dec     ecx
        jnz     .read_loop

.finish:
        mov     [esp+regEAX*4], edx
        popad
        return
endp





endmodule