; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: TWindow object class
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: TWindow is the common ancestor of all visible on the screen controls.
;         This file contains only OS independent part of the library and includes
;         the OS dependent files.
;_________________________________________________________________________________________
module "TWindow library"

; Interfaces for the OS dependent procedures

; NOTICE: Some of them are not needed, so can be removed.


;-----------------------------------------------------------------------------------------
; Creates OS window, according to the content of TWindow structure [.pWindow]
;
; Notice, that this OS windows are created ONLY for the visible top level windows.
;
;-----------------------------------------------------------------------------------------
interface _CreateWindow, .pWindow

;-----------------------------------------------------------------------------------------
; Destroy the OS window. The OS windows are actually destroyed when invisible and then
; recreated when needed.
;-----------------------------------------------------------------------------------------
interface _DestroyWindow, .hwnd

;-----------------------------------------------------------------------------------------
; Converts the client coordinates to screen coordinates.
;-----------------------------------------------------------------------------------------
interface _ClientToScreen, .hwnd, .x, .y

;-----------------------------------------------------------------------------------------
; Changes the visibility of the window.
;-----------------------------------------------------------------------------------------
interface _ShowWindow, .hwnd, .flag


interface _RefreshWindowRect, .hwnd, .x, .y, .width, .height
interface _SetWindowTextUtf8, .hwnd, .ptrUtf8
interface _SetModalTowards, .hwnd, .hwndParent
interface _FinalizeModal, .hwnd, .hwndParent
interface _GetWindowStruct, .hwin
interface _SetWindowStruct, .hwin, .value


; probably not needed, but will keep them for some time.

;interface _SetWindowBorder, .hwnd, .brdType
;interface _GetWindowBounds, .hwnd, .pBounds
;interface _SetWindowBounds, .hwnd, .pBounds
;interface _CreateNullWindow
;interface _GetParent, .hwnd
;interface _GetChildren, .hwnd
;interface _GetVisible, .hwnd
;interface _SetFocus, .hwnd
;interface _AddChild, .hwnd, .child
;interface _GetWindowTextUtf8, .hwnd, .ptrUtf8
;interface _EnableWindow, .hwnd, .flag
;interface _SetTransientTowards, .hwnd, .hwndParent


include '%TargetOS%/windows.asm'



; Border kind of window.
borderNone    = 0
borderFull    = 1
borderModal   = 2
borderToolbox = 3


object TWindow, TObject

  .handle         dd ?   ; it is handle to the system provided window.

  ._x             dd ?
  ._y             dd ?
  ._width         dd ?
  ._height        dd ?

  ._border        dd ?    ; has meaning only for the top level windows!

  ._cursor        dd ?    ; mouse cursor.

  ._caption       dd ?    ; string handle with window caption.

  ._enabled       dd ?

  ._visible       dd ?

  .__want_focus   dd ?

  .__action       dd ?

  .__main         dd ?    ; this is the window that is main for this window. has meaning only for top level windows.

; window canvas

  ._screen        dd ?    ; TImage containing the image of the window with all children composited.
  ._canvas        dd ?    ; TImage containing the image of the window only.

  ._rect_list     dd ?    ; array of rectangles containing all the changed rectangles of the control.

; window user event handlers

  ._OnPaint        dd ?
  ._OnScroll       dd ?
  ._OnKeyPressed   dd ?

; parameters

  param .OSWindow, .GetOSWindow, NONE           ; the first window down the tree, that has window handle.

  param .Action, .__action, .SetAction

  param .x,      ._x,      .SetX
  param .y,      ._y,      .SetY
  param .width,  ._width,  .SetWidth
  param .height, ._height, .SetHeight

  param .border, ._border, ._border

  param ._MainWindow, .__main, .__main

  param .Cursor, ._cursor, ._cursor

  param .Visible, ._visible, .SetVisible
  param .Caption, .GetCaption, .SetCaption

  param .Enabled, .GetEnabled, .SetEnabled
  param .GlobalEnabled, .GetGlobalEnabled, NONE

  param .WantFocus, .__want_focus, NONE

  param .Screen, .GetImgScreen, NONE
  param .Canvas, ._canvas, NONE

  param .OnPaint, ._OnPaint, .SetOnPaint
  param .OnScroll, ._OnScroll, ._OnScroll

  param .OnKeyPressed, ._OnKeyPressed, ._OnKeyPressed

  param .SplitCell, NONE, .SetSplitCell

