; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: TFreshEdit object class; Advanced visual programming editor.
;
;  Target OS: Any
;
;  Dependencies:  strlib, memory, arrays, GUI
;
;  Notes:
;    FreshEdit features:
;    - Unicode (UTF-8)
;    - Word wrap
;    - Code folding
;    - Associated debug information
;    - save with format information (bookmarks and breakpoints)
;    - Read-only lines
;    - Undo and Redo functions.
;    - Adjustable colors, keyboard and behavior.
;    - Char, Block and Line selections.
;    - Syntax highlighters interface for different languages.
;    - Interface to visual programming tools and auto source generators.
;_________________________________________________________________________________________
;include "FreshEditThemes.asm"

include "themes/classic.asm"

module "TFreshEdit library"

; Line flags

lfBookmark   = 1        ; the line have bookmark set.
lfFocused    = 2        ; the line is focused until the user press some key.
lfProtected  = 4        ; the line can not be edited.
lfWordWrap   = 8        ; the line must be word wrapped. After changes, the format have to be updated.
lfBreakpoint = 16       ; set breakpoint in debug mode.
lfFoldHeader = 32       ; this is the first line of foldable part of the text.


struct TEditorLine
  .Data         dd  ?   ; pointer to line data.
  .flags        dd  ?   ; line flags

  .syn_context  dd  ?   ; flags about current syntax highlighter context.
  .debugdata    dd  ?   ; pointer to debug data, associated to this line
  .fold_level   dd  ?   ; the level of folding.

  .width        dd  ?   ; width of the line text in chars. It is the length of the string for non wrapped lines and maximal subline for wrapped.
  align 32
  .shift = 5
ends


undoInsertText     = 0
undoDeleteText     = 1


struct TUndoInfo
  .operation   db ?       ; what was changed???
  .selMode     db ?
  align 4
  .caretX      dd ?
  .caretY      dd ?
  .selectionX  dd ?
  .selectionY  dd ?
  .data        dd ?
  align 32
  .shift = 5
ends

; About TEditorLine.syn_context:
;
; This context flags depend on the current syntax highlighter active.
; The purpose of the context is to follow syntax constructions that
; spreads on several lines. Such as multiline comments, markup tags, etc.
; Also, some of the flags could be used for other purposes - code folding
; is one of the options.



; Flags that indicates what is changed in the editor window after some operation
; Indicate what update should be processed

chgfNoClearSelection = 1
chgfNeedRefresh      = 2
chgfFinished         = 4

; insert modes

modeInsert = 0
modeOverwrite = 1

; selection modes

selmChar = 0
selmBlock = 1

; flag ReadOnly values
froReadWrite          = 0
froReadOnlyWithCaret  = 1
froReadOnlyNoCaret    = 2


object TFreshEdit, TScrollWindow
  ._Font            dd  ?   ; font of the editor.

; main editor font sizes
  ._fontheight      dd  ?   ; in pixels.
  ._fontwidth       dd  ?   ; in pixels.
  ._fontdescent     dd  ?   ; in pixels.

; system font sizes (used for additional texts and line numbers)
  ._fontheight2     dd  ?   ; in pixels.
  ._fontwidth2      dd  ?   ; in pixels.
  ._fontdescent2    dd  ?   ; in pixels.

  .__LeftMargin     dd  ?   ; width of the left margin field. Auto computed.
  .__NumberMargin   dd  ?   ; width of the field for the line number. Auto computed.
  ._ExtraFont       dd  ?   ;

  ._cols            dd  ?   ; width of the window in text columns.
  ._rows            dd  ?   ; height of the window in text rows.
                            ; must to be update on resize and on font change.

  ._pLines          dd  ?   ; pointer to TArray of TEditorLine items.
  ._pIndex          dd  ?   ; pointer to TArray of dword that contains numbers of TEditorLine for the screen.
  ._pLengths        dd  ?   ; pointer to TArray with counts of the lines with given length.

  ._pUndoList       dd  ?   ; pointer to TArray of TUndoInfo
  ._pRedoList       dd  ?   ; pointer to TArray of TUndoInfo

; view change fields

  ._TopLine         dd  ?   ; number of the line that is at the topmost line of the window.
  ._LeftColumn      dd  ?   ; the column of the text at the leftmost side of the window.

  ._xCaret          dd  ?   ; horizontal position of the caret inside the current line.
  ._yCaret          dd  ?   ; vertical position of the caret inside the text.

  ._xSelection      dd  ?
  ._ySelection      dd  ?

  ._fReadOnly       dd  ?
  ._SelMode         dd  ?
  ._InsMode         dd  ?
  ._TabStop         dd  ?

; end change fields

  ._prevTopLine     dd  ?
  ._prevLeftColumn  dd  ?

  ._prevxCaret      dd  ?
  ._prevyCaret      dd  ?

  ._prevxDelta      dd  ?
  ._prevyDelta      dd  ?

  ._prevSelMode     dd  ?
  ._prevInsMode     dd  ?

; end backup fields

  ._DragButton      dd  ?

  ._procSyntax      dd  ?       ; syntax highlighter procedure.

  ._iconBreakpointA dd  ?       ; active breakpoint icon.
  ._iconBreakpointI dd  ?       ; inactive breakpoint icon.
  ._iconDebugInfo   dd  ?
  ._maskBreakpointA dd  ?
  ._maskBreakpointI dd  ?
  ._maskDebugInfo   dd  ?


  ._iconUnfold      dd  ?
  ._iconFold        dd  ?

  ._maskUnfold      dd  ?

; Event handlers

  ._OnControlKey     dd  ?

; Parameters
  param .Selection, .GetSelection, .SetSelection      ; selected text read/write

  param .CurrentLine, .GetCurrentLine, NONE    ; TEditorLine where caret resides.
  param .Text, .GetText, .SetText
  param .MaxLineLen, .GetMaxLineLen, NONE

  param .SyntaxProc, ._procSyntax, ._procSyntax
  param .OnControlKey, ._OnControlKey, ._OnControlKey

; Methods

  method .Create
  method .Destroy

  method .Clear          ; clears the text in the editor.
  method .Refresh
  method ._RedrawShadow

; System events handlers

  method .Resize, .newWidth, .newHeight

  method .EventFocusIn
  method .EventFocusOut

  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 .SysEventHandler, .pEvent

; Methods handling the parameters.

  method .GetSelection
  method .SetSelection, .value
  method .GetCurrentLine
  method .GetText
  method .SetText, .value
  method .GetMaxLineLen

; navigation methods

  method .Move, .direction, .count, .force      ; moves the caret

  method .LineBegin
  method .LineEnd
  method .ScreenBegin
  method .ScreenEnd
  method .FileBegin
  method .FileEnd

; editing methods

  method .DeleteSel
  method .InsertLine, .index    ; index = -1 inserts at current position of the caret.
                                ; index = -2 appends the line at the end of the text.
  method .DeleteLine, .index    ; index = -1 deletes the line on the current position of the caret.

; block processing methods.

  method .DeleteTextRange, .indexFrom, .charFrom, .indexTo, .charTo
  method .GetTextRange, .indexFrom, .charFrom, .indexTo, .charTo

; Undo/Redo functions.

  method .RegisterForUndo, .operation, .data

  method .Undo
  method .Redo

  method .ClearUndo

; these two methods are flexible, but very slow. .Text parameter seting is fastest way to change whole text of the editor.
  method .InsertChar, .char
  method .InsertString, .hstring    ; inserts a string at the caret position.
                                    ; If the string contains formating chars they are processed properly.
  method .DelChar      ; deletes the char under the caret.

; appearance methods
  method .FormatLine, .index

; themes methods

;  method .LoadTheme, .configdb, .directory

endobj


; about .pIndex field:
;
;  This array contains the number of text lines that have to be displayed on the
;  respective screen lines.
;  Not every text line have screen line representation - it means the count of items of [pIntex]
;  can be smaller than count of items of [pLines]. This case is when some lines are folded and not visible.
;  On the other hand, another variant is also possible - several items of [pIndex] to point to
;  one line in [pLines] - when the text line is wraped and displayed on several lines of the screen.
;
;_________________________________________________________________________________________



method TFreshEdit.Create
begin
        inherited

        pushad

        mov     ebx, [.self]

        mov     [ebx+TFreshEdit._cursor], mcText

        stdcall GetFontMetrics
;        stdcall FontGetCharSize, [ebx+TFreshEdit.handle], [ebx+TFreshEdit._Font], __HeightProbeString
        test    edx, edx
        jnz     @f
        inc     edx
@@:
        mov     [ebx+TFreshEdit._fontheight], edx
        mov     [ebx+TFreshEdit._fontwidth], eax
        mov     [ebx+TFreshEdit._fontdescent], ecx

; System font settings.
;        stdcall FontGetCharSize, [ebx+TFreshEdit.handle], 0, __NumberProbeString
        mov     [ebx+TFreshEdit._fontheight2], edx
        mov     [ebx+TFreshEdit._fontwidth2], eax
        mov     [ebx+TFreshEdit._fontdescent2], ecx

; Init some values.
        mov     [ebx+TFreshEdit._TabStop], 8

        mov     [ebx+TFreshEdit._DragButton], -1

        stdcall CreateArray, sizeof.TEditorLine
        mov     [ebx+TFreshEdit._pLines], eax

        stdcall CreateArray, 4
        mov     [ebx+TFreshEdit._pIndex], eax

        stdcall CreateArray, 4
        mov     [ebx+TFreshEdit._pLengths], eax

        stdcall CreateArray, sizeof.TUndoInfo
        mov     [ebx+TFreshEdit._pUndoList], eax

        stdcall CreateArray, sizeof.TUndoInfo
        mov     [ebx+TFreshEdit._pRedoList], eax

        popad
        return
endp



;_________________________________________________________________________________________


method TFreshEdit.Destroy
begin
        push    ebx
        mov     ebx, [.self]

        exec    ebx, TFreshEdit:Clear
        stdcall FreeMem, [ebx+TFreshEdit._pLines]
        stdcall FreeMem, [ebx+TFreshEdit._pIndex]
        stdcall FreeMem, [ebx+TFreshEdit._pLengths]
        stdcall FreeMem, [ebx+TFreshEdit._pUndoList]
        stdcall FreeMem, [ebx+TFreshEdit._pRedoList]

        pop     ebx

        inherited
        return
endp


;_________________________________________________________________________________________


method TFreshEdit.GetMaxLineLen
begin
        mov     eax, [.self]
        mov     eax, [eax+TFreshEdit._pLengths]
        mov     eax, [eax+TArray.count]

        sub     eax, 1
        adc     eax, 0  ; decrement with saturation

        return
endp


;_________________________________________________________________________________________


method TFreshEdit.GetCurrentLine
begin
        push    ebx edx edi

        mov     edi, [.self]
        mov     eax, [edi+TFreshEdit._yCaret]
        mov     ebx, [edi+TFreshEdit._pIndex]
        cmp     eax, [ebx+TArray.count]
        jae     .errorcl

.lineloop:
        mov     edx, [ebx+TArray.array+4*eax]
        cmp     edx, -1
        jne     .found

        dec     eax
        jns     .lineloop

.errorcl:
        stc
        pop     edi edx ebx
        return

; edx is the number of string in the lines array.
.found:
        mov     ebx, [edi+TFreshEdit._pLines]
        cmp     edx, [ebx+TArray.count]
        jae     .errorcl

        shl     edx, TEditorLine.shift
        lea     eax, [ebx+TArray.array+edx]

        clc
        pop     edi edx ebx
        return
endp



;_________________________________________________________________________________________


method TFreshEdit.GetText
begin
        push    ebx ecx edx esi edi

        stdcall StrNew
        mov     ebx, eax

        mov     edi, [.self]

        mov     esi, [edi+TFreshEdit._pLines]
        mov     ecx, [esi+TArray.count]

        jecxz   .text_ready
        lea     esi, [esi+TArray.array]

.line_loop:
        stdcall TFreshEdit.__AddFormatLine, ebx, [esi+TEditorLine.flags]

        stdcall StrDup, [esi+TEditorLine.Data]
        push    eax
        push    eax
        push    eax
        stdcall StrLen, eax
        mov     edx, eax
        stdcall StrPtr ; from the stack

        test    edx, edx
        jz      .line_ready

.loop_char:
        cmp     byte [eax], 1
        jne     .next
        mov     byte [eax], $20
.next:
        inc     eax
        dec     edx
        jnz     .loop_char

.line_ready:
        stdcall StrCat, ebx ; from the stack.
        stdcall StrDel      ; from the stack
        stdcall StrCharCat, ebx, $0a0d

.next_line:
        add     esi, sizeof.TEditorLine
        loop    .line_loop

.text_ready:
        mov     eax, ebx
        pop     edi esi edx ecx ebx
        return
endp



;_________________________________________________________________________________________


method TFreshEdit.GetSelection
begin
        push    esi

        mov     esi, [.self]
        mov     eax, [esi+TFreshEdit._SelMode]
        cmp     eax, selmChar
        je      .get_sel_char
        cmp     eax, selmBlock
        je      .get_sel_block

        stc
        return

.get_sel_char:
        exec    esi, TFreshEdit:GetTextRange, [esi+TFreshEdit._yCaret],        \
                                              [esi+TFreshEdit._xCaret],        \
                                              [esi+TFreshEdit._ySelection],    \
                                              [esi+TFreshEdit._xSelection]

        pop     esi
        return


.get_sel_block:
        xor     eax, eax
        pop     esi
        return
endp



;_________________________________________________________________________________________


method TFreshEdit.SetText
  .pLine    dd ?
  .flags    dd ?
begin
        pushad

        mov     edi, [.self]
        exec    edi, TFreshEdit:Clear

        stdcall StrPtr, [.value]
        mov     esi, eax

.lineloop:
        mov     [.flags], 0

        stdcall TFreshEdit.__DecodeFormatLine, esi
        jc      .line_begin

        mov     [.flags], eax
        add     esi, ecx

