iglobal
  xGrid           dd 8
  yGrid           dd 8
  fGridVisible    dd TRUE
  fSnapToGrid     dd TRUE
  fPartialSelect  dd TRUE
endg


;-----------------------------------------------------------------------
; Window procedure of the subclassed designtime window.
; It make window to behave properly in visual editor - selecting, moving
; resize etc.
; Also, it inhibits the main functionality of the controls.
;------------------------------------------------------------------------
proc procDesignControl, .hwnd, .wmsg, .wparam, .lparam
.rect RECT
.rect0 RECT
.pnt    POINT
.pnt0   POINT
begin
        invoke  GetPropW, [.hwnd], [propTemplateDT]
        mov     edi, eax
        test    eax, eax
        jz      .ondefault

        mov     ebx, [.wmsg]
        dispatch ebx

.ondefault:
        stc
        return

;-----------------------------------------------------------------
oncase WM_NCHITTEST

        invoke  GetPropW, [.hwnd], [propOldWinProc]
        test    eax, eax
        jz      @f

        invoke  CallWindowProcW, eax, [.hwnd], [.wmsg], [.wparam], [.lparam]
        cmp     eax, -1
        jne     @f

        mov     eax, HTCLIENT
@@:
        clc
        return

;-----------------------------------------------------------------
;oncase WM_MOUSEACTIVATE
;        mov     eax, MA_NOACTIVATE
;        clc
;        return

;-----------------------------------------------------------------
oncase WM_ACTIVATE
        mov     ebx, [edi+TWinTemplateDT.ptrFile]
        test    ebx, ebx
        jz      .endactivate

        stdcall SetCurrentFile, ebx

        invoke  SetPropW, [ebx+TOpenFile.hEditor], [propVisualMode], 1

.endactivate:
        mov     eax, edi
        stdcall GetSizer
        invoke  SetFocus, eax
        xor     eax, eax
        clc
        return

;------------------------------------------------------------------
oncase WM_SETFOCUS
        mov     eax, edi
        stdcall GetSizer
        invoke  SetFocus, eax
        xor     eax,eax
        clc
        return

;-------------------------------------------------------------------
oncase WM_ERASEBKGND
        lea     ebx, [.rect]
        invoke  GetClientRect, [.hwnd], ebx

        invoke  GetClassLongW, [.hwnd], GCL_HBRBACKGROUND
        test    eax,eax
        jnz     .brushok

        invoke  GetSysColorBrush, COLOR_BTNFACE

.brushok:
        invoke  FillRect, [.wparam], ebx, eax

        invoke  GetWindowLongW, [.hwnd], GWL_STYLE
        test    eax,WS_CHILD
        jnz     .ondefault

        cmp     [fGridVisible], FALSE
        je      .enderase

        invoke  CreatePen, PS_SOLID, 0, $00a0a0a0
        invoke  SelectObject, [.wparam], eax
        push    eax

        xor     edi, edi
.loopx:
        invoke  MoveToEx, [.wparam], edi, 0, NULL
        invoke  LineTo,   [.wparam], edi, [.rect.bottom]

        add     edi, [xGrid]
        cmp     edi, [.rect.right]
        jbe     .loopx

        xor     edi, edi
.loopy:
        invoke  MoveToEx, [.wparam], 0, edi, NULL
        invoke  LineTo,   [.wparam], [.rect.right], edi

        add     edi, [yGrid]
        cmp     edi, [.rect.bottom]
        jbe     .loopy

        invoke  SelectObject, [.wparam]
        invoke  DeleteObject, eax

.enderase:
        xor     eax,eax
        clc
        return


;-------------------------------------------------------------------
oncase WM_SETCURSOR

        invoke  LoadCursorW, NULL, IDC_ARROW
        invoke  SetCursor,eax
        xor     eax,eax
        clc
        return

;---------------------------------------------------------------------------
oncase WM_WINDOWPOSCHANGED
locals
  .upd dd ?