; private methods

  method .__UpdateHandle
  method .__UpdateFocus
  method .__UpdateCanvas


; parameter methods

  method .SetParent, .value

  method .SetAction, .value

  method .UpdateAction, .action    ; to be defined only for controls that can be attached to TAction

  method .SetX, .value
  method .SetY, .value
  method .SetWidth, .value
  method .SetHeight, .value

  method .Move, .newX, .newY
  method .Resize, .newWidth, .newHeight

  method .GetEnabled
  method .SetEnabled, .value

  method .GetGlobalEnabled

  method .GetCaption
  method .SetCaption, .value

  method .SetVisible, .value

  method .SetOnPaint, .value

  method .SetSplitCell, .value

  method .GetImgScreen

  method .GetOSWindow

; other methods

  method .Create, .parent
  method .Destroy


; Parent/child methods definitions of abstract methods, inherited from TObject

  method .ChildByXY, .x, .y, .onlyenabled

  method .RectChanged, .pRect, .fOnlyNotifyParent      ; invalidates the rectangle, so all children windows in this rectangle are
                                                       ; to be redrawn. Called from the children window on any visual changes.
                                                       ; notice, that if the child changed the size ot position, it needs special
                                                       ; processing of the rectangle. Actually the changed rectangle is the
                                                       ; bounding rectangle of the new and old rectangles.

  method .RectChangedXY, .x, .y, .width, .height, .fOnlyNotifyParent

  method  .ClientToScreenXY, .x, .y


; Painting methods

  method .Paint        ; Paints itself on the .Canvas TImage

  method .Refresh
  method .RefreshAll

; System event methods

  method .EventFocusIn
  method .EventFocusOut

  method .EventMouseEnter
  method .EventMouseLeave

  method .EventMouseMove, .x, .y, .kbdState

  method .EventButtonPress, .button, .kbdState, .x, .y
  method .EventButtonRelease, .button, .kbdState, .x, .y

  method .EventScroll, .direction, .command, .value

  method .EventKeyPress, .utf8, .scancode, .kbdState
  method .EventKeyRelease, .scancode, .kbdState

  method .CloseRequest, .reason

endobj


interface TWindow.OnPaint, .self, .canvas

interface TWindow.OnScroll, .self, .direction, .command, .value

interface TWindow.OnKeyPressed, .self, .utf8, .scancode, .kbdState



; OS independent code.
;_________________________________________________________________________________________



method TWindow.Create
begin
        inherited [.parent]

        set     [.self], TWindow:Enabled, TRUE
        set     [.self], TWindow:Cursor, mcArrow
        set     [.self], TWindow:border, borderFull
        return
endp


;_________________________________________________________________________________________



method TWindow.Destroy
begin
        pushad

        mov     esi, [.self]

; hide the window, destroy the handle and remove the focus.

        set     esi, TWindow:Visible, FALSE
        exec    esi, TWindow:__UpdateHandle
        exec    esi, TWindow:__UpdateFocus

        cmp     esi, [__LastPointedWindow]
        jne     @f
        mov     [__LastPointedWindow], 0
@@:

; detach from action if any

        mov     ecx, [esi+TWindow.__action]
        jecxz   @f

        exec    ecx, TAction:Detach, esi

@@:
; destroy the canvas and screen images if any.

        mov     ecx, [esi+TWindow._canvas]
        jecxz   .canvas_ok

        stdcall DestroyImage, ecx

.canvas_ok:

        mov     ecx, [esi+TWindow._screen]
        jecxz   .screen_ok

        stdcall DestroyImage, ecx

.screen_ok:

; free the string handles

        stdcall StrDel, [esi+TWindow._caption]

        popad

        inherited
        return
endp


;_________________________________________________________________________________________


method TWindow.GetImgScreen
.client RECT
.rect   RECT
begin
        pushad

        xor     eax, eax
        mov     esi, [.self]

        cmp     [esi+TWindow._screen], eax
        je      .finish_canvas

        mov     edi, [esi+TWindow._rect_list]   ; list of the rectangles that need rendering.
        test    edi, edi
        jz      .finish_screen                  ; no changes, the screen is up to date.

        mov     ecx, [edi+TArray.count]
        test    ecx, ecx
        jz      .finish_free

        lea     edi, [edi+TArray.array]

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

        mov     [.client.left], eax
        mov     [.client.top], eax
        mov     [.client.right], ebx
        mov     [.client.bottom], edx

        lea     ebx, [.client]
        lea     edx, [.rect]