.line_begin:
        push    esi

.char:
        lodsb
        test    al, al
        jz      .end_of_line

        cmp     al, $0a
        je      .end_of_line
        cmp     al, $0d
        jne     .char

.end_of_line:
        mov     ebx, esi
        sub     ebx, [esp]
        dec     ebx

        test    al, al
        jnz     @f
        dec     esi
        jmp     .lineok

@@:
        xor     al, $0a xor $0d
        cmp     [esi], al
        jne     .lineok
        inc     esi

.lineok:
        pop     eax

        push    ecx esi edi

        mov     ecx, ebx
        mov     esi, eax

        stdcall StrNew
        mov     ebx, eax
        stdcall StrSetCapacity, ebx, ecx
        mov     edi, eax

        mov     [edi+string.len], ecx

        rep movsb
        xor eax, eax    ; terminating zero - important for linux sometimes,
                        ; because the memory reallocation returns not zero filled memory.
        stosb

        pop     edi esi ecx

; insert text line.
        stdcall AddArrayItems, [edi+TFreshEdit._pLines], 1
        mov     [edi+TFreshEdit._pLines], edx
        mov     ecx, [edx+TArray.count]
        mov     [.pLine], eax

        mov     [eax+TEditorLine.Data], ebx
        push    [.flags]
        pop     [eax+TEditorLine.flags]
        mov     [eax+TEditorLine.syn_context], 0
        mov     [eax+TEditorLine.debugdata], 0
        mov     [eax+TEditorLine.width], -1

; insert index line.
        stdcall AddArrayItems, [edi+TFreshEdit._pIndex], 1
        mov     [edi+TFreshEdit._pIndex], edx

        dec     ecx
        mov     [eax], ecx

; format the line
        mov     ecx, [edx+TArray.count]
        dec     ecx
        exec    edi, TFreshEdit:FormatLine, ecx

; goto next line.
        cmp     byte [esi], 0
        jne     .lineloop

        mov     [edi+TFreshEdit._TopLine], 0
        mov     [edi+TFreshEdit._yCaret], 0

        mov     eax, [edi+TFreshEdit._pLengths]

        stdcall TFreshEdit.__UpdateScrollBars, edi

        popad
        return
endp


;_________________________________________________________________________________________


method TFreshEdit.SetSelection
begin
        push    eax edx esi

; delete the old selection

        mov     esi, [.self]

        exec    esi, TFreshEdit:DeleteSel
        test    eax, eax
        js      @f

        mov     [esi+TFreshEdit._yCaret], eax
        mov     [esi+TFreshEdit._xCaret], edx

@@:
; Insert the specified string on the current caret position.
        exec    esi, TFreshEdit:InsertString, [.value]
        pop     edx eax esi
        return
endp


;_________________________________________________________________________________________

; move commands
mvcMoveX   = 1
mvcMoveY   = 2
mvcScrollX = 4
mvcScrollY = 8


method TFreshEdit.Refresh
begin
        push    eax
        mov     eax, [.self]
        inherited
        pop     eax
        return
endp


; method TFreshEdit.Clear clears the text loaded in the editor.
;

method TFreshEdit.Clear
begin
        pushad

        mov     ebx, [.self]
        mov     ebx, [ebx+TFreshEdit._pLines]
        mov     ecx, [ebx+TArray.count]
        lea     ebx, [ebx+TArray.array]

.clrloop:
        jecxz   .endclr
        stdcall StrDel, [ebx+TEditorLine.Data]
        mov     [ebx+TEditorLine.Data], 0
        add     ebx, sizeof.TEditorLine
        dec     ecx
        jmp     .clrloop

.endclr:
        mov     ebx, [.self]
        stdcall FreeMem, [ebx+TFreshEdit._pIndex]
        stdcall FreeMem, [ebx+TFreshEdit._pLines]
        stdcall FreeMem, [ebx+TFreshEdit._pLengths]

        stdcall CreateArray, 4
        mov     [ebx+TFreshEdit._pIndex], eax

        stdcall CreateArray, sizeof.TEditorLine
        mov     [ebx+TFreshEdit._pLines], eax

        stdcall CreateArray, 4
        mov     [ebx+TFreshEdit._pLengths], eax

        xor     eax, eax

        mov     [ebx+TFreshEdit._xCaret], eax
        mov     [ebx+TFreshEdit._yCaret], eax
        mov     [ebx+TFreshEdit._xSelection], eax
        mov     [ebx+TFreshEdit._ySelection], eax

        exec    ebx, TFreshEdit:Refresh
        stdcall TFreshEdit.__UpdateScrollBars, ebx

        popad
        return
endp



;.........................................................................................
; The method TFreshEdit.Move moves the caret towards given direction and on
; the given count of steps.
;
method TFreshEdit.Move
begin
        pushad

        mov     esi, [.self]

        test    [.direction], mvcMoveX or mvcMoveY
        jz      .fitok

        cmp     [.force], 0
        jne     .fitok

        stdcall TFreshEdit.__FitCaretInWindow, esi

        test    eax, eax
        jnz     .endmove

.fitok:
        test    [.direction], mvcMoveX or mvcScrollX
        jnz     .move_horizontal

        test    [.direction], mvcMoveY or mvcScrollY
        jnz     .move_vertical

.endmove:
        popad
        return


.move_horizontal:
        test    [.direction], mvcMoveX
        jz      .xcaretok

        mov     ecx, [esi+TFreshEdit._xCaret]
        add     ecx, [.count]
        jns     @f
        xor     ecx, ecx
@@:
        mov     [esi+TFreshEdit._xCaret], ecx

.xcaretok:
        test    [.direction], mvcScrollX
        jz      .leftcolok

        mov     ecx, [esi+TFreshEdit._LeftColumn]
        add     ecx, [.count]
        jns     @f
        xor     ecx, ecx
@@:
        mov     [esi+TFreshEdit._LeftColumn], ecx

.leftcolok:

        mov     ecx, [esi+TFreshEdit._xCaret]
        mov     edx, [esi+TFreshEdit._LeftColumn]
        cmp     ecx, edx
        jb      .hscroll
        add     edx, [esi+TFreshEdit._cols]
        cmp     ecx, edx
        jb      .endmove

.hscroll:
        mov     ecx, [esi+TFreshEdit._cols]
        mov     eax, [.count]
        shr     ecx, 2
        cdq

        mov     eax, [esi+TFreshEdit._xCaret]
        sub     eax, ecx
        sub     eax, ecx
        sub     eax, ecx
        imul    edx, ecx
        sub     eax, edx
        sub     eax, edx
        jns     @f
        xor     eax, eax
@@:
        mov     [esi+TFreshEdit._LeftColumn], eax
        jmp     .endmove

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

.move_vertical:
        test    [.direction], mvcMoveY
        jz      .ycaretok

        mov     ecx, [esi+TFreshEdit._yCaret]
        add     ecx, [.count]
        jns     @f
        xor     ecx, ecx
@@:
        mov     [esi+TFreshEdit._yCaret], ecx

.ycaretok:
        test    [.direction], mvcScrollY
        jz      .toplineok

        mov     ecx, [esi+TFreshEdit._TopLine]
        add     ecx, [.count]
        jns     @f
        xor     ecx, ecx
@@:
        mov     edx, [esi+TFreshEdit._pIndex]
        mov     edx, [edx+TArray.count]
        sub     edx, [esi+TFreshEdit._rows]
        jns     @f
        xor     edx, edx
@@:
        cmp     ecx, edx
        jl      @f
        mov     ecx, edx
@@:
        mov     [esi+TFreshEdit._TopLine], ecx

.toplineok:

        mov     ecx, [esi+TFreshEdit._yCaret]
        mov     eax, [esi+TFreshEdit._pIndex]
        mov     eax, [eax+TArray.count]
        cmp     ecx, eax
        jb      @f
        lea     ecx, [eax-1]
@@:
        mov     [esi+TFreshEdit._yCaret], ecx

        mov     edx, [esi+TFreshEdit._TopLine]
        cmp     ecx, edx
        jb      .vscroll
        add     edx, [esi+TFreshEdit._rows]
        cmp     ecx, edx
        jb      .endmove

.vscroll:
        test    [.direction], mvcMoveX or mvcMoveY
        jz      .endmove                             ; it is only scroll, so don't care about caret.

; scrolls the view, in order to fit caret in window.
        mov     ecx, [esi+TFreshEdit._rows]
        mov     eax, [.count]
        neg     eax
        cdq
        mov     eax, [esi+TFreshEdit._rows]
        dec     eax
        imul    edx, eax

        mov     eax, [esi+TFreshEdit._yCaret]
        add     eax, edx
        jns     @f
        xor     eax, eax
@@:
        mov     [esi+TFreshEdit._TopLine], eax
        jmp     .endmove
endp


method TFreshEdit.LineBegin
begin
        push    eax
        stdcall TFreshEdit.__FitCaretInWindow, [.self]
        jnz     .endlb

        mov     eax, [.self]
        mov     [eax+TFreshEdit._xCaret], 0
        mov     [eax+TFreshEdit._LeftColumn], 0

.endlb:
        pop     eax
        return
endp



method  TFreshEdit.LineEnd
begin
        pushad
        mov     esi, [.self]

        stdcall TFreshEdit.__FitCaretInWindow, esi
        jnz     .endle

        mov     esi, [.self]
        mov     ecx, [esi+TFreshEdit._yCaret]
        mov     ebx, [esi+TFreshEdit._pIndex]

        stdcall TFreshEdit.__PosToPointer, esi, [esi+TFreshEdit._xCaret], [esi+TFreshEdit._yCaret]
        mov     ebx, eax

        stdcall StrPtr, [edx+TEditorLine.Data]
        mov     edi, eax

; search the end of the current line
.el_loop:
        stdcall DecodeUtf8, [edi+ebx]
        test    eax, eax
        jz      .el_found
        cmp     eax, $01
        je      .el_found

        add     ebx, edx
        jmp     .el_loop

.el_found:
        stdcall TFreshEdit.__PointerToPos, esi, [esi+TFreshEdit._yCaret], ebx
        mov     [esi+TFreshEdit._yCaret], eax
        mov     [esi+TFreshEdit._xCaret], edx

.endle:
        stdcall TFreshEdit.__FitCaretInWindow, esi
        popad
        return
endp



method TFreshEdit.ScreenBegin
begin

        return
endp



method TFreshEdit.ScreenEnd
begin

        return
endp


method TFreshEdit.FileBegin
begin

        return
endp


method TFreshEdit.FileEnd
begin

        return
endp



method TFreshEdit.RegisterForUndo
begin

        return
endp


method TFreshEdit.Undo
begin
        return
endp



method TFreshEdit.Redo
begin
        return
endp


method TFreshEdit.ClearUndo
begin

        return
endp

; delete the selection

method TFreshEdit.DeleteSel
begin
        push    esi

        mov     esi, [.self]
        mov     eax, [esi+TFreshEdit._SelMode]
        cmp     eax, selmChar
        je      .del_sel_char
        cmp     eax, selmBlock
        je      .del_sel_block

        pop     esi eax
        return

.del_sel_char:
        exec    esi, TFreshEdit:DeleteTextRange, [esi+TFreshEdit._yCaret],           \
                                                 [esi+TFreshEdit._xCaret],           \
                                                 [esi+TFreshEdit._ySelection],       \
                                                 [esi+TFreshEdit._xSelection]
        pop     esi
        return


.del_sel_block:
        pop     esi
        return
endp


method TFreshEdit.DeleteTextRange
  .final_str   dd ?
  .final_flags dd ?
  .final_level dd ?
  .fromline    dd ?
  .toline      dd ?
  .ret_str     dd ?
begin
        pushad

; sort the "from" and "To" in order to have "From" < "To"
        mov     eax, [.indexFrom]
        mov     ecx, [.charFrom]
        cmp     eax, [.indexTo]
        jne     @f
        cmp     ecx, [.charTo]
@@:
        je      .nothing_to_delete    ; from == to so nothing to delete.
        jb      @f
        xchg    eax, [.indexTo]
        xchg    ecx, [.charTo]
        mov     [.indexFrom], eax
        mov     [.charFrom], ecx
@@:

; last line processing
        stdcall TFreshEdit.__PosToPointer, [.self], [.charTo], [.indexTo]

        stdcall StrSplit, [edx+TEditorLine.Data], eax
        mov     [.final_str], eax

        mov     eax, [edx+TEditorLine.flags]
        mov     ecx, [edx+TEditorLine.fold_level]
        mov     [.final_flags], eax
        mov     [.final_level], ecx

; first line processing
        stdcall TFreshEdit.__PosToPointer, [.self], [.charFrom], [.indexFrom]
        mov     [.fromline], edx

        jecxz   .splitit
        add     eax, ecx
.addspc:
        stdcall StrCharCat, [edx+TEditorLine.Data], ' '
        dec     ecx
        jnz     .addspc

.splitit:
        stdcall StrSplit, [edx+TEditorLine.Data], eax
        stdcall StrDel, eax
        stdcall StrCat, [edx+TEditorLine.Data], [.final_str]
        stdcall StrDel, [.final_str]
        mov     eax, [.final_level]
        mov     ecx, [.final_flags]
        mov     [edx+TEditorLine.fold_level], eax
        or      [edx+TEditorLine.flags], ecx

; now delete all lines except the first.

        mov     edi, [.indexTo]

.del_loop:
        stdcall TFreshEdit.__PosToPointer, [.self], -1, edi
        cmp     edx, [.fromline]
        je      .delete_ok

        exec    [.self], TFreshEdit:DeleteLine, edi
        mov     edi, eax
        dec     edi
        jmp     .del_loop


.delete_ok:
        exec    [.self], TFreshEdit:FormatLine, [.indexFrom]

        clc
        popad
        mov     eax, [.indexFrom]
        mov     edx, [.charFrom]
        return