endl

        mov     ebx, [.lparam]
        mov     eax, [ebx+WINDOWPOS.flags]
        and     eax, SWP_NOSIZE or SWP_NOMOVE
        cmp     eax, SWP_NOSIZE or SWP_NOMOVE
        je      .endposchanged

        mov     [.upd], 0

        test    eax, SWP_NOSIZE
        jnz     .sizeok

        mov     eax, [ebx+WINDOWPOS.cx]
        mov     edx, [ebx+WINDOWPOS.cy]
        xchg    [edi+TWinTemplateDT.width], ax
        xchg    [edi+TWinTemplateDT.height], dx

        sub     ax, [edi+TWinTemplateDT.width]
        sub     dx, [edi+TWinTemplateDT.height]

        or      ax, dx
        jz      .sizeok

        or      [.upd], upSize

.sizeok:
        test    eax, SWP_NOMOVE
        jnz     .posok

        mov     eax, [ebx+WINDOWPOS.x]
        mov     edx, [ebx+WINDOWPOS.y]
        xchg    [edi+TWinTemplateDT.left], ax
        xchg    [edi+TWinTemplateDT.top], dx

        sub     ax, [edi+TWinTemplateDT.left]
        sub     dx, [edi+TWinTemplateDT.top]
        or      ax, dx
        jz      .posok

        or      [.upd], upPosition

.posok:
        invoke  PostMessageW, [hPropEditor], PRM_UPDATEPROP, edi, [.upd]

.updatesizer:
        mov     eax, edi
        stdcall GetSizer
        invoke  SendMessageW, eax, SZM_UPDATESIZE, 0, 0

; Mark control as modified...
        mov    eax, edi
.search:
        cmp     [eax+TWinTemplateDT.parent], 0
        je      .found
        mov     eax, [eax+TWinTemplateDT.parent]
        jmp     .search

.found:
        cmp     [.upd], 0
        je      .endposchanged

        mov     eax, [eax+TWinTemplateDT.ptrFile]
        invoke  SendMessageW, [eax+TOpenFile.hEditor], _CEM_SETMODIFIED, TRUE, 0

.endposchanged:
        xor     eax, eax
        clc
        return

.quitsize:
        xor     eax, eax
        clc
        return

;-------------------------------------------------------------------
oncase WM_CLOSE
        invoke  SetFocus, [hEditorsHost]
        invoke  ShowWindow, [.hwnd], SW_HIDE

        xor     eax, eax
        clc
        return

;-------------------------------------------------------------------
oncase WM_LBUTTONDOWN
  locals
    .cursorpos POINT
    .tool      dd ?
  endl
        stdcall GetCurrentTool
        mov     [.tool], eax
        test    eax, eax
        jnz     .selectrect

        test    [.wparam], MK_SHIFT
        jnz     .selectrect

        mov     esi, [edi+TWinTemplateDT.parent]
        test    esi, esi
        jz      .topselectrect

        mov     ecx, 1
        test    [.wparam], MK_CONTROL
        jnz     @f
        dec     ecx
@@:
; select one control
        mov     eax, esi
        stdcall GetSizer
        mov     esi, eax
        invoke  SendMessageW, esi, SZM_SELECTCONTROL, edi, ecx

        test    [.wparam], MK_CONTROL
        jnz     .endsingleselect

        lea     ebx, [.cursorpos]
        invoke  GetCursorPos, ebx
        invoke  ScreenToClient, esi, ebx
        mov     ax, word [.cursorpos.y]
        shl     eax, 16
        mov     ax, word [.cursorpos.x]

        invoke  SendMessageW, esi, WM_LBUTTONDOWN, [.wparam], eax

.endsingleselect:
        xor     eax, eax
        clc
        return

.topselectrect:
        invoke  SendMessageW, [hPropEditor], PEM_SETCONTROL, edi, 0

.selectrect:
        test    [.wparam], MK_CONTROL
        jnz     @f
        mov     eax, edi
        stdcall GetSizer
        invoke  SendMessageW, eax, SZM_UNSELECTALL, 0, 0