.rect_loop:
        stdcall RectIntersect, edx, ebx, edi
        jc      .next_rect

        stdcall TWindow.__BlendOneRect, esi, edx

.next_rect:
        add     edi, sizeof.RECT
        loop    .rect_loop

.finish_free:
        xor     eax, eax
        xchg    [esi+TWindow._rect_list], eax

        stdcall FreeMem, eax

.finish_screen:
        mov     eax, [esi+TWindow._screen]
        mov     [esp+4*regEAX], eax
        popad
        return


.finish_canvas:
        mov     eax, [esi+TWindow._canvas]
        mov     [esp+4*regEAX], eax
        popad
        return
endp



proc TWindow.__BlendOneRect, .pWindow, .pRect
.dest RECT
.xSrc dd ?
.ySrc dd ?
begin
        pushad

        mov     esi, [.pWindow]
        mov     edi, [.pRect]
        mov     edx, [esi+TWindow._screen]

.process_screen:

; start with the background.

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

        sub     eax, [edi+RECT.left]
        jz      .end_loop

        sub     ecx, [edi+RECT.top]
        jz      .end_loop

        stdcall CopyImageRect, edx, [edi+RECT.left], [edi+RECT.top], [esi+TWindow._canvas], [edi+RECT.left], [edi+RECT.top], eax, ecx

; then blend the children windows

        get     ebx, esi, TObject:Children
        test    ebx, ebx
        jz      .end_loop

        xor     ecx, ecx
        dec     ecx

.children_loop:
        inc     ecx
        cmp     ecx, [ebx+TArray.count]
        je      .end_loop

        mov     esi, [ebx+4*ecx+TArray.array]

        istype  esi, TWindow
        jne     .children_loop

        get     eax, esi, TWindow:Visible
        test    eax, eax
        jz      .children_loop

        push    [esi+TWindow._x] [esi+TWindow._y] [esi+TWindow._width] [esi+TWindow._height]
        pop     [.dest.bottom] [.dest.right] [.dest.top] [.dest.left]

        mov     eax, [.dest.left]
        add     [.dest.right], eax

        mov     eax, [.dest.top]
        add     [.dest.bottom], eax

        lea     eax, [.dest]
        stdcall RectIntersect, eax, eax, edi
        jc      .children_loop                  ; the rectangles does not intersect, so the child should not to be painted.

        mov     eax, [.dest.left]
        sub     [.dest.right], eax

        sub     eax, [esi+TWindow._x]
        mov     [.xSrc], eax

        mov     eax, [.dest.top]
        sub     [.dest.bottom], eax
        sub     eax, [esi+TWindow._y]
        mov     [.ySrc], eax

        get     eax, esi, TWindow:Screen
        test    eax, eax
        jz      .children_loop

        stdcall BlendImage, edx, [.dest.left], [.dest.top], eax, [.xSrc], [.ySrc], [.dest.right], [.dest.bottom]
        jmp     .children_loop

.end_loop:
        popad
        return
endp



;_________________________________________________________________________________________



method TWindow.SetAction
begin
        pushad

        mov     esi, [.self]
        mov     ecx, [.value]
        mov     edx, [esi+TWindow.__action]

        cmp     ecx, edx
        je      .finish

        mov     [esi+TWindow.__action], ecx

        test    edx, edx
        jz      .old_ok

        exec    edx, TAction:Detach, esi

.old_ok:
        test    ecx, ecx
        jz      .new_ok

        exec    ecx, TAction:Attach, esi

.new_ok:
        getm    eax, esi, TWindow:UpdateAction
        test    eax, eax
        jz      .finish

        stdcall eax, esi, ecx

.finish:
        popad
        return
endp


;_________________________________________________________________________________________


method TWindow.GetCaption
begin
        mov     eax, [.self]
        mov     eax, [eax+TWindow._caption]
        test    eax, eax
        jz      .finish
        stdcall StrDup, eax
.finish:
        return
endp


;_________________________________________________________________________________________


method TWindow.SetCaption
begin
        push    eax esi

        mov     esi, [.self]

        lea     eax, [esi+TWindow._caption]
        stdcall SetString, eax, [.value]
        stdcall StrPtr, [eax]

        cmp     [esi+TWindow.handle], 0
        je      @f

        stdcall _SetWindowTextUtf8, [esi+TWindow.handle], eax