.nothing_to_delete:
        clc
        popad
        xor     eax, eax
        dec     eax
        return
endp




method TFreshEdit.GetTextRange
  .final_str   dd ?
  .fromline    dd ?
  .toline      dd ?
  .ret_str     dd ?
begin
        pushad
        mov     [.ret_str], 0

; sort the "from" and "To" in order to have "From" < "To"
        mov     eax, [.indexFrom]
        mov     ecx, [.charFrom]
        cmp     eax, [.indexTo]
        jne     @f
        cmp     ecx, [.charTo]
@@:
        je      .finish_get    ; from == to so nothing to return
        jb      @f
        xchg    eax, [.indexTo]
        xchg    ecx, [.charTo]
        mov     [.indexFrom], eax
        mov     [.charFrom], ecx
@@:

; last line processing
        stdcall TFreshEdit.__PosToPointer, [.self], [.charTo], [.indexTo]
        mov     [.toline], edx

        stdcall StrExtract, [edx+TEditorLine.Data], 0, eax
        mov     [.final_str], eax

; first line processing
        stdcall TFreshEdit.__PosToPointer, [.self], [.charFrom], [.indexFrom]
        mov     [.fromline], edx

        mov     edi, [edx+TEditorLine.Data]
        cmp     edx, [.toline]
        jne     .dup_first
        mov     edi, [.final_str]
.dup_first:
        push    eax
        stdcall StrDup, edi
        mov     edi, eax
        pop     eax

        jecxz   .get_splitit
        add     eax, ecx

.get_addspc:
        stdcall StrCharCat, edi, ' '
        dec     ecx
        jnz     .get_addspc

.get_splitit:
        stdcall StrSplit, edi, eax
        stdcall StrDel, edi
        mov     [.ret_str], eax

; now concatenate all lines to the first.

        mov     edi, [.toline]
        mov     esi, [.fromline]
        cmp     esi, edi
        je      .end_get2

.get_loop:
        add     esi, sizeof.TEditorLine
        cmp     esi, edi
        je      .end_get
        stdcall StrCharCat, [.ret_str], $0a0d
        stdcall StrCat, [.ret_str], [esi+TEditorLine.Data]
        jmp     .get_loop

.end_get:
        stdcall StrCharCat, [.ret_str], $0a0d
        stdcall StrCat, [.ret_str], [.final_str]

.end_get2:
        stdcall StrDel, [.final_str]

; here, format the string - remove word wrap characters if any...
        stdcall  StrPtr, [.ret_str]

.get_format:
        cmp     byte [eax], 0
        je      .finish_get

        cmp     byte [eax], $01      ; word wrap char
        jne     @f
        mov     byte [eax], $20
@@:
        inc     eax
        jmp     .get_format

.finish_get:
        clc
        popad
        mov     eax, [.ret_str]
        return
endp


method TFreshEdit.DeleteLine
begin
        push    edx esi edi
        mov     esi, [.self]

        mov     eax, [.index]
        cmp     eax, -1
        jne     @f
        mov     eax, [esi+TFreshEdit._yCaret]
@@:
        mov     edi, [esi+TFreshEdit._pIndex]
        cmp     eax, [edi+TArray.count]
        jae     .end_del_line

.beg_search:
        cmp     [edi+TArray.array+4*eax], -1    ; word wrap
        jne     .do_del
        dec     eax
        jnz     .beg_search

.do_del:
        mov     ecx, [edi+TArray.array+4*eax]
        shl     ecx, TEditorLine.shift
        add     ecx, TArray.array
        add     ecx, [esi+TFreshEdit._pLines]
        stdcall StrDel, [ecx+TEditorLine.Data]
        stdcall TFreshEdit.__FixLength, esi, -1, [ecx+TEditorLine.width]

        stdcall DeleteArrayItems, [esi+TFreshEdit._pLines], [edi+TArray.array+4*eax], 1
        mov     [esi+TFreshEdit._pLines], edx

.del_index2:
        stdcall DeleteArrayItems, edi, eax, 1
        mov     edi, edx
        cmp     eax, [edi+TArray.count]
        jae     .end_del_index2

        cmp     [edi+TArray.array+4*eax], -1
        je      .del_index2

.end_del_index2:
        mov     [esi+TFreshEdit._pIndex], edi
        push    eax

; fix the index to the end:
.fix_index_del:
        cmp     eax, [edi+TArray.count]
        jae     .end_fix_index

        cmp     [edi+TArray.array+4*eax], -1
        je      @f
        dec     [edi+TArray.array+4*eax]
@@:
        inc     eax
        jmp     .fix_index_del

.end_fix_index:
        pop     eax
.end_del_line:
        clc
        pop     edi esi edx
        return
endp



method TFreshEdit.InsertChar
begin
        push    ecx edx esi edi

        mov     esi, [.self]

.ins_loop:
        stdcall TFreshEdit.__PosToPointer, esi, [esi+TFreshEdit._xCaret], [esi+TFreshEdit._yCaret]
        jnc     .line_found

        exec    [.self], TFreshEdit:InsertLine, -2
        jnc     .ins_loop                               ; possible hang!

        xor     edx, edx
        jmp     .end_ins_char

.line_found:
        test    [edx+TEditorLine.flags], lfProtected
        jz      .not_protected

        lea     edx, [eax+ecx]
        jmp     .end_ins_char

.not_protected:
        cmp     [.char], $0d
        je      .split_here
        cmp     [.char], $0a
        je      .split_here

        jecxz   .spaces_ok

.addspc2:
        stdcall StrCharInsert, [edx+TEditorLine.Data], ' ', eax
        inc     eax
        loop    .addspc2

.spaces_ok:
.do_ins_char:
        stdcall StrCharInsert, [edx+TEditorLine.Data], [.char], eax
        mov     esi, eax
        stdcall DecodeUtf8, [.char]
        add     edx, esi
        jmp     .end_ins_char

.split_here:
        push    eax edx

        mov     eax, [esi+TFreshEdit._yCaret]
        inc     eax
        exec    [.self], TFreshEdit:InsertLine, eax
        mov     edi, eax

        pop     edx eax
        stdcall StrSplit, [edx+TEditorLine.Data], eax

        stdcall StrDel, [edi+TEditorLine.Data]
        mov     [edi+TEditorLine.Data], eax

        mov     eax, [esi+TFreshEdit._yCaret]
        inc     eax
        exec    esi, TFreshEdit:FormatLine, eax
        exec    esi, TFreshEdit:FormatLine, [esi+TFreshEdit._yCaret]

.split_ok:
        exec    esi, TFreshEdit:LineBegin
        exec    esi, TFreshEdit:Move, mvcMoveY, 1, TRUE
        xor     edx, edx

.end_ins_char:
        push    edx
        mov     esi, [.self]
        stdcall TFreshEdit.__PointerToPos, [.self], [esi+TFreshEdit._yCaret], edx
        mov     [esi+TFreshEdit._xCaret], edx
        mov     [esi+TFreshEdit._yCaret], eax
        pop     eax
        clc
        pop     edi esi edx ecx
        return
endp


method TFreshEdit.InsertString
begin
        push    ecx edx esi

        stdcall StrPtr, [.hstring]
        mov     esi, eax

.char_loop:
        stdcall DecodeUtf8, [esi]
        jc      .end_ins_str

        mov     eax, [esi]
        add     esi, edx

        lea     ecx, [8*edx]
        mov     edx, 1
        shl     edx, cl
        dec     edx
        and     eax, edx

        cmp     eax, 0
        je      .end_ins_str

        cmp     eax, $0d
        je      .ins_cr
        cmp     eax, $0a
        jne     .ins_it

.ins_cr:
        xor     al, $0d xor $0a
        cmp     al, [esi]
        je      .char_loop

.ins_it:
        exec    [.self], TFreshEdit:InsertChar, eax
        jmp     .char_loop

.end_ins_str:
        pop     esi edx ecx
        return
endp


method TFreshEdit.InsertLine
begin
        push    esi edi
        mov     esi, [.self]

; first where to search. The next line is OK, but if the line is wrapped...
        cmp     [.index], -1
        jne     @f
        mov     eax, [esi+TFreshEdit._yCaret]
        mov     [.index], eax
@@:

        mov     edi, [esi+TFreshEdit._pIndex]
        mov     edx, [.index]

        cmp     edx, [edi+TArray.count]
        jb      .search_begin

; insert as a last line
        mov     edx, [edi+TArray.count]
        mov     ecx, -1
        jmp     .ins_here

; if the line is wrapped, search for the begin.
.search_begin:
        mov     ecx, [edi+TArray.array+4*edx]
        cmp     ecx, -1                         ; wrapped line?
        jne     .ins_here
        dec     edx
        jns     .search_begin
        jmp     .err_ins_line

; ...insert index item.
; edx contains the index in _pIndex where the new line have to be inserted.
; ecx contains the index in _pLines where the new line have to be inserted.
.ins_here:
        push    edx
        stdcall InsertArrayItems, [esi+TFreshEdit._pIndex], edx, 1
        mov     [esi+TFreshEdit._pIndex], edx
        mov     edi, edx
        pop     edx
        jc      .err_ins_line

        cmp     ecx, -1
        jne     .storeline

        mov     ecx, [esi+TFreshEdit._pLines]
        mov     ecx, [ecx+TArray.count]

.storeline:
        mov     [eax], ecx

; ...then fix the index to the end.

        sub     edx, [edi+TArray.count]
        neg     edx
        dec     edx
        jle     .index_fixed

.inc_index:
        lea     eax, [eax+4]
        cmp     dword [eax], -1
        je      @f
        inc     dword [eax]
@@:
        dec     edx
        jnz     .inc_index


.index_fixed:
; then insert new line
        stdcall InsertArrayItems, [esi+TFreshEdit._pLines], ecx, 1
        mov     [esi+TFreshEdit._pLines], edx
        jc      .err_ins_line

        mov     edx, eax
        stdcall StrNew

        mov     [edx+TEditorLine.Data], eax
        xor     eax, eax
        mov     [edx+TEditorLine.flags], eax
        mov     [edx+TEditorLine.syn_context], eax
        mov     [edx+TEditorLine.debugdata], eax
        mov     [edx+TEditorLine.width], eax
        mov     eax, edx

        stdcall TFreshEdit.__FixLength, esi, 0, -1

        pop     edi esi
        clc
        return

.err_ins_line:
        pop     edi esi
        stc
        return
endp


; formats some line of the text, depending on the flags. Also, fix the index items for
; this line.

method TFreshEdit.FormatLine
  .imin  dd  ?
  .imax  dd  ?
  .start_count dd ?
  .rows        dd ?

  .caret_ofs dd ?
  .wrap_here dd ?

  .retval dd ?   ; correction count on tab-expansion
begin
; first get the line pointer

        push    ecx esi edi

        mov     esi, [.self]
        mov     edi, [esi+TFreshEdit._pIndex]

        mov     ecx, [.index]
        cmp     ecx, [edi+TArray.count]
        jae     .error_format

.loop_end:
        inc     ecx
        cmp     ecx, [edi+TArray.count]
        jae     .end_found
        cmp     [edi+TArray.array+4*ecx], -1
        je      .loop_end

.end_found:
        dec     ecx
        mov     [.imax], ecx

        mov     ecx, [.index]
        inc     ecx

.loop_start:
        dec     ecx
        js      .error_format

        cmp     [edi+TArray.array+4*ecx], -1
        je      .loop_start

        mov     [.imin], ecx
        mov     [.rows], 1    ; how many lines are withit the line

        mov     ecx, [edi+TArray.array+4*ecx]   ; index in _pLines array.
        shl     ecx, TEditorLine.shift
        add     ecx, [esi+TFreshEdit._pLines]
        lea     edi, [ecx+TArray.array]         ; edi points to TEditorLine structure.

; begin of formating
        stdcall StrClipSpacesR, [edi+TEditorLine.Data]
        stdcall ExpandTabs, [edi+TEditorLine.Data], [esi+TFreshEdit._TabStop]
        mov     [.retval], eax
        stdcall StrLen, [edi+TEditorLine.Data]
        mov     ecx, eax
        mov     [.start_count], eax
        jecxz   .wrap_ok

        stdcall StrPtr, [edi+TEditorLine.Data]
        mov     edx, eax

; first replace $01 with $20 (wrap char to space)
        push    edx ecx
.spc_loop:
        cmp     byte [edx], $01
        jne     @f
        mov     byte [edx], $20
@@:
        inc     edx
        loop    .spc_loop
        pop     ecx edx

; word wrap?
        test    [edi+TEditorLine.flags], lfWordWrap
        jz      .wrap_ok

        push    edi

        mov     edi, edx
.wrap_outer:
        mov     ecx, [ASM.WrapPos]
        inc     ecx
        mov     [.wrap_here], -1
        xor     edx, edx

.wrap_loop:
        add     edi, edx
        stdcall DecodeUtf8, [edi]
        test    eax, eax
        jz      .wrap_ready

        cmp     eax, ' '
        jne     @f
        mov     [.wrap_here], edi
@@:
        loop    .wrap_loop

        cmp     [.wrap_here], -1
        jne     .do_wrap

.forward:
        add     edi, edx
        stdcall DecodeUtf8, [edi]
        test    eax, eax
        jz      .wrap_ready

        cmp     eax, ' '
        jne     .forward

        mov     [.wrap_here], edi

.do_wrap:
        mov     edi, [.wrap_here]
        mov     byte [edi], $01         ; wrap the line.
        inc     edi
        inc     [.rows]
        jmp     .wrap_outer


.wrap_ready:
; ready, so fix the index.
        pop     edi

.wrap_ok:
        mov     eax, [.imin]
        inc     eax

        mov     ecx, [.imax]
        sub     ecx, [.imin]
        inc     ecx
        sub     ecx, [.rows]    ; ecx>0 means some rows from the index must be deleted
                                ; ecx<0 means some rows must be added.
                                ; ecx=0 means the index is ok
        jz      .index_ok
        js      .add_more_index

        stdcall DeleteArrayItems, [esi+TFreshEdit._pIndex], eax, ecx
        mov     [esi+TFreshEdit._pIndex], edx
        jmp     .index_ok