@@:
; search window to get capture...
.parentloop:
        test    [edi+TWinTemplateDT.styleEx], WS_EX_CONTROLPARENT
        jnz     .parentok

        cmp     [edi+TWinTemplateDT.parent], 0
        je      .parentok

        mov     edi, [edi+TWinTemplateDT.parent]
        jmp     .parentloop

.parentok:
        mov     eax, [edi+TWinTemplateDT.hwnd]
        mov     [.hwnd], eax
        invoke  SetCapture, [.hwnd]

        lea     esi,[.rect]
        invoke  GetClientRect, [.hwnd], esi
        invoke  ClientToScreen, [.hwnd], esi
        add     esi,8
        invoke  ClientToScreen, [.hwnd], esi
        sub     esi,8
        invoke  ClipCursor, esi

        invoke  GetCursorPos, esi
        invoke  ScreenToClient, [.hwnd], esi

        mov     ebx, [esi+POINT.x]
        mov     ecx, [esi+POINT.y]

        cmp     [.tool], 0
        je      .snapok

        stdcall RoundToGrid, ebx, [xGrid]
        mov     ebx, eax
        stdcall RoundToGrid, ecx, [yGrid]
        mov     ecx, eax

.snapok:
        mov     [edi+TWinTemplateDT.DragRect.left],ebx
        mov     [edi+TWinTemplateDT.DragRect.top],ecx
        inc     ebx
        inc     ecx
        mov     [edi+TWinTemplateDT.DragRect.right],ebx
        mov     [edi+TWinTemplateDT.DragRect.bottom],ecx

        xor     eax, eax
        clc
        return


;-------------------------------------------------------------------
oncase WM_MOUSEMOVE

        invoke  GetCapture
        cmp     eax,[.hwnd]
        jne     .endmove

; Draw select rect
        invoke  GetDCEx, [.hwnd], NULL, DCX_PARENTCLIP
        mov     ebx, eax

        lea     esi, [edi+TWinTemplateDT.DragRect]
        stdcall DrawSortedFocusRect, ebx, esi

        stdcall GetCurrentTool

        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]

        test    eax, eax
        jz      .movesnapok

        stdcall RoundToGrid, ecx, [xGrid]
        mov     ecx, eax
        stdcall RoundToGrid, edx, [yGrid]
        mov     edx, eax

.movesnapok:
        mov     [edi+TWinTemplateDT.DragRect.right], ecx
        mov     [edi+TWinTemplateDT.DragRect.bottom], edx
        stdcall DrawSortedFocusRect, ebx, esi

        invoke  ReleaseDC, [.hwnd], ebx

.endmove:
        xor     eax, eax
        clc
        return

;-------------------------------------------------------------------
oncase WM_LBUTTONUP

        invoke  GetCapture
        cmp     eax,[.hwnd]
        jne     .endbuttonup

        invoke  ReleaseCapture
        invoke  ClipCursor,NULL

; select rect or create rectangle.
        invoke  GetDCEx, [.hwnd], NULL, DCX_PARENTCLIP
        push    eax
        lea     ebx, [edi+TWinTemplateDT.DragRect]
        stdcall DrawSortedFocusRect, eax, ebx                           ; remove the last select rect.
        invoke  ReleaseDC, [.hwnd] ;, from the stack.

        stdcall GetCurrentTool
        test    eax, eax
        jz      .select

        mov     esi, eax

        invoke  SendMessageW, edx, TB_CHECKBUTTON, 0, TRUE

        stdcall CreateControlFromTool, esi, edi, ebx          ;CreateControlFromTool, Tool, Parent, ptrRect
        test    eax,eax
        jnz     .endbuttonup

.select:
        xor     ecx, ecx
        test    [.wparam], MK_CONTROL
        jz      @f
        inc     ecx
@@:
        stdcall SelectInDragRect, edi, ecx