@@:
        exec    esi, TWindow:Refresh

        pop     esi eax
        return
endp


;_________________________________________________________________________________________



method TWindow.SetVisible
begin
        push    eax esi

        mov     esi, [.self]

        mov     eax, [.value]
        cmp     eax, [esi+TWindow._visible]
        je      .finish

        mov     [esi+TWindow._visible], eax

        exec    esi, TWindow:__UpdateHandle
        exec    esi, TWindow:__UpdateCanvas
        exec    esi, TWindow:__UpdateFocus

        exec    esi, TWindow:Refresh

.finish:
        pop     esi eax
        return
endp


;_________________________________________________________________________________________


method TWindow.SetOnPaint
begin
        push    eax esi

        mov     esi, [.self]
        mov     eax, [.value]
        cmp     eax, [esi+TWindow._OnPaint]
        je      .finish

        mov     [esi+TWindow._OnPaint], eax

        exec    esi, TWindow:Refresh

.finish:
        pop     esi eax
        return
endp

;_________________________________________________________________________________________


method TWindow.SetSplitCell
begin
        push    eax

        get     eax, [.self], TWindow:Parent
        istype  eax, TForm
        jne     .finish

        exec    eax, TForm:AttachControlToCell, [.self], [.value]

.finish:
        pop     eax
        return
endp


;_________________________________________________________________________________________


method TWindow.GetGlobalEnabled
begin
        push    ecx

        mov     ecx, [.self]

        get     eax, ecx, TWindow:Action
        test    eax, eax
        jz      .action_ok

        get     eax, eax, TAction:Enabled
        jmp     .check_parent

.action_ok:
        mov     eax, [ecx+TWindow._enabled]

.check_parent:
        test    eax, eax
        jz      .finish

        get     ecx, [.self], TWindow:Parent
        jecxz   .finish

        get     eax, ecx, TWindow:GlobalEnabled

.finish:
        pop     ecx
        return
endp



;_________________________________________________________________________________________



method TWindow.GetEnabled
begin
        push    ecx

        mov     ecx, [.self]

        get     eax, ecx, TWindow:Action
        test    eax, eax
        jz      .action_ok

        get     eax, eax, TAction:Enabled
        jmp     .finish

.action_ok:
        mov     eax, [ecx+TWindow._enabled]

.finish:
        pop     ecx
        return
endp




method TWindow.SetEnabled
begin
        push    ecx eax
        mov     ecx, [.self]

        mov     eax, [.value]
        xchg    eax, [ecx+TWindow._enabled]
        cmp     eax, [ecx+TWindow._enabled]
        je      .finish

        exec    ecx, TWindow:__UpdateFocus
        exec    ecx, TWindow:Refresh

.finish:
        pop     eax ecx
        return
endp





method TWindow.GetOSWindow
begin
        push    esi

        mov     esi, [.self]

.loop:
        cmp     [esi+TWindow.handle], 0
        jne     .finish

        get     esi, esi, TWindow:Parent
        test    esi, esi
        jnz     .loop

.finish:
        mov     eax, esi
        pop     esi
        return
endp


;_________________________________________________________________________________________


method TWindow.Paint
begin
        pushad

        mov     esi, [.self]

        get     eax, esi, TWindow:OnPaint
        test    eax, eax
        jz      .user_paint_ok

        get     ecx, esi, TWindow:Canvas
        test    ecx, ecx
        jz      .user_paint_ok

        stdcall eax, esi, ecx                   ; user OnPaint handler.

.user_paint_ok:
        popad
        return
endp


;_________________________________________________________________________________________



method TWindow.Refresh
begin
;        OutputValue "Refresh call from: $", [ebp+4], 16, 8

        exec    [.self], TWindow:Paint
        exec    [.self], TWindow:RectChanged, 0, FALSE
        return
endp



method TWindow.RefreshAll
begin
        pushad
        get     esi, [.self], TObject:Children
        test    esi, esi
        jz      .end_loop

        mov     ecx, [esi+TArray.count]

.loop:
        dec     ecx
        js      .end_loop

        istype  [esi+TArray.array+4*ecx], TWindow
        jne     .loop

        exec    [esi+TArray.array+4*ecx], TWindow:RefreshAll
        jmp     .loop

.end_loop:
        exec    [.self], TWindow:Refresh

        popad
        return
endp



;_________________________________________________________________________________________