.add_more_index:
        neg     ecx
        stdcall InsertArrayItems, [esi+TFreshEdit._pIndex], eax, ecx
        mov     [esi+TFreshEdit._pIndex], edx

.fill_new_items:
        mov     dword [eax], -1
        lea     eax, [eax+4]
        loop    .fill_new_items

.index_ok:
        stdcall TFreshEdit.__LineWidth, [edi+TEditorLine.Data]
        xchg    eax, [edi+TEditorLine.width]
        stdcall TFreshEdit.__FixLength, esi, [edi+TEditorLine.width], eax

        clc
        mov     eax, [.retval]
        mov     edx, [.imin]
        pop     edi esi ecx
        return

.error_format:
        int3
        mov     eax, -1
        clc
        pop     edi esi ecx
        return
endp


method TFreshEdit.DelChar
  .del_char_line dd ?
  .del_line_index dd ?
  .byte_offs dd ?
begin
        pushad

        mov     esi, [.self]
        stdcall TFreshEdit.__PosToPointer, esi, [esi+TFreshEdit._xCaret], [esi+TFreshEdit._yCaret]
        jc      .end_del_char

        test    [edx+TEditorLine.flags], lfProtected
        jnz     .end_del_char

        jecxz   .do_del_char

.del_pad_spc:
        stdcall StrCharInsert, [edx+TEditorLine.Data], ' ', eax
        inc     eax
        loop    .del_pad_spc

.do_del_char:
        mov     [.byte_offs], eax
        mov     [.del_char_line], edx
        mov     ebx, eax
        stdcall StrLen, [edx+TEditorLine.Data]
        cmp     ebx, eax
        je      .join_next_line

        mov     ecx, eax
        sub     ecx, ebx

        stdcall StrPtr, [edx+TEditorLine.Data]
        mov     edi, eax
        stdcall DecodeUtf8, [edi+ebx]
        lea     esi, [edi+edx]
        sub     [edi+string.len], edx
        add     esi, ebx
        add     edi, ebx
        cld
        rep movsb

.end_del_char:
        popad
        mov     eax, [.byte_offs]
        clc
        return

.join_next_line:
        mov     eax, [.del_char_line]
        mov     edx, [esi+TFreshEdit._pLines]
        sub     eax, edx
        sub     eax, TArray.array
        shr     eax, TEditorLine.shift
        inc     eax
        cmp     eax, [edx+TArray.count]
        jae     .end_del_char

        mov     [.del_line_index], eax

        mov     edx, [.del_char_line]
        mov     ebx, edx
        add     edx, sizeof.TEditorLine
        test    [edx+TEditorLine.flags], lfProtected
        jz      .do_join

        stdcall StrLen, [ebx+TEditorLine.Data]
        test    eax, eax
        jnz     .end_del_char

.do_join:
        stdcall StrCat, [ebx+TEditorLine.Data], [edx+TEditorLine.Data]
        mov     eax, [edx+TEditorLine.flags]
        or      [ebx+TEditorLine.flags], eax

; now delete the line
        stdcall TFreshEdit.__FixLength, esi, -1, [edx+TEditorLine.width]
        stdcall StrDel, [edx+TEditorLine.Data]
        stdcall DeleteArrayItems, [esi+TFreshEdit._pLines], [.del_line_index], 1
        mov     [esi+TFreshEdit._pLines], edx

; then fix the index.
        mov     edi, [esi+TFreshEdit._pIndex]
        mov     eax, [esi+TFreshEdit._yCaret]
        mov     edx, [.del_line_index]
        inc     eax

.char_fix_index:
        cmp     eax, [edi+TArray.count]
        jae     .end_del_char

        cmp     [edi+TArray.array+4*eax], -1
        je      .next_line_fix
        cmp     [edi+TArray.array+4*eax], edx
        je      .del_index_elements

        dec     [edi+TArray.array+4*eax]

.next_line_fix:
        inc     eax
        jmp     .char_fix_index

.del_index_elements:
        mov     ecx, eax

.char_scan_loop:
        inc     ecx
        cmp     ecx, [edi+TArray.count]
        jae     .end_char_scan
        cmp     [edi+TArray.array+4*ecx], -1
        je      .char_scan_loop

.end_char_scan:
        sub     ecx, eax
        stdcall DeleteArrayItems, edi, eax, ecx
        mov     [esi+TFreshEdit._pIndex], edx
        mov     edi, edx
        mov     edx, [.del_line_index]
        jmp     .char_fix_index
endp



method TFreshEdit._RedrawShadow
begin
        stdcall TFreshEdit.__DrawShadow, [.self]
        inherited
        return
endp


;_________________________________________________________________________________________


; SYSTEM EVENTS HANDLERS.



method TFreshEdit.Resize
begin
        inherited
        pushad

        mov     esi, [.self]
        xor     edx, edx

        cmp     [esi+TFreshEdit._fontwidth], edx
        je      .finish
        cmp     [esi+TFreshEdit._fontheight], edx
        je      .finish

        stdcall TFreshEdit.__ComputeLeftMargin, esi

        mov     eax, [esi+TFreshEdit._width]
        sub     eax, [esi+TFreshEdit.__LeftMargin]

        xor     edx, edx
        div     [esi+TFreshEdit._fontwidth]
        mov     [esi+TFreshEdit._cols], eax

        mov     eax, [esi+TFreshEdit._height]

        xor     edx, edx
        div     [esi+TFreshEdit._fontheight]
        mov     [esi+TFreshEdit._rows], eax

        stdcall TFreshEdit.__UpdateScrollBars, esi

.finish:
        popad
        return
endp



method TFreshEdit.EventKeyPress
begin
;        pushad
;
;        mov     esi, [.self]
;
;        cmp     [.utf8], $0d
;        je      .insert_it
;
;        cmp     [.utf8], $09
;        je      .insert_it
;
;        cmp     [.utf8], $20
;        jb      .control
;
;        cmp     [.scancode], $ff
;        ja      .control
;
;        test    [.kbdState], maskCtrl or maskAlt
;        jnz     .control
;
;.insert_it:
;; insert typed character
;locals
;  .byte_offs dd ?
;endl
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        exec    esi, TFreshEdit:DeleteSel
;        test    eax, eax
;        js      @f
;
;        mov     [esi+TFreshEdit._yCaret], eax
;        mov     [esi+TFreshEdit._xCaret], edx
;
;@@:
;        exec    esi, TFreshEdit:InsertChar, [ebx+TKeyboardEvent.key]
;        mov     [.byte_offs], eax
;
;        exec    esi, TFreshEdit:FormatLine, [esi+TFreshEdit._yCaret]
;        add     [.byte_offs], eax
;
;        stdcall TFreshEdit.__PointerToPos, esi, [esi+TFreshEdit._yCaret], [.byte_offs]
;
;        mov     [esi+TFreshEdit._yCaret], eax
;        mov     [esi+TFreshEdit._xCaret], edx
;
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        stdcall TFreshEdit.__FitCaretInWindow, esi
;
;        or     [.changes], chgfNeedRefresh
;        jmp    .refresh
;
;
;.control:
;
;        test    [ebx+TKeyboardEvent.kbdStatus], maskShift
;        jz      @f
;
;        or      [.changes], chgfNoClearSelection
;
;@@:
;
;; check the keyboard shortcuts
;        lea     edi, [FreshEditTheme.shortcuts]
;        xor     ecx, ecx
;
;.shortcut_loop:
;        mov     eax, [edi+TShortcut.key]
;        test    eax, eax
;        jz      .compare_scancode
;
;        cmp     eax, [ebx+TKeyboardEvent.key]
;        je      .found_shortcut
;
;.next_shortcut:
;        add     edi, sizeof.TShortcut
;        inc     ecx
;        cmp     ecx, TFETheme.shortcuts.count
;        je      .external_shortcut
;        jmp     .shortcut_loop
;
;.compare_scancode:
;
;        mov     eax, [edi+TShortcut.scancode]
;        mov     edx, [edi+TShortcut.maskStatus]
;        and     edx, [ebx+TKeyboardEvent.kbdStatus]
;        cmp     edx, [edi+TShortcut.kbdStatus]
;        jne     .next_shortcut
;
;        cmp     eax, [ebx+TKeyboardEvent.scancode]
;        jne     .next_shortcut
;
;.found_shortcut:
;
;        cmp     ecx, .shortcut_handlers.count
;        jae     .external_shortcut
;
;        movzx   ecx, word [.shortcut_handlers+2*ecx]
;        add     ecx, .shortcut_handlers
;        jmp     ecx
;
;.shortcut_handlers:
;
;        dw      .handler_up         - .shortcut_handlers
;        dw      .handler_down       - .shortcut_handlers
;        dw      .handler_left       - .shortcut_handlers
;        dw      .handler_right      - .shortcut_handlers
;        dw      .handler_pgup       - .shortcut_handlers
;        dw      .handler_pgdn       - .shortcut_handlers
;        dw      .handler_home       - .shortcut_handlers
;        dw      .handler_end        - .shortcut_handlers
;        dw      .handler_pgbeg      - .shortcut_handlers
;        dw      .handler_pgend      - .shortcut_handlers
;        dw      .handler_txtbeg     - .shortcut_handlers
;        dw      .handler_txtend     - .shortcut_handlers
;        dw      .handler_wordleft   - .shortcut_handlers
;        dw      .handler_wordright  - .shortcut_handlers
;        dw      .handler_delete     - .shortcut_handlers
;        dw      .handler_linedel    - .shortcut_handlers
;        dw      .handler_backspc    - .shortcut_handlers
;        dw      .handler_wordwrap   - .shortcut_handlers
;        dw      .handler_bookmark   - .shortcut_handlers
;        dw      .handler_insmode    - .shortcut_handlers
;        dw      .handler_selmode    - .shortcut_handlers
;        .shortcut_handlers.count = ($ - .shortcut_handlers)/2
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_up:
;        mov     eax, -1
;        mov     ecx, mvcMoveY
;        jmp     .moveit
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_down:
;        mov     eax, 1
;        mov     ecx, mvcMoveY
;        jmp     .moveit
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_left:
;        mov     eax, -1
;        mov     ecx, mvcMoveX
;        jmp     .moveit
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_right:
;        mov     eax, 1
;        mov     ecx, mvcMoveX
;        jmp     .moveit
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_pgup:
;        mov     eax, [esi+TFreshEdit._rows]
;        dec     eax
;        neg     eax
;        mov     ecx, mvcMoveY or mvcScrollY
;        jmp     .moveit
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_pgdn:
;        mov     eax, [esi+TFreshEdit._rows]
;        dec     eax
;        mov     ecx, mvcMoveY or mvcScrollY
;        jmp     .moveit
;
;
;.moveit:
;        test    [ebx+TKeyboardEvent.kbdStatus], maskScrLk
;        jnz     .scroll_override
;
;        cmp     [esi+TFreshEdit._fReadOnly], froReadOnlyNoCaret
;        jne     .scroll_override_ok
;
;.scroll_override:
;        test    ecx, mvcMoveX
;        jz      @f
;        or     ecx, mvcScrollX
;@@:
;        test    ecx, mvcMoveY
;        jz      @f
;        or      ecx, mvcScrollY
;@@:
;
;.scroll_override_ok:
;        exec    esi, TFreshEdit:Move, ecx, eax, FALSE
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_home:
;        exec    esi, TFreshEdit:LineBegin
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_end:
;        exec    esi, TFreshEdit:LineEnd
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_pgbeg:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_pgend:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_txtbeg:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_txtend:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_wordleft:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_wordright:
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;.handler_delete:
;locals
;  .byte_offs2 dd ?
;endl
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        exec    esi, TFreshEdit:DeleteSel
;        test    eax, eax
;        jns     .deleted
;
;        exec    esi, TFreshEdit:DelChar
;        mov     [.byte_offs2], eax
;        exec    esi, TFreshEdit:FormatLine, [esi+TFreshEdit._yCaret]
;        stdcall TFreshEdit.__PointerToPos, esi, edx, [.byte_offs2]
;
;.deleted:
;        mov     [esi+TFreshEdit._yCaret], eax
;        mov     [esi+TFreshEdit._xCaret], edx
;
;        stdcall TFreshEdit.__FitCaretInWindow, esi
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;; BUG:  it does not works as expected, but I don't have anough patience to fix it just now.....
;.handler_backspc:
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        push    [esi+TFreshEdit._xCaret] [esi+TFreshEdit._yCaret]
;        exec    esi, TFreshEdit:Move, mvcMoveX, -1, FALSE
;        pop     edx eax
;
;        cmp     eax, [esi+TFreshEdit._xCaret]
;        jne     .do_del
;        cmp     edx, [esi+TFreshEdit._yCaret]
;        je      .refresh
;
;.do_del:
;        stdcall TFreshEdit.__PosToPointer, esi, [esi+TFreshEdit._xCaret], [esi+TFreshEdit._yCaret]
;        test    ecx, ecx
;        jnz     .refresh
;
;        stdcall StrLen, [edx+TEditorLine.Data]
;        cmp     ecx, eax
;        jae     .refresh
;
;        jmp     .handler_delete
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;.handler_linedel:
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        exec    esi, TFreshEdit:DeleteLine, [esi+TFreshEdit._yCaret]
;        mov     [esi+TFreshEdit._yCaret], eax
;        mov     [esi+TFreshEdit._xCaret], 0
;        stdcall TFreshEdit.__UpdateCaretPosition, esi
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_wordwrap:
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        get     eax, esi, TFreshEdit:CurrentLine
;        jc      .refresh
;
;        xor     [eax+TEditorLine.flags], lfWordWrap
;        exec    esi, TFreshEdit:FormatLine, [esi+TFreshEdit._yCaret]
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_bookmark:
;        cmp     [esi+TFreshEdit._fReadOnly], froReadWrite
;        jne     .refresh
;
;        get     eax, esi, TFreshEdit:CurrentLine
;        jc      .refresh
;
;        xor     [eax+TEditorLine.flags], lfBookmark
;        or      [.changes], chgfNeedRefresh or chgfNoClearSelection
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_insmode:
;        xor     [esi+TFreshEdit._InsMode], $1
;        or      [.changes], chgfNoClearSelection or chgfNeedRefresh
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.handler_selmode:
;        mov     eax, [esi+TFreshEdit._SelMode]
;        inc     eax
;        cmp     eax, selmBlock
;        jbe     @f
;        xor     eax, eax
;@@:
;        mov     [esi+TFreshEdit._SelMode], eax
;        or      [.changes], chgfNoClearSelection
;        jmp     .refresh
;
;;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
;
;.external_shortcut:
;        cmp     [esi+TFreshEdit._OnControlKey], 0
;        je      .refresh
;
;        push    ebx ecx edx esi edi
;        stdcall [esi+TFreshEdit._OnControlKey], esi, ebx
;        pop     edi esi edx ecx ebx
;
;        or      [.changes], eax
;
;.refresh:
;        test    [.changes], chgfNoClearSelection
;        jnz     .selectionchanged
;
;        cmp     [ebx+TSysEvent.event], seScroll
;        je      .selectionchanged
;
;        cmp     [ebx+TSysEvent.event], seMouseMove
;        je      .selectionchanged
;
;        cmp     [ebx+TSysEvent.event], seMouseBtnPress
;        jne     .clearsel
;
;
;.clearsel:
;        push    [esi+TFreshEdit._xCaret] [esi+TFreshEdit._yCaret]
;        pop     [esi+TFreshEdit._ySelection] [esi+TFreshEdit._xSelection]
;        jmp     .endmove
;
;.selectionchanged:
;
;.endmove:
;; compare fields with backup fields
;        mov     eax, [esi+TFreshEdit._TopLine]
;        mov     ecx, [esi+TFreshEdit._LeftColumn]
;        cmp     eax, [esi+TFreshEdit._prevTopLine]
;        jne     .scrolled
;        cmp     ecx, [esi+TFreshEdit._prevLeftColumn]
;        je      .scrollerok
;
;.scrolled:
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .caretmoved
;
;.scrollerok:
;        mov     eax, [esi+TFreshEdit._xCaret]
;        mov     ecx, [esi+TFreshEdit._yCaret]
;        mov     edx, [esi+TFreshEdit._InsMode]
;        cmp     eax, [esi+TFreshEdit._prevxCaret]
;        jne     .caretmoved
;        cmp     ecx, [esi+TFreshEdit._prevyCaret]
;        jne     .caretmoved
;        cmp     edx, [esi+TFreshEdit._prevInsMode]
;        je      .caretposok
;
;.caretmoved:
;        stdcall TFreshEdit.__UpdateCaretPosition, esi
;
;.caretposok:
;        mov     eax, [esi+TFreshEdit._xSelection]
;        mov     ecx, [esi+TFreshEdit._ySelection]
;        sub     eax, [esi+TFreshEdit._xCaret]
;        sub     ecx, [esi+TFreshEdit._yCaret]
;        mov     edx, [esi+TFreshEdit._prevSelMode]
;
;        cmp     eax, [esi+TFreshEdit._prevxDelta]
;        jne     .selchanged
;        cmp     ecx, [esi+TFreshEdit._prevyDelta]
;        jne     .selchanged
;        cmp     edx, [esi+TFreshEdit._SelMode]
;        je      .selok
;
;.selchanged:
;        or      [.changes], chgfNeedRefresh
;.selok:
;; now save the status fields for the next pass
;
;        mov     eax, [esi+TFreshEdit._TopLine]
;        mov     ecx, [esi+TFreshEdit._LeftColumn]
;        mov     [esi+TFreshEdit._prevTopLine], eax
;        mov     [esi+TFreshEdit._prevLeftColumn], ecx
;
;        mov     eax, [esi+TFreshEdit._xCaret]
;        mov     ecx, [esi+TFreshEdit._yCaret]
;        mov     edx, [esi+TFreshEdit._InsMode]
;        mov     [esi+TFreshEdit._prevxCaret], eax
;        mov     [esi+TFreshEdit._prevyCaret], ecx
;        mov     [esi+TFreshEdit._prevInsMode], edx
;
;        mov     eax, [esi+TFreshEdit._xSelection]
;        mov     ecx, [esi+TFreshEdit._ySelection]
;        sub     eax, [esi+TFreshEdit._xCaret]
;        sub     ecx, [esi+TFreshEdit._yCaret]
;        mov     edx, [esi+TFreshEdit._SelMode]
;        mov     [esi+TFreshEdit._prevxDelta], eax
;        mov     [esi+TFreshEdit._prevyDelta], ecx
;        mov     [esi+TFreshEdit._prevSelMode], edx
;
;
;        test    [.changes], chgfNeedRefresh
;        jz      .finish_event
;
;        mov     [esi+TFreshEdit._fShadowReady], 0
;        exec    esi, TWindow:Refresh
;
;.finish_event:
;        test    [.changes], chgfFinished
;        jz      .continue
;        clc
;        jmp     .finish

        return