.endbuttonup:
        xor     eax,eax
        clc
        return

        enddispatch
endp



;***************************************************
proc SelectInDragRect, .ptrParent, .fDontClear

.select RECT
.rect   RECT
.inter  RECT
.hSizer  dd ?

begin
        push    edi ebx esi

        mov     edi, [.ptrParent]
        test    edi, edi
        jz      .endchildren

        mov     eax, edi
        stdcall GetSizer
        mov     [.hSizer], eax

        lea     ecx, [edi+TWinTemplateDT.DragRect]
        lea     esi, [.select]
        invoke  CopyRect, esi, ecx
        stdcall SortRect

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

.searchchildren:
        dec     ebx
        js      .endchildren

        mov     edi, [esi+4*ebx]
        movsx   ecx, [edi+TWinTemplateDT.left]
        movsx   edx, [edi+TWinTemplateDT.top]
        mov     [.rect.left], ecx
        mov     [.rect.top], edx
        movsx   ecx, [edi+TWinTemplateDT.width]
        movsx   edx, [edi+TWinTemplateDT.height]
        add     ecx, [.rect.left]
        add     edx, [.rect.top]
        mov     [.rect.right], ecx
        mov     [.rect.bottom], edx

        lea     ecx, [.rect]
        lea     edx, [.inter]
        lea     eax, [.select]
        invoke  IntersectRect, edx, ecx, eax

        cmp     [fPartialSelect], FALSE
        jne     .partial

        lea     edx, [.inter]
        lea     ecx, [.rect]
        invoke  EqualRect, ecx, edx
        test    eax,eax
        jnz     .selectit
        jz      .searchchildren

.partial:
        lea     edx, [.inter]
        invoke  IsRectEmpty, edx
        test    eax,eax
        jnz     .searchchildren

.selectit:
        invoke  SendMessageW, [.hSizer], SZM_SELECTCONTROL, edi, [.fDontClear]
        mov     [.fDontClear], 1

        jmp     .searchchildren

.endchildren:
        pop     esi ebx edi
        return
endp


;***************************************************
; Draws focus rectangle on the given DC
; even if the rectangle is not sorted.
;***************************************************
proc DrawSortedFocusRect, .hDC, .ptrRect

.rect   RECT

        begin
        push    esi

        lea     esi, [.rect]
        invoke  CopyRect, esi, [.ptrRect]
        call    SortRect
        invoke  DrawFocusRect, [.hDC], esi

        pop     esi
        return
endp


;***************************************************
; Creates new control depending of the current
; selected tool.
;***************************************************
proc CreateControlFromTool, .Tool, .Parent, .ptrRect
begin
        push    esi edi ebx

; Creates empty TWinTemplate and attaches it to the parents window tree.
        stdcall GetMem, sizeof.TWinTemplateDT
        mov     edi, eax

        stdcall CreateArray, 4
        mov     [edi+TWinTemplateDT.children], eax

        mov     ecx, [.Parent]
        mov     [edi+TWinTemplateDT.parent], ecx

        stdcall AddArrayItems, [ecx+TWinTemplateDT.children], 1
        mov     [ecx+TWinTemplateDT.children], edx
        mov     [eax], edi

        mov     ebx, [.Tool]
        mov     esi, [.ptrRect]
        call    SortRect

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

        cmp     eax, 8
        jge     @f
        mov     eax, [ebx+TDesignTimeInfo.DefaultWidth]
@@:
        cmp     ecx, 8
        jge     @f
        mov     ecx, [ebx+TDesignTimeInfo.DefaultHeight]