;---------------------------------------------------------------------------------
; Arguments:
;   .self
;   .x
;   .y
;
; Returns:
;   eax - pointer to TWindow
;   ecx - X coordinates, translated to the client coordinates of the child.
;   edx - Y coordinates, translated to the client coordinates of the child.
;
; If children window was not found, the method returns:
; eax = [.self]
; ecx = [.x]
; edx = [.y]
;---------------------------------------------------------------------------------

method TWindow.ChildByXY
begin
        push    ebx esi edi

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

        mov     esi, [eax+TObject.__pChildren]
        test    esi, esi
        jz      .finish

        mov     edi, [esi+TArray.count]
        lea     esi, [esi+TArray.array]


.loop:
        dec     edi
        js      .finish

        mov     ebx, [esi+4*edi]

        istype  ebx, TWindow
        jne     .loop

        sub     ecx, [ebx+TWindow._x]
        jl      .no2

        sub     edx, [ebx+TWindow._y]
        jl      .no

        cmp     ecx, [ebx+TWindow._width]
        jge     .no

        cmp     edx, [ebx+TWindow._height]
        jge      .no

        cmp     [.onlyenabled], FALSE
        je      .enabled_ok

        push    eax
        get     eax, ebx, TWindow:GlobalEnabled
        test    eax, eax
        pop     eax
        jz      .no


.enabled_ok:
        pushad

        get     esi, ebx, TWindow:Canvas
        test    esi, esi
        jz      .end_canvas

        mov     eax, edx
        imul    eax, [esi+TImage.width]
        shl     eax, 2
        add     eax, [esi+TImage.pPixels]

        cmp     byte [eax+4*ecx+3], 0   ; the alpha value under the pointer

.end_canvas:
        popad
        jnz     .yes

.no:
        add     edx, [ebx+TWindow._y]

.no2:
        add     ecx, [ebx+TWindow._x]
        jmp     .loop


.yes:
        mov     eax, ebx
        exec    eax, TWindow:ChildByXY, ecx, edx, [.onlyenabled]

.finish:
        pop     edi esi ebx
        return

endp




;_________________________________________________________________________________________
;
; Includes the passed rectangle in the invalid rectangle. Later this rectangle will need
; to be repainted, but only when the screen image is required.
;

method TWindow.RectChanged      ;, .pRect, .fOnlyNotifyParent

.rect   RECT
.client RECT

begin
        pushad

        mov     esi, [.self]
        lea     edi, [.rect]

        mov     ecx, [esi+TWindow._width]
        test    ecx, ecx
        jz      .finish

        mov     edx, [esi+TWindow._height]
        test    edx, edx
        jz      .finish

        xor     eax, eax
        mov     [edi+RECT.left], eax
        mov     [edi+RECT.top], eax
        mov     [edi+RECT.right], ecx
        mov     [edi+RECT.bottom], edx

        mov     eax, [.pRect]
        test    eax, eax
        jz      .rect_ok

        stdcall RectIntersect, edi, edi, eax
        jc      .finish

.rect_ok:
        cmp     [.fOnlyNotifyParent], 0
        jne     .notify_parent

        mov     edx, [esi+TWindow._rect_list]
        test    edx, edx
        jnz     .list_ok

        cmp     [esi+TWindow._screen], edx      ; edx is 0 here!
        je      .rect_added_ok

        stdcall CreateArray, sizeof.RECT
        mov     edx, eax

.list_ok:
        stdcall RectListAdd, edx, edi

.rect_added_ok:
        mov     [esi+TWindow._rect_list], edx


; now send some redraw events if needed.

        mov     ecx, [esi+TWindow.handle]
        jecxz   .notify_parent

        mov     eax, [edi+RECT.right]
        mov     edx, [edi+RECT.bottom]
        sub     eax, [edi+RECT.left]
        sub     edx, [edi+RECT.top]

        stdcall _RefreshWindowRect, ecx, [edi+RECT.left], [edi+RECT.top], eax, edx
        jmp     .finish

; now do the same for the parent window

.notify_parent:

        mov     ecx, [esi+TWindow._x]           ; shift the rectangle for the parent coordinates.
        mov     edx, [esi+TWindow._y]

        add     [edi+RECT.left], ecx
        add     [edi+RECT.top], edx
        add     [edi+RECT.right], ecx
        add     [edi+RECT.bottom], edx

        get     esi, esi, TWindow:Parent
        test    esi, esi
        jz      .finish

        exec    esi, TWindow:RectChanged, edi, FALSE