endp





method TFreshEdit.EventFocusIn
begin
        pushad
        mov     esi, [.self]

        cmp     [esi+TFreshEdit._fReadOnly], froReadOnlyNoCaret
        je      .finish

        stdcall CaretAttach, esi
        stdcall CaretShow, TRUE
        stdcall TFreshEdit.__UpdateCaretPosition, esi

.finish:
        popad
        return
endp



method TFreshEdit.EventFocusOut
begin

        stdcall CaretShow, FALSE

        return
endp


method TFreshEdit.EventScroll
begin
        pushad
        mov     esi, [.self]

        cmp     [.direction], scrollX
        je      .xscroll

; yscroll
        xor     edx, edx
        mov     ecx, [.value]
        cmp     [.command], scTrack
        je      .track

        cmp     [.command], scWheelUp
        je      .scrollwheelup
        cmp     [.command], scWheelDn
        je      .scrollwheeldn

        cmp     [.command], scUp
        je      .scrollup
        cmp     [.command], scDown
        je      .scrolldn

        jmp     .finish

; scroll down
.scrollwheeldn:
        imul    ecx, [ASM.MouseWheel]

.scrolldn:
        exec    esi, TFreshEdit:Move, mvcScrollY, ecx, FALSE
        jmp     .refresh

.scrollwheelup:
        imul    ecx, [ASM.MouseWheel]
        neg     ecx

.scrollup:
        exec    esi, TFreshEdit:Move, mvcScrollY, ecx, FALSE
        jmp     .refresh

.track:
        mov     [esi+TFreshEdit._TopLine], ecx
        jmp     .refresh

.xscroll:
        mov     eax, [.value]
        mov     [esi+TFreshEdit._LeftColumn], eax

; here refresh
.refresh:



.finish:
        popad
        return
endp



method TFreshEdit.EventButtonPress
begin
        pushad

        mov     esi, [.self]

        mov     ecx, [.x]
        test    ecx, ecx
        js      .continue

        cmp     ecx, [esi+TFreshEdit._width]
        jge     .continue

        mov     eax, [.y]
        test    eax, eax
        js      .continue

        cmp     eax, [esi+TFreshEdit._height]
        jge     .continue

        mov     edx, [esi+TFreshEdit.__LeftMargin]
        cmp     ecx, edx
        jl      .left_margin_process

        stdcall SetMouseCapture, esi

        mov     eax, [.button]
        mov     [esi+TFreshEdit._DragButton], eax

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

        stdcall TFreshEdit.__MoveCaretToPixel, esi, [.x], [.y]
        jc      .continue
        jmp     .refresh

.left_margin_process:
        sub     edx, 16         ; !
        cmp     ecx, edx
        jl      .continue       ; number or breakpoint click

; Fold/unfold click
; check for the fold/unfold icon under the cursor
;        xor     edx, edx
;        div     [esi+TFreshEdit._fontheight]
;        add     eax, [esi+TFreshEdit._TopLine]
;
;        mov     edx, [esi+TFreshEdit._pIndex]
;        cmp     eax, [edx+TArray.count]
;        jae     .continue
;
;        mov     ecx, [edx+TArray.array+4*eax]
;        cmp     ecx, -1
;        je      .continue       ; it is not fold icon row.
;
;        shl     ecx, TEditorLine.shift
;        add     ecx, [esi+TFreshEdit._pLines]
;        add     ecx, TArray.array                       ; pointer to TEditorLine
;
;        test    [ecx+TEditorLine.flags], lfFoldHeader
;        jz      .continue
;
;; determine what is the current state folded or expanded
;locals
;  .current dd ?
;  .next    dd ?
;endl
;        mov     [.current], eax        ; current line index
;        mov     ecx, [edx+TArray.array+4*eax]
;        inc     ecx
;
;.next_loop:
;        inc     eax
;        cmp     eax, [edx+TArray.count]
;        jae     .folded
;
;        cmp     [edx+TArray.array+4*eax], -1
;        je      .next_loop
;
;        mov     [.next], eax
;        cmp     ecx, [edx+TArray.array+4*eax]         ; the next index == current+1 -> expanded
;        jne     .folded
;
;; so, fold it...
;        stdcall TFreshEdit.__FoldZoneLen, esi, [.current]
;
;        mov     ecx, [.next]
;        sub     ecx, [.current]
;        inc     ecx
;
;        cmp     eax, ecx
;        jbe     .continue       ; nothing to fold
;
;        sub     eax, ecx
;        stdcall DeleteArrayItems, [esi+TFreshEdit._pIndex], [.next], eax
;        mov     [esi+TFreshEdit._pIndex], edx
;
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .refresh
;
;.folded:
;; the zone is folded, so restore the index items.
;        stdcall TFreshEdit.__RestoreIndex, esi, [.current], [.next]
;        stdcall TFreshEdit.__UpdateScrollBars, esi
;        or      [.changes], chgfNeedRefresh
;        jmp     .refresh

.refresh:


.continue:
        popad

        inherited  [.button], [.kbdState], [.x], [.y]
        return
endp


method TFreshEdit.EventButtonRelease
begin
        pushad
        mov     esi, [.self]

        mov     eax, [.button]
        cmp     eax, [esi+TFreshEdit._DragButton]
        jne     .finish

        stdcall SetMouseCapture, 0
        mov     [esi+TFreshEdit._DragButton], -1

.finish:
        popad

        inherited  [.button], [.kbdState], [.x], [.y]
        return
endp



method TFreshEdit.EventMouseMove
begin
        pushad

        mov     esi, [.self]

        mov     ecx, mcText
        mov     eax, [.x]
        cmp     eax, [esi+TFreshEdit.__LeftMargin]
        ja      @f
        mov     ecx, mcArrow
@@:
        mov     [esi+TFreshEdit._cursor], ecx

        cmp     [esi+TFreshEdit._DragButton], mbLeft
        jne     .continue

        stdcall TFreshEdit.__MoveCaretToPixel, esi, [.x], [.y]
        jc      .continue

.refresh:

.continue:
        popad
        inherited [.x], [.y], [.kbdState]
        return
endp



;method TFreshEdit.SysEventHandler
;.changes dd ?
;begin
;        push    eax ebx ecx edx esi edi
;
;        mov     [.changes], 0
;        mov     ebx, [.pEvent]
;        mov     esi, [.self]
;
;        mov     eax, [ebx+TSysEvent.event]
;
 c;        cmp     [ebx+TSysEvent.event], seResize
;        je      .resize
;
;        cmp     [ebx+TSysEvent.event], seKbdKeyPress
;        je      .keypress
;
;        cmp     [ebx+TSysEvent.event], seFocusIn
;        je      .focusin
;
;        cmp     [ebx+TSysEvent.event], seFocusOut
;        je      .focusout
;
;        cmp     [ebx+TSysEvent.event], seScroll
;        je      .scroll
;
;        cmp     [ebx+TSysEvent.event], seMouseBtnPress
;        je      .mousebtnpress
;
;        cmp     [ebx+TSysEvent.event], seMouseBtnRelease
;        je      .mousebtnrelease
;
;        cmp     [ebx+TSysEvent.event], seMouseMove
;        je      .mousemove
;
;
;.continue:
;        inherited [.pEvent]
;
;.finish:
;        pop     edi esi edx ecx ebx eax
;        return
;
;;.........................................................................................
;.mousemove:
;;.........................................................................................
;.mousebtnrelease:
;
;;.........................................................................................
;.mousebtnpress:
;
;;.........................................................................................
;.scroll:
;
;;.........................................................................................
;
;.focusin:
;
;.focusout:
;;.........................................................................................
;.keypress:
;
;;.........................................................................................
;.resize:
;endp



;_________________________________________________________________________________________



proc TFreshEdit.__MoveCaretToPixel, .editor, .x, .y
begin
        push    ecx edx esi

        xor     ecx, ecx
        mov     esi, [.editor]

; x
        mov     eax, [.y]
        mov     edx, [esi+TFreshEdit._FreeArea.y]
        cmp     eax, edx
        jl      .nomove

        add     edx, [esi+TFreshEdit._FreeArea.height]
        cmp     eax, edx
        jge     .nomove

        mov     eax, [.x]
        mov     edx, [esi+TFreshEdit._FreeArea.x]
        cmp     eax, edx
        jl      .nomove

        add     edx, [esi+TFreshEdit._FreeArea.width]
        cmp     eax, edx
        jge     .nomove

        sub     eax, [esi+TFreshEdit.__LeftMargin]
        jge     @f
        xor     eax, eax