@@:
        mov     [edi+TWinTemplateDT.width], ax
        mov     [edi+TWinTemplateDT.height], cx

        mov     eax, [esi+RECT.left]
        mov     ecx, [esi+RECT.top]
        mov     [edi+TWinTemplateDT.left], ax
        mov     [edi+TWinTemplateDT.top], cx

        mov     eax, [ebx+TDesignTimeInfo.DefaultStyle]
        mov     ecx, [ebx+TDesignTimeInfo.DefaultStyleEx]
        or      eax, WS_VISIBLE
        mov     [edi+TWinTemplateDT.style], eax
        mov     [edi+TWinTemplateDT.styleEx], ecx

        stdcall StrDup, [ebx+TDesignTimeInfo.ClassName]
        mov     [edi+TWinTemplateDT.ptrClass], eax

        stdcall StrDup, [ebx+TDesignTimeInfo.DefaultText]
        mov     [edi+TWinTemplateDT.ptrText], eax

        stdcall StrDup, cFormNameNone
        mov     [edi+TWinTemplateDT.hName], eax

        stdcall StrNew
        mov     [edi+TWinTemplateDT.WinProc], eax

        stdcall CreateDesignWindow, edi

.qtrue:
        xor     eax,eax
        dec     eax
.finish:
        pop     ebx edi esi
        return
endp


cDefaultFormStyleEx = 0
cDefaultFormStyle = WS_VISIBLE or WS_SYSMENU
cDefaultX         = 220
cDefaultY         = 220
cDefaultWidth     = 320
cDefaultHeight    = 240
cDefaultFontSize  = 8
cDefaultFontWeight  = FW_NORMAL
cDefaultFontItalic  = FALSE
cFormDefaultFont text 'MS Sans Serif'



;---------------------------------------------------------------------
; Fully destroys control and frees all allocated
; memory.
; Arguments:
;   ptrWinTemplateDT - pointer to the TWinTemplateDT structure of the
; control.
;---------------------------------------------------------------------
proc DestroyDesignWindow, .ptrWinTemplateDT, .detach
begin
        push    esi edi ebx

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

        mov     edi, [esi+TWinTemplateDT.children]
        mov     ebx, [edi+TArray.count]

; Loop to destroy all children.
.delchildren:
        dec     ebx
        js      .endchildren

        stdcall DestroyDesignWindow, [edi+4*ebx+TArray.array], FALSE
        jmp     .delchildren

.endchildren:
        stdcall FreeMem, edi

        cmp     [.detach], FALSE
        je      .parentok

; detach from parent window

        mov     edi, [esi+TWinTemplateDT.parent]
        test    edi, edi
        jz      .parentok

        stdcall ListIndexOf, [edi+TWinTemplateDT.children], esi
        jc      .parentok

        stdcall DeleteArrayItems, [edi+TWinTemplateDT.children], eax, 1
        mov     [edi+TWinTemplateDT.children], edx

.parentok:

; Destroy strings used in design time.

        stdcall StrDel, [esi+TWinTemplateDT.hName]
        stdcall StrDel, [esi+TWinTemplateDT.ptrClass]
        stdcall StrDel, [esi+TWinTemplateDT.ptrText]
        stdcall StrDel, [esi+TWinTemplateDT.WinProc]

; Destroy windows instances.
        invoke  DestroyWindow, [esi+TWinTemplateDT.hSizer]
        invoke  DestroyWindow, [esi+TWinTemplateDT.hwnd]

; Remove the control from property editor.
        invoke  SendMessageW, [hPropEditor], PEM_GETCONTROL, 0, 0
        cmp     eax, esi
        jne     .propeditok

        invoke  SendMessageW, [hPropEditor], PEM_SETCONTROL, [esi+TWinTemplateDT.parent], 0

.propeditok:

; Destroy TWinTemplateDT structure.
        stdcall FreeMem, esi

.finish:
        pop     ebx edi esi
        return
endp


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

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



proc RoundToGrid, .coordinate, .step
begin
        push    ecx edx

        mov     eax, [.coordinate]

        cmp     [fSnapToGrid], FALSE
        je      .quit

        cdq
        mov     ecx, [.step]
        idiv    ecx

        sar     ecx,1
        cmp     edx, ecx
        jl      @f
        inc     eax
@@:
        imul    eax, [.step]

.quit:
        pop     edx ecx
        return
endp