.finish:
        popad
        return
endp



method TWindow.RectChangedXY
begin
        push    eax

        mov     eax, [.x]
        add     [.width], eax

        mov     eax, [.y]
        add     [.height], eax

        lea     eax, [.x]
        exec    [.self], TWindow:RectChanged, eax, [.fOnlyNotifyParent]

        pop     eax
        return
endp


;_________________________________________________________________________________________



method TWindow.SetParent
begin
        inherited [.value]
        exec    [.self], TWindow:__UpdateHandle      ; create/destroy handle of the OS window, according to the  window state.
        return
endp


;_________________________________________________________________________________________



method TWindow.__UpdateHandle
begin
        pushad

        mov     esi, [.self]

        xor     ecx, ecx

        get     eax, esi, TWindow:Visible
        test    eax, eax
        jz      .no_handle

        get     eax, esi, TWindow:Parent
        test    eax, eax
        jnz     .no_handle

; need handle

        cmp     [esi+TWindow.handle], ecx
        jne     .finish                         ; the window needs OS handle and has one.

; The window has no handle, but needs it.

        stdcall _CreateWindow, esi
        mov     [esi+TWindow.handle], eax

.finish:
        popad
        return


.no_handle:

        cmp     [esi+TWindow.handle], ecx
        je      .finish                         ; no need for handle and there is no such.

        stdcall _DestroyWindow, [esi+TWindow.handle]    ; destroy not needed window handle.

        mov     [esi+TWindow.handle], ecx
        jmp     .finish

endp




method TWindow.__UpdateFocus
begin
        mov     eax, [.self]
        cmp     eax, [__FocusedWindow]
        jne     .finish

        get     eax, [.self], TWindow:Visible
        test    eax, eax
        jz      .clear

        get     eax, [.self], TWindow:Enabled
        test    eax, eax
        jz      .clear

        get     eax, [.self], TWindow:WantFocus
        test    eax, eax
        jz      .clear

.finish:
        return

.clear:
        mov     [__FocusedWindow], 0
        exec    [.self], TWindow:EventFocusOut
        return
endp





method TWindow.__UpdateCanvas
begin
        pushad
        mov     esi, [.self]

        mov     ebx, [esi+TWindow._visible]
        mov     edi, [esi+TWindow._canvas]

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

        test    ebx, ebx
        jz      .destroy

        test    edi, edi
        jz      .create_new

; check the size

        cmp     ecx, [edi+TImage.width]
        ja      .destroy

        cmp     edx, [edi+TImage.height]
        jbe     .exit


.destroy:
        stdcall DestroyImage, edi                         ; canvas
        stdcall DestroyImage, [esi+TWindow._screen]

        xor     eax, eax
        mov     [esi+TWindow._screen], eax
        mov     [esi+TWindow._canvas], eax

        test    ebx, ebx
        jz      .exit


.create_new:

; new size - get it with x1.5 reserve in order to allow easy resizing.
;        lea     ecx, [ecx*3]
;        lea     edx, [edx*3]
;        shr     ecx, 1
;        shr     edx, 1

; The above reserve is too big. Lets make it more RAM-friendly.

        add     ecx, 256
        add     edx, 256

;        OutputValue "New canvas  width:", ecx, 10, -1
;        OutputValue "New canvas height:", edx, 10, -1

        stdcall CreateImage, ecx, edx
        mov     [esi+TWindow._canvas], eax

        cmp     [esi+TWindow.__pChildren], 0
        je      .exit

        stdcall CreateImage, ecx, edx
        mov     [esi+TWindow._screen], eax

.exit:
        popad
        return
endp



;_________________________________________________________________________________________
;
; Geometry methods.


method TWindow.SetX
begin
        push    eax
        mov     eax, [.self]
        exec    eax, TWindow:Move, [.value], [eax+TWindow._y]
        pop     eax
        return
endp



method TWindow.SetY
begin
        push    eax
        mov     eax, [.self]
        exec    eax, TWindow:Move, [eax+TWindow._x], [.value]
        pop     eax
        return
endp



method TWindow.SetWidth
begin
        push    eax
        mov     eax, [.self]
        exec    eax, TWindow:Resize, [.value], [eax+TWindow._height]
        pop     eax
        return
endp



method TWindow.SetHeight
begin
        push    eax
        mov     eax, [.self]
        exec    eax, TWindow:Resize, [eax+TWindow._width], [.value]
        pop     eax
        return