@@:
        cdq
        idiv    [esi+TFreshEdit._fontwidth]
        add     eax, [esi+TFreshEdit._LeftColumn]

        cmp     [esi+TFreshEdit._xCaret], eax
        je      @f

        test    eax, eax
        js      @f

        mov     [esi+TFreshEdit._xCaret], eax
@@:
; y
        mov     eax, [.y]
        cdq
        idiv    [esi+TFreshEdit._fontheight]
        add     eax, [esi+TFreshEdit._TopLine]

        cmp     [esi+TFreshEdit._yCaret], eax
        je      @f

        test    eax, eax
        js      @f
        mov     [esi+TFreshEdit._yCaret], eax
@@:
        mov     eax, ecx
        clc

.finish:
        pop     esi edx ecx
        return

.nomove:
        stc
        jmp     .finish
endp




proc TFreshEdit.__DrawShadow, .editor
  .context  dd  ?
  .latest   dd  ?
  .color     dd ?
  .colormask dd ?
begin
        pushad

        mov     esi, [.editor]
        mov     edi, [esi+TFreshEdit._pShadow]

        cmp     [edi+TBackBuffer.width], 1
        jle     .finish
        cmp     [edi+TBackBuffer.height], 1
        jle     .finish

        cmp     [esi+TFreshEdit._fShadowReady], 0
        jne     .finish

        stdcall AllocateContext, [edi+TBackBuffer.raster]
        mov     [.context], eax

        stdcall TFreshEdit.__ComputeLeftMargin, esi

; draw left margin field
        mov     eax, [esi+TFreshEdit.__LeftMargin]
        sub     eax, 4

        stdcall DrawFillRect, [.context], 0, 0, eax, [edi+TBackBuffer.height], [FreshEditTheme.clLeftMargin]

; clean the image. How good is to paint in double buffer - no flicker at all. :)
        inc     eax
        mov     ecx, [edi+TBackBuffer.width]
        sub     ecx, eax
        test    [FreshEditTheme.Options], eoStripedBackground
        jnz     .do_zebra

        stdcall DrawFillRect, [.context], eax, 0, ecx, [edi+TBackBuffer.height], [FreshEditTheme.clBackground]
        jmp     .leftmargin_ok

.do_zebra:
        xor     edx, edx
        mov     ebx, [FreshEditTheme.clBackground]
        mov     [.color], ebx
        xor     ebx, [FreshEditTheme.clAltBack]

        test    [esi+TFreshEdit._TopLine], 1
        jz      .zebra
        xor     [.color], ebx
.zebra:
        stdcall DrawFillRect, [.context], eax, edx, ecx, [esi+TFreshEdit._fontheight], [.color]
        add     edx, [esi+TFreshEdit._fontheight]
        xor     [.color], ebx
        cmp     edx, [edi+TBackBuffer.height]
        jb      .zebra

.leftmargin_ok:

; draw the wrap position if visible
.wrap_position:
        mov     eax, [FreshEditTheme.WrapPos]
        sub     eax, [esi+TFreshEdit._LeftColumn]
        imul    eax, [esi+TFreshEdit._fontwidth]
        add     eax, [esi+TFreshEdit.__LeftMargin]

        cmp     eax, [esi+TFreshEdit.__LeftMargin]
        jl      .wrapok
        cmp     eax, [edi+TBackBuffer.width]
        jge     .wrapok

        stdcall SetSimpleLine, [.context], [FreshEditTheme.clWrapColumn]
        stdcall DrawLine, [.context], eax, 0, eax, [edi+TBackBuffer.height]

.wrapok:
; ebx is pointer to the index array.
        mov     ebx, [esi+TFreshEdit._pIndex]

; edx is the number of the line in [pIndex]
        mov     edx, [esi+TFreshEdit._TopLine]

; ecx is the y coordinate of the line.
        mov     ecx, [esi+TFreshEdit._fontheight]
        sub     ecx, [esi+TFreshEdit._fontdescent]
;        sub     ecx, 3                                  ; this is not good.

; check the first line
.searchbegin:
        cmp     [ebx+TArray.array+4*edx], -1
        jne     .lineloop

        sub     ecx, [esi+TFreshEdit._fontheight]
        dec     edx
        jnz     .searchbegin

; so draw the lines.
.lineloop:
        mov     eax, ecx
        sub     eax, [esi+TFreshEdit._fontheight]
        add     eax, [esi+TFreshEdit._fontdescent]
        cmp     eax, [edi+TBackBuffer.height]
        jge     .endlines

        cmp     edx, [ebx+TArray.count]
        jae     .endlines

; call the TFreshEditLineType.procDraw for the current line type.
        stdcall TFreshEdit.__DrawLine, esi, [.context], edx, ecx
        add     ecx, [esi+TFreshEdit._fontheight]

        inc     edx
        jmp     .lineloop

.endlines:
; fix the left line border to the end of the windows.
        add     ecx, [esi+TFreshEdit._fontdescent]
        mov     eax, [esi+TFreshEdit.__LeftMargin]
        sub     ecx, [esi+TFreshEdit._fontheight]
        sub     eax, 2
        stdcall SetSimpleLine, [.context], [FreshEditTheme.clLeftMarginBorder]
        stdcall DrawLine, [.context], eax, ecx, eax, [edi+TBackBuffer.height]
        stdcall SetSimpleLine, [.context], [FreshEditTheme.clLeftMargin]
        dec     eax
        stdcall DrawLine, [.context], eax, ecx, eax, [edi+TBackBuffer.height]
        dec     eax
        stdcall DrawLine, [.context], eax, ecx, eax, [edi+TBackBuffer.height]

        stdcall ReleaseContext, [.context]

.finish:
        mov     [esi+TFreshEdit._fShadowReady], 1

        popad
        return
endp



proc TFreshEdit.__DrawLine, .editor, .context, .indexLine, .y
  .line   dd  ?
  .last_line dd ?

  .attr   dd  ?
  .ysel0  dd  ?
  .xsel0  dd  ?
  .ysel1  dd  ?
  .xsel1  dd  ?

  .unfold dd  ?

  .next   dd  ?
  .markh  dd  ?

  .linenumber dd ?

  .fold_icon dd ?
  .fold_line dd ?

begin
        pushad

        mov     edi, [.editor]

        mov     [.line], -1
        mov     [.unfold], 0

        mov     eax, [.indexLine]
        lea     ecx, [eax+1]
        mov     edx, [edi+TFreshEdit._pIndex]

.nextloop:
        cmp     ecx, [edx+TArray.count]
        jae     .unfoldok
        cmp     [edx+TArray.array+4*ecx], -1
        jne     @f
        inc     ecx
        jmp     .nextloop
@@:
        push    ecx
        mov     ecx, [edx+TArray.array+4*ecx]
        dec     ecx
        cmp     ecx, [edx+TArray.array+4*eax]
        pop     ecx
        je      .unfoldok

        mov     [.unfold], 1

.unfoldok:
        mov     [.next], ecx

; if the line is part of the word-wrapped line, don't draw the text, because it is already OK.
        mov     edx, [edx+TArray.array+4*eax]     ; index in pLines
        mov     [.linenumber], edx
        inc     [.linenumber]           ; count it from 1
        cmp     edx, -1
        je      .end_draw_text       ; wrapped line.

        mov     eax, [edi+TFreshEdit._pLines]
        mov     [.last_line], 0
        mov     eax, [eax+TArray.count]
        dec     eax
        cmp     eax, edx
        jne     @f
        inc     [.last_line]    ; flag that this line is the last line.
@@:
        shl     edx, TEditorLine.shift
        add     edx, [edi+TFreshEdit._pLines]
        add     edx, TArray.array
        mov     [.line], edx                      ; pointer to TEditorLines

; draw the text of the line
;. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
        stdcall StrLen, [edx+TEditorLine.Data]
        test    eax, eax
        jz      .empty_string

        mov     ecx, eax
        shl     eax, 4          ; sizeof.TCharAttr =  16

        stdcall GetMem, eax     ; attr array.
        mov     [.attr], eax
        mov     esi, eax

        mov     ebx, [FreshEditTheme.clSimpleText]
        test    [edx+TEditorLine.flags], lfProtected
        jz      @f
        mov     ebx, [FreshEditTheme.clProtectedText]
@@:
        mov     edx, [edi+TFreshEdit._Font]

; fill the attribute array with the default values.
.attrloop:
        mov     [esi+TCharAttr.color], ebx
        mov     [esi+TCharAttr.font], edx
        lea     esi, [esi+sizeof.TCharAttr]
        loop    .attrloop

        mov     edx, [.line]
        test    [edx+TEditorLine.flags], lfProtected
        jnz     .syntaxok

; Call the syntax highlighter if any:
        xor     ecx, ecx
        cmp     [.indexLine], ecx
        je      @f
        mov     ecx, [edx-sizeof.TEditorLine+TEditorLine.syn_context]
@@:
        cmp     [edi+TFreshEdit._procSyntax], 0
        je      .syntaxok

        push    edi edx
        stdcall [edi+TFreshEdit._procSyntax], [edx+TEditorLine.Data], [.attr], ecx
        pop     edx edi
        mov     [edx+TEditorLine.syn_context], eax

.syntaxok:
        stdcall StrPtr, [edx+TEditorLine.Data]
        cmp     byte [eax], 0
        je      .end_draw_text

;        stdcall DrawString, [.context], eax, [eax+string.len], [edi+TFreshEdit.__LeftMargin], [.y], [edi+TFreshEdit._Font], 0

        stdcall DrawColoredString, [.context], eax, [.attr], [eax+string.len], [edi+TFreshEdit.__LeftMargin],     \
                                   [.y], [edi+TFreshEdit._LeftColumn], [edi+TFreshEdit._fontwidth], [edi+TFreshEdit._fontheight]

        stdcall FreeMem, [.attr]

.end_draw_text:

; draw left margin graphics:
        stdcall TFreshEdit.__DrawLeftMargin, edi, [.context], [.indexLine]

; now draw transparent selection

; selection bounds.
        mov     eax, [edi+TFreshEdit._xSelection]
        mov     ecx, [edi+TFreshEdit._ySelection]
        mov     ebx, [edi+TFreshEdit._xCaret]
        mov     edx, [edi+TFreshEdit._yCaret]

        cmp     ecx, edx
        jl      @f
        je      .sortx
        xchg    ecx, edx
        cmp     [edi+TFreshEdit._SelMode], selmChar
        jne     @f
        xchg    eax, ebx
@@:
        cmp     [edi+TFreshEdit._SelMode], selmChar
        je      .endsort

.sortx:
        cmp     eax, ebx
        jle     @f
        xchg    eax, ebx
@@:

.endsort:
        mov     [.xsel0], eax
        mov     [.ysel0], ecx
        mov     [.xsel1], ebx
        mov     [.ysel1], edx

        mov     eax, [.indexLine]
        cmp     eax, [.ysel0]
        jl      .end_sel
        cmp     eax, [.ysel1]
        jg      .end_sel

        mov     eax, [edi+TFreshEdit._SelMode]

        cmp     eax, selmChar
        je      .selchar

; sel block
        mov     eax, [.xsel0]
        cmp     eax, [.xsel1]
        je      .end_sel

        sub     eax, [edi+TFreshEdit._LeftColumn]
        jge     @f
        xor     eax, eax
@@:
        imul    eax, [edi+TFreshEdit._fontwidth]
        add     eax, [edi+TFreshEdit.__LeftMargin]

        mov     ecx, [.xsel1]
        sub     ecx, [edi+TFreshEdit._LeftColumn]
        jge     @f
        xor     ecx, ecx
@@:
        imul    ecx, [edi+TFreshEdit._fontwidth]
        add     ecx, [edi+TFreshEdit.__LeftMargin]
        sub     ecx, eax
        jmp     .drawsel

.selchar:
        xor     eax, eax
        mov     edx, [.indexLine]
        cmp     edx, [.ysel0]
        jne     .firstok

        mov     eax, [.xsel0]
        sub     eax, [edi+TFreshEdit._LeftColumn]
        jge     .firstok
        xor     eax, eax

.firstok:
        imul    eax, [edi+TFreshEdit._fontwidth]
        add     eax, [edi+TFreshEdit.__LeftMargin]

        mov     ecx, [edi+TFreshEdit._cols]    ; selection is to the end of the line, for the intermediate lines.
        inc     ecx

        cmp     edx, [.ysel1]
        jne     .lastok

        mov     ecx, [.xsel1]
        sub     ecx, [edi+TFreshEdit._LeftColumn]
        test    ecx, ecx
        jns     .lastok
        xor     ecx, ecx

.lastok:
        imul    ecx, [edi+TFreshEdit._fontwidth]
        add     ecx, [edi+TFreshEdit.__LeftMargin]
        sub     ecx, eax

.drawsel:
        mov     edx, [.y]
        sub     edx, [edi+TFreshEdit._fontheight]
        add     edx, 3

        stdcall SetDrawMode, [.context], [FreshEditTheme.clSelMode]
        stdcall DrawFillRect, [.context], eax, edx, ecx, [edi+TFreshEdit._fontheight], [FreshEditTheme.clSelBack]
        stdcall SetDrawMode, [.context], cmCopy

.end_sel:
        popad
        return

.empty_string:
        mov     eax, [.indexLine]
        mov     ecx, [FreshEditTheme.clBackground]
        cmp     eax, [.ysel0]
        jb      .setback
        cmp     eax, [.ysel1]
        jae     .setback

        mov     ecx, [FreshEditTheme.clSelBack]

.setback:
        jmp     .end_draw_text

endp





proc TFreshEdit.__DrawLeftMargin, .editor, .context, .indexLine

.x         dd ?
.y         dd ?
.width     dd ?
.height    dd ?

.ytext     dd ?
.yicons    dd ?

.unfold    dd ?  ; is the line folded?
.indexNext dd ?  ; the index of the next line in TFreshEdit._pIndex

.ipLines   dd ?  ; the index of the current line in pLines array.
.ptrLine   dd ?  ; pointer of the current TEditorLine.

.fold_icon dd ?
.fold_line dd ?

begin
        pushad

        mov     esi, [.editor]

        test    [FreshEditTheme.Options], eoLeftMargin
        jz      .finish

        stdcall SetDrawMode, [.context], cmCopy

; search for the next line index;
        mov     ecx, [.indexLine]
        mov     edx, [esi+TFreshEdit._pIndex]
        cmp     [edx+TArray.array+4*ecx], -1
        je      .finish

        mov     [.unfold], 0

        xor     eax, eax
        dec     eax

.nextloop:
        inc     ecx
        cmp     ecx, [edx+TArray.count]
        jae     .found_next

        cmp     [edx+TArray.array+4*ecx], eax
        je      .nextloop

.found_next:
        mov     [.indexNext], ecx

        mov     eax, [.indexLine]
        mov     eax, [edx+TArray.array+4*eax]   ; current line - index in pLines
        mov     [.ipLines], eax

        cmp     ecx, [edx+TArray.count]
        jae     .unfoldok

        mov     ecx, [edx+TArray.array+4*ecx]   ; next line index in pLines
        dec     ecx
        cmp     ecx, eax
        setne   byte [.unfold]

.unfoldok:
        mov     edx, eax
        shl     edx, TEditorLine.shift
        add     edx, TArray.array
        add     edx, [esi+TFreshEdit._pLines]
        mov     [.ptrLine], edx

; determine the bounds of the drawing
        mov     ecx, [.indexNext]
        sub     ecx, [.indexLine]
        mov     eax, [esi+TFreshEdit.__LeftMargin]
        imul    ecx, [esi+TFreshEdit._fontheight]
        mov     [.width], eax
        mov     [.height], ecx

        xor     eax, eax
        mov     [.x], eax

        mov     eax, [.indexLine]
        sub     eax, [esi+TFreshEdit._TopLine]
        imul    eax, [esi+TFreshEdit._fontheight]
        mov     [.y], eax                               ; it can be negative for the word wrapped lines, partially displayed.

        add     eax, [esi+TFreshEdit._fontheight]
        sub     eax, [esi+TFreshEdit._fontdescent]
        mov     [.ytext], eax

        mov     ecx, [esi+TFreshEdit._iconBreakpointA]
        mov     eax, [esi+TFreshEdit._fontheight]
        sub     eax, [ecx+TImage.height]
        sar     eax, 1
        adc     eax, [.y]
        mov     [.yicons], eax

; draw the bookmark background.
        test    [edx+TEditorLine.flags], lfBookmark
        jz      .bookmarkok
        mov     eax, [.width]
        sub     eax, 4
        stdcall DrawFillRect, [.context], [.x], [.y], eax, [.height], [FreshEditTheme.clBookmarkBack]
.bookmarkok:

; draw the cutted right border:
        mov     eax, [.width]
        mov     ecx, [.y]
        sub     eax, 2
        add     ecx, [.height]

        stdcall SetSimpleLine, [.context], [FreshEditTheme.clLeftMarginBorder]

        test    [edx+TEditorLine.flags], lfWordWrap
        jnz     .wrap_border

        stdcall DrawLine, [.context], eax, [.y], eax, ecx

        mov     ebx, [FreshEditTheme.clLeftMargin]
        test    [edx+TEditorLine.flags], lfBookmark
        jz      @f
        mov     ebx, [FreshEditTheme.clBookmarkBack]
@@:
        stdcall SetSimpleLine, [.context], ebx
        dec     eax
        stdcall DrawLine, [.context], eax, [.y], eax, ecx
        dec     eax
        stdcall DrawLine, [.context], eax, [.y], eax, ecx
        jmp     .borderok

.wrap_border:
        dec     ecx

        push    ecx  eax [.y] eax [.context]       ; vertical
        push    [.y] eax [.y] eax [.context]       ; upper edge
        push    ecx  eax ecx  eax [.context]       ; down edge

        sub     dword [esp+12], 2
        sub     dword [esp+16], 2
        stdcall DrawLine

        sub     dword [esp+12], 2
        add     dword [esp+16], 2
        stdcall DrawLine

        sub     dword [esp+4], 2
        add     dword [esp+8], 2
        sub     dword [esp+12], 2
        sub     dword [esp+16], 1
        stdcall DrawLine

        mov     ebx, [FreshEditTheme.clLeftMargin]
        test    [edx+TEditorLine.flags], lfBookmark
        jz      @f
        mov     ebx, [FreshEditTheme.clBookmarkBack]
@@:
        stdcall SetSimpleLine, [.context], ebx

        push    [.y] eax [.y] eax [.context]       ; upper edge
        push    [.y] eax [.y] eax [.context]       ; upper edge
        push    ecx  eax ecx  eax [.context]       ; down corner pixels
        push    ecx  eax ecx  eax [.context]       ; down corner pixels

        sub     dword [esp+4], 2
        stdcall DrawLine

        sub     dword [esp+4], 2
        sub     dword [esp+8], 1
        sub     dword [esp+12], 2
        stdcall DrawLine

        sub     dword [esp+4], 2
        stdcall DrawLine

        sub     dword [esp+4], 2
        add     dword [esp+8], 1
        sub     dword [esp+12], 2
        stdcall DrawLine

.borderok:
; Line number draw
        test    [FreshEditTheme.Options], eoLineNumbers
        jz      .line_numbers_ok

        mov     ecx, [.ipLines]
        test    ecx, ecx
        jl      .line_numbers_ok

        inc     ecx
        stdcall NumToStr, ecx, ntsDec or ntsUnsigned
        push    eax eax
        stdcall StrLen, eax
        mov     ecx, eax
        stdcall StrPtr ; from the stack
        mov     ebx, eax

        stdcall GetTextBounds, [.context], eax, ecx, 0 ; [edi+TFreshEdit._Font]
        sub     eax, [esi+TFreshEdit.__NumberMargin]
        neg     eax
        stdcall DrawString, [.context], ebx, ecx, eax, [.ytext], 0, [FreshEditTheme.clLeftMarginText]
        stdcall StrDel ; from the stack

.line_numbers_ok:
; draw icons in the middle

; icon breakpoint
        mov     edx, [.ptrLine]
        test    [edx+TEditorLine.flags], lfBreakpoint
        jnz     .draw_breakpoint

        cmp     [edx+TEditorLine.debugdata], 0
        je      .breakpoint_ok

.draw_breakpoint:
        mov     eax, [esi+TFreshEdit.__NumberMargin]
        add     eax, 3
        push    [.yicons] eax ; coordinates

        mov     eax, [esi+TFreshEdit._maskDebugInfo]
        mov     ecx, [esi+TFreshEdit._iconDebugInfo]
        test    [edx+TEditorLine.flags], lfBreakpoint
        jz      @f
        mov     eax, [esi+TFreshEdit._maskBreakpointA]
        mov     ecx, [esi+TFreshEdit._iconBreakpointA]
        cmp     [edx+TEditorLine.debugdata], 0
        jne     @f
        mov     eax, [esi+TFreshEdit._maskBreakpointI]
        mov     ecx, [esi+TFreshEdit._iconBreakpointI]
@@:
        stdcall DrawMaskedImage, [.context], eax, ecx

.breakpoint_ok:

; draw fold tree.

        mov     edx, [.ptrLine]

; first, determine, what icon should be set for fold/unfold icon:
        xor     ecx, ecx
        mov     ebx, [esi+TFreshEdit._pIndex]
        mov     eax, [.indexNext]
        cmp     eax, [ebx+TArray.count]
        jae     @f
        mov     eax, [ebx+TArray.array+4*eax]   ; index in pLines of the next visible line.
        shl     eax, TEditorLine.shift
        mov     ecx, [esi+TFreshEdit._pLines]
        lea     ecx, [ecx+TArray.array+eax]
@@:
        test    [edx+TEditorLine.flags], lfFoldHeader
        jnz     .fold_fold

        jecxz   .fold_end

        mov     eax, [ecx+TEditorLine.fold_level]
        cmp     eax, [edx+TEditorLine.fold_level]
        ja      .fold_continue
        jb      .fold_end

        test    [ecx+TEditorLine.flags], lfFoldHeader
        jz      .fold_continue

.fold_end:
        xor     eax, eax
        mov     ecx, 2          ; 2 means corner up/right
        jmp     .fold_ok

.fold_fold:
        mov     ecx, 1          ; 1 means half vertical down
        mov     eax, [esi+TFreshEdit._iconUnfold]
        cmp     [.unfold], 0
        jne     .fold_ok
        mov     eax, [esi+TFreshEdit._iconFold]
        jmp     .fold_ok

.fold_continue:
        xor     eax, eax
        xor     ecx, ecx        ; 0 means full vertical line

.fold_ok:
        mov     [.fold_icon],  eax
        mov     [.fold_line], ecx

; compute the coordinates.
.coordinates:
        mov     ecx, [.yicons]
        mov     eax, [esi+TFreshEdit.__LeftMargin]
        sub     eax, 15

; icon unfold
        cmp     [edx+TEditorLine.fold_level], 0
        je      .tree_ok

        push    eax ebx ecx
        lea     ebx, [ecx+5] ; ebx == Y middle of the icon.
        add     eax, 5       ; eax == X middle of the icon.
        mov     ecx, [.height]

        stdcall SetDrawMode, [.context], cmCopy
        stdcall SetSimpleLine, [.context], [FreshEditTheme.clLeftMarginTree]

        push    [.y] eax [.y] eax
        add     [esp+4], ecx

        cmp     [.fold_line], 0  ; full vertical.
        je      .draw_line

        push    ebx eax ebx eax
        add     dword [esp+8], 9
        stdcall DrawLine, [.context] ; coordinates from the stack

        xchg    [esp+12], ebx

        cmp     [.fold_line], 1      ; short vertical
        je      .draw_line

        xchg    [esp+4], ebx

.draw_line:
        stdcall DrawLine, [.context] ; coordinates from the stack.
        pop     ecx ebx eax

        cmp     [.fold_icon], 0
        je      .tree_ok

        stdcall DrawMaskedImage, [.context], [esi+TFreshEdit._maskUnfold], [.fold_icon], eax, ecx

.tree_ok:

.finish:
        popad
        return
endp





;_________________________________________________________________________________________
;
; Moves the caret to the proper pixel position, depending on caret position inside the text.
;
;__________________________________________________________________________________________

proc TFreshEdit.__UpdateCaretPosition, .editor
.width dd ?
.height dd ?
.offset dd ?
begin
        push    eax ebx edx

        mov     ebx, [.editor]


        mov     eax, [ebx+TFreshEdit._fontheight]
        mov     edx, [ebx+TFreshEdit._fontwidth]

        mov     [.offset], 0

        cmp     [ebx+TFreshEdit._InsMode], modeInsert
        jne     .sizeok

        test    [FreshEditTheme.Options], eoVerticalCaret
        jz      .hcaret
        mov     edx, 1
        jmp     .sizeok

.hcaret:
        sub     eax, 2
        mov     [.offset], eax
        mov     eax, 2

.sizeok:
        mov     [.width], edx
        mov     [.height], eax

        mov     eax, [ebx+TFreshEdit._yCaret]
        sub     eax, [ebx+TFreshEdit._TopLine]
        jl      .hideit

        cmp     eax, [ebx+TFreshEdit._rows]
        jg      .hideit

        imul    eax, [ebx+TFreshEdit._fontheight]
        add     eax, [.offset]

        mov     edx, [ebx+TFreshEdit._xCaret]
        sub     edx, [ebx+TFreshEdit._LeftColumn]
        imul    edx, [ebx+TFreshEdit._fontwidth]
        add     edx, [ebx+TFreshEdit.__LeftMargin]

        stdcall CaretChange, edx, eax, [.width], [.height]

.finish:
        pop     edx ebx eax
        return

.hideit:
        stdcall CaretChange, -1, -1, 1, 1
        jmp     .finish

endp



;_________________________________________________________________________________________

proc TFreshEdit.__UpdateScrollBars, .editor
begin
        push    eax ebx ecx edx

        mov     ebx, [.editor]

; V scroller
        mov     eax, [ebx+TFreshEdit._pIndex]
        mov     eax, [eax+TArray.count]
        sub     eax, [ebx+TFreshEdit._rows]
        mov     [ebx+TFreshEdit._VScroller.Max], eax

        mov     eax, [ebx+TFreshEdit._TopLine]
        mov     [ebx+TFreshEdit._VScroller.Pos], eax

        mov     eax, [ebx+TFreshEdit._rows]
        mov     [ebx+TFreshEdit._VScroller.Page], eax

; H scroller
        get     eax, ebx, TFreshEdit:MaxLineLen

        mov     ecx, [ebx+TFreshEdit._cols]
        sub     eax, ecx
        mov     [ebx+TFreshEdit._HScroller.Max], eax

        mov     [ebx+TFreshEdit._HScroller.Page], ecx
        mov     eax, [ebx+TFreshEdit._LeftColumn]
        mov     [ebx+TFreshEdit._HScroller.Pos], eax

        pop     edx ecx ebx eax
        return
endp



; if the caret is inside the window - does nothing.
; if the caret is outside the window - scrolls the text to get the caret in the window.

proc TFreshEdit.__FitCaretInWindow, .editor
begin
        push    ebx ecx edx esi
        xor     eax, eax
        mov     esi, [.editor]

; vertical
        mov     ecx, [esi+TFreshEdit._TopLine]
        mov     edx, ecx
        add     edx, [esi+TFreshEdit._rows]
        dec     edx
        mov     ebx, [esi+TFreshEdit._yCaret]

        cmp     ebx, ecx
        jb      .updown
        cmp     ebx, edx
        jbe     .checkx

.updown:
        mov     ecx, [esi+TFreshEdit._rows]
        shr     ecx, 1
        sub     ebx, ecx
        jg      @f
        xor     ebx, ebx
@@:
        mov     [esi+TFreshEdit._TopLine], ebx
        or      eax, $1