endp




method TWindow.Move
begin
        pushad
        mov     esi, [.self]

        mov     ebx, [.newX]
        mov     ecx, [.newY]

        cmp     ebx, [esi+TWindow._x]
        jne     .update

        cmp     ecx, [esi+TWindow._y]
        je      .finish

.update:
        exec    esi, TWindow:RectChangedXY, [esi+TWindow._x], [esi+TWindow._y], [esi+TWindow._width], [esi+TWindow._height], TRUE        ; previous position.

        mov     [esi+TWindow._x], ebx
        mov     [esi+TWindow._y], ecx

        exec    esi, TWindow:RectChangedXY, [esi+TWindow._x], [esi+TWindow._y], [esi+TWindow._width], [esi+TWindow._height], TRUE        ; new position.

; here some system events have to be generated in order to repaint the parent window.

.finish:
        popad
        return
endp



method TWindow.Resize     ;, .newWidth, .newHeight
begin
        pushad
        mov     esi, [.self]

        mov     ebx, [.newWidth]
        mov     ecx, [.newHeight]

        cmp     ebx, [esi+TWindow._width]
        jne     .update

        cmp     ecx, [esi+TWindow._height]
        je      .finish

.update:
        exec    esi, TWindow:RectChangedXY, [esi+TWindow._x], [esi+TWindow._y], [esi+TWindow._width], [esi+TWindow._height], FALSE        ; previous rectangle

        mov     [esi+TWindow._width], ebx
        mov     [esi+TWindow._height], ecx

        exec    esi, TWindow:RectChangedXY, [esi+TWindow._x], [esi+TWindow._y], [esi+TWindow._width], [esi+TWindow._height], FALSE        ; new rectangle

        exec    esi, TWindow:__UpdateCanvas
        exec    esi, TWindow:Refresh

.finish:
        popad
        return
endp




method TWindow.EventMouseEnter
begin
        push    esi edx

        mov     esi, [.self]
        get     edx, esi, TWindow:OSWindow

        stdcall GetStockCursor, [esi+TWindow._cursor]
        stdcall SetMouseCursor, [edx+TWindow.handle], eax

        pop     edx esi
        return
endp




method TWindow.EventMouseLeave
begin

        return
endp




method TWindow.EventMouseMove   ;, .x, .y, .kbdState
begin

        return
endp




method TWindow.EventButtonPress        ;, .button, .kbdState, .x, .y
begin

        return
endp



method TWindow.EventButtonRelease      ;, .button, .kbdState, .x, .y
begin

        return
endp




method TWindow.EventScroll     ;, .direction, .command, .value
begin
        mov     eax, [.self]
        cmp     [eax+TWindow._OnScroll], 0
        je      .finish

        pushad
        stdcall [eax+TWindow._OnScroll], [.self], [.direction], [.command], [.value]
        popad

.finish:
        return
endp




method TWindow.CloseRequest     ;, .reason
begin

        return
endp




method TWindow.EventKeyPress    ;, .utf8, .scancode, .kbdState
begin
        mov     eax, [.self]
        cmp     [eax+TWindow._OnKeyPressed], 0
        je      .finish

        pushad
        stdcall [eax+TWindow._OnKeyPressed], [.self], [.utf8], [.scancode], [.kbdState]
        popad

.finish:
        return
endp



method TWindow.EventKeyRelease  ;, .kbdState
begin

        return
endp



method TWindow.EventFocusIn
begin
        exec    [.self], TWindow:Refresh
        return
endp



method TWindow.EventFocusOut
begin
        exec    [.self], TWindow:Refresh
        return
endp




; Utility procedures.


method  TWindow.ClientToScreenXY; , .window, .x, .y
begin
        push    eax esi

        mov     ecx, [.x]
        mov     edx, [.y]
        mov     esi, [.self]

.loop:
        get     eax, esi, TWindow:Parent
        test    eax, eax
        jz      .os_win

        add     ecx, [esi+TWindow._x]
        add     edx, [esi+TWindow._y]

        mov     esi, eax
        jmp     .loop

.os_win:
        mov     eax, [esi+TWindow.handle]
        test    eax, eax
        jz      .finish


        stdcall _ClientToScreen, eax, ecx, edx

.finish:
        pop     esi eax
        return
endp



method TWindow.UpdateAction
begin
        exec    [.self], TWindow:Refresh
        return
endp



endmodule