.checkx:
        mov     ecx, [esi+TFreshEdit._LeftColumn]
        mov     edx, ecx
        add     edx, [esi+TFreshEdit._cols]
        dec     edx
        mov     ebx, [esi+TFreshEdit._xCaret]

        cmp     ebx, ecx
        jb      .leftright
        cmp     ebx, edx
        jbe     .endset

.leftright:
        mov     ecx, [esi+TFreshEdit._cols]
        shr     ecx, 1
        sub     ebx, ecx
        jg      @f
        xor     ebx, ebx
@@:
        mov     [esi+TFreshEdit._LeftColumn], ebx
        or      eax, $2

.endset:
        test    eax, eax
        pop     esi edx ecx ebx
        return
endp

; Converts x,y position in the text to memory parameters
; arguments:
;   .x, .y - x and y coordinates of the position.
; returns:
; edx - pointer to the TEditorLine
; eax - byte offset inside the string that corresponds with the given position.
; ecx - count of the spaces that have to be inserted on offset eax in the string
;       in order to reach the .x coordinate (when the position is beyond the end of the line)
proc TFreshEdit.__PosToPointer, .editor, .x, .y
.pline dd ?
.start dd ?
begin
        push    ebx esi edi

        mov     edi, [.editor]
        mov     eax, [.y]
        mov     ebx, [edi+TFreshEdit._pIndex]
        cmp     eax, [ebx+TArray.count]
        jae     .error

        xor     ecx, ecx

.lineloop:
        mov     edx, [ebx+TArray.array+4*eax]
        cmp     edx, -1
        jne     .found

        inc     ecx
        dec     eax
        jns     .lineloop

.error:
        stc
        jmp     .finish

.crash_error:
        int3
        jmp     .error

; ecx is the wrapped line count.
; edx is the number of string in the lines array.
.found:
        mov     ebx, [edi+TFreshEdit._pLines]
        cmp     edx, [ebx+TArray.count]
        jae     .crash_error

        shl     edx, TEditorLine.shift
        lea     eax, [ebx+TArray.array+edx]

        mov     [.pline], eax        ; handle of the string.

        cmp     [.x], -1
        je      .only_line

        stdcall StrPtr, [eax+TEditorLine.Data]
        mov     esi, eax
        mov     [.start], eax
        jecxz   .wrapok

.wraploop:
        stdcall DecodeUtf8, dword [esi]
        add     esi, edx

        test    eax, eax
        jz      .crash_error

        cmp     eax, $01        ; line wrap char
        jne     .wraploop

        dec     ecx
        jnz     .wraploop

.wrapok:
        mov     ecx, [.x]
        jecxz   .endofstring

.charloop:
        stdcall DecodeUtf8, dword [esi]
        test    eax, eax
        jz      .endofstring
        cmp     eax, $01
        je      .endofstring

        add     esi, edx
        dec     ecx
        jnz     .charloop

.endofstring:
        sub     esi, [.start]
        mov     eax, esi

.only_line:
        mov     edx, [.pline]

        clc

.finish:
        pop     edi esi ebx
        return
endp


; Converts the pointer parameters to caret coordinate.
; arguments:
;   .index - index of the line
;   .offset - byte offset inside the string.
; returns:
;   eax - y coordinate of the caret
;   edx - x coordinate of the caret
;   ecx - pointer to the current TEditorLine

proc TFreshEdit.__PointerToPos, .editor, .index, .offset
.base dd ?
.x    dd ?
begin
        push    esi edi

        mov     esi, [.editor]
        mov     edi, [esi+TFreshEdit._pIndex]
        mov     ecx, [.index]

.line_loop:
        cmp     [edi+TArray.array+4*ecx], -1
        jne     .found
        dec     ecx
        jns     .line_loop

.error:
        stc
        jmp     .finish

.found:
        mov     [.base], ecx                ; base index
        mov     [.x], 0

        mov     ecx, [edi+TArray.array+4*ecx] ; index in pLines
        shl     ecx, TEditorLine.shift
        add     ecx, [esi+TFreshEdit._pLines]
        add     ecx, TArray.array

        cmp     [.offset], 0
        je      .ready

        stdcall StrPtr, [ecx+TEditorLine.Data]
        mov     esi, eax

.char_loop:
        stdcall DecodeUtf8, [esi]
        jc      .error
        add     esi, edx

        cmp     eax, 1  ; wrap char
        jne     .no_wrap

        inc     [.base]
        mov     [.x], -1

.no_wrap:
        inc     [.x]
        sub     [.offset], edx
        ja      .char_loop

.ready:
        mov     eax, [.base]
        mov     edx, [.x]
        clc

.finish:
        pop     edi esi
        return
endp



; .hString - the string containing the line.
;
; returns:
;   eax - the width of the line in chars.
;         if the line contains wrapped sublines,
;         the maximal length will be returned.

proc TFreshEdit.__LineWidth, .hString
.maxlen dd ?
begin
        push    esi ecx edx

        mov     [.maxlen], 0
        stdcall StrPtr, [.hString]
        mov     esi, eax

.outer:
        xor     ecx, ecx

.loop:
        stdcall DecodeUtf8, [esi]
        jc      .error

        add     esi, edx

        test    eax, eax
        jz      .endofline

        cmp     eax, $01        ; wrap char
        je      .endofline

        inc     ecx
        jmp     .loop

.endofline:
        cmp     ecx, [.maxlen]
        jbe     .maxok
        mov     [.maxlen], ecx
.maxok:
        test    eax, eax
        jnz     .outer

        mov     eax, [.maxlen]
        clc

.error:
        pop     edx ecx esi
        return
endp




proc TFreshEdit.__FixLength, .editor, .newlen, .oldlen
begin
        push    eax ecx edx esi edi

        mov     esi, [.editor]
        mov     edx, [esi+TFreshEdit._pLengths]

        mov     eax, [.oldlen]

        cmp     eax, -1
        je      .oldok

        cmp     eax, [edx+TArray.count]
        jae     .oldok  ; wrong arguments???

        cmp     [edx+TArray.array+4*eax], 0
        je      .oldok  ; wrong count.

        dec     dword [edx+TArray.array+4*eax]

.oldok:
        mov     ecx, [.newlen]

        cmp     ecx, -1
        je      .newok

        cmp     ecx, [edx+TArray.count]
        jb      .incr

        sub     ecx, [edx+TArray.count]
        inc     ecx

        stdcall AddArrayItems, edx, ecx
        mov     [esi+TFreshEdit._pLengths], edx
        mov     edi, eax
        xor     eax, eax
        rep stosd
        mov     ecx, [.newlen]

.incr:
        inc     dword [edx+TArray.array+4*ecx]

; then, shrink the array.
.newok:
        mov     ecx, [edx+TArray.count]

.searchloop:
        dec     ecx
        jz      .endsearch

        cmp     [edx+TArray.array+4*ecx], 0
        je      .searchloop

.endsearch:
        lea     eax, [ecx+1]
        sub     ecx, [edx+TArray.count]
        neg     ecx
        stdcall DeleteArrayItems, edx, eax, ecx
        mov     [esi+TFreshEdit._pLengths], edx

        pop     edi esi edx ecx eax
        return
endp



proc TFreshEdit.__ComputeLeftMargin, .editor
begin
        pushad

        mov     esi, [.editor]
        xor     ebx, ebx

        mov     [esi+TFreshEdit.__NumberMargin], ebx

        test    [FreshEditTheme.Options], eoLeftMargin
        jz      .marginok

        test    [FreshEditTheme.Options], eoLineNumbers
        jz      .marginok1

        mov     eax, [esi+TFreshEdit._pLines]
        mov     eax, [eax+TArray.count]
        test    eax, eax
        jz      .marginok1

        call    .digits
        imul    ecx, [esi+TFreshEdit._fontwidth2]
        add     ebx, ecx

        mov     [esi+TFreshEdit.__NumberMargin], ebx

.marginok1:

        mov     eax, [ASM.iconBreakpoints]
        add     ebx, [eax+TImage.width]

        mov     eax, [ASM.lrIconBreakpoints]
        lea     ebx, [ebx+2*eax]           ; left/right margins to the image.

        mov     eax, [ASM.iconFoldings]
        add     ebx, [eax+TImage.width]

        add     ebx, 3+1                   ; 3px for word wrap cut + 1px whitespace at the line beginning.

.marginok:
        xchg    [esi+TFreshEdit.__LeftMargin], ebx
        cmp     ebx, [esi+TFreshEdit.__LeftMargin]
        je      .finish

        stdcall TFreshEdit.__UpdateCaretPosition, esi

.finish:
        popad
        return


.digits:
        xor     ecx, ecx
        cmp     eax, 1          ; 0
        jb      .edig

        inc     ecx
        cmp     eax, 10         ; 1
        jb      .edig

        inc     ecx
        cmp     eax, 100        ; 2
        jb      .edig

        inc     ecx
        cmp     eax, 1000       ; 3
        jb      .edig

        inc     ecx
        cmp     eax, 10000      ; 4
        jb      .edig

        inc     ecx
        cmp     eax, 100000     ; 5
        jb      .edig

        inc     ecx
        cmp     eax, 1000000    ; 6
        jb      .edig

        inc     ecx
        cmp     eax, 10000000   ; 7
        jb      .edig

        inc     ecx
        cmp     eax, 100000000  ; 8
        jb      .edig

        inc     ecx
        cmp     eax, 1000000000 ; 9
        jb      .edig

        inc     ecx             ; 10

.edig:
        retn

endp




proc TFreshEdit.__FoldZoneLen, .editor, .index
.result dd ?
begin
        pushad

        mov     [.result], 0

        mov     esi, [.editor]
        mov     edi, [esi+TFreshEdit._pLines]
        mov     edx, [esi+TFreshEdit._pIndex]

        mov     eax, [.index]
        cmp     eax, [edx+TArray.count]
        jae     .finish

        mov     ecx, [edx+TArray.array+4*eax]   ; index of the first element.
        shl     ecx, TEditorLine.shift
        mov     ebx, [edi+TArray.array+ecx+TEditorLine.fold_level]

.loop:
        inc     [.result]
        inc     eax
        cmp     eax, [edx+TArray.count]
        je      .finish

        mov     ecx, [edx+TArray.array+4*eax]
        cmp     ecx, -1
        je      .loop

        shl     ecx, TEditorLine.shift

        cmp     [edi+TArray.array+ecx+TEditorLine.fold_level], ebx
        jb      .finish
        ja      .loop

        test    [edi+TArray.array+ecx+TEditorLine.flags], lfFoldHeader
        jz      .loop

.finish:
        popad
        mov     eax, [.result]
        return
endp


proc TFreshEdit.__RestoreIndex, .editor, .start_index, .end_index
begin
        pushad

        mov     esi, [.editor]
        mov     edi, [esi+TFreshEdit._pIndex]

        mov     eax, [.start_index]
        mov     edx, [.end_index]

        mov     ebx, [edi+TArray.array+4*edx]
        mov     ecx, [edi+TArray.array+4*eax]   ; index of the first element
        sub     ebx, ecx
        dec     ebx
        jz      .finish

        stdcall InsertArrayItems, [esi+TFreshEdit._pIndex], edx, ebx
        mov     [esi+TFreshEdit._pIndex], edx

        add     [.end_index], ebx

.fill_loop:
        inc     ecx
        mov     [eax], ecx
        add     eax, 4
        dec     ebx
        jnz     .fill_loop

        mov     ecx, [.end_index]

.format_loop:
        exec    esi, TFreshEdit:FormatLine, ecx
        dec     ecx
        cmp     ecx, [.start_index]
        jne     .format_loop

.finish:
        popad
        return
endp


proc TFreshEdit.__AddFormatLine, .hstring, .format
begin
        test    [.format], lfBookmark or lfWordWrap or lfProtected
        jz      .format_ok

        stdcall StrCharCat, [.hstring], ';#F:'

        test    [.format], lfBookmark
        jz      @f
        stdcall StrCharCat, [.hstring], 'b'    ; ctrl-B == Bookmark
@@:
        test    [.format], lfWordWrap
        jz      @f
        stdcall StrCharCat, [.hstring], 'w'   ; ctrl-A == Word wrap
@@:
        test    [.format], lfProtected
        jz      @f
        stdcall StrCharCat, [.hstring], 'p'   ; ctrl-D == Protected
@@:
        stdcall StrCharCat, [.hstring], $0a0d

.format_ok:
        return
endp


; decodes format line (begining with ';#F:')
; returns:
;       CF=0 if the line contains format information.
;            eax - format flags
;            ecx - format line length;
;

proc TFreshEdit.__DecodeFormatLine, .pString
begin
        push    ebx esi

        mov     esi, [.pString]

        lodsb
        cmp     al, ';'
        jne     .not_format
        lodsb
        cmp     al, '#'
        jne     .not_format
        lodsb
        cmp     al, 'F'
        jne     .not_format
        lodsb
        cmp     al, ':'
        jne     .not_format

        xor     ebx, ebx

.format_loop:
        lodsb
        test    al, al
        jz      .not_format

        cmp     al, $0d
        je      .eol
        cmp     al, $0a
        je      .eol

        cmp     al, 'b'
        jne     @f
        or      ebx, lfBookmark
        jmp     .format_loop
@@:
        cmp     al, 'w'
        jne     @f
        or      ebx, lfWordWrap
        jmp     .format_loop
@@:
        cmp     al, 'p'
        jne     @f
        or      ebx, lfProtected
        jmp     .format_loop
@@:

.not_format:
        xor     eax, eax
        pop     esi ebx
        stc
        return

.eol:
        mov     ah, al
        xor     ah, $0a xor $0d
        lodsb
        cmp     al, ah
        je      .format_ok
        dec     esi

.format_ok:
        sub     esi, [.pString]
        mov     ecx, esi
        mov     eax, ebx
        pop     esi ebx
        clc
        return
endp


endmodule

