Here is an editor gadget that accepts colors and fonts ....
This editor was mainly developed to be an editing area inside a canvas-gadget, can offer an editing area inside custom canvas-gadget (the excellent property box of Danilo or the nice Chart of uwekel .... and inside my MyGrid gadget). It still can be used as an editor on its own.
I never thought an editor would be that complicated I have no idea how editors are done, i cam up with the below! I still believe things could be done much simpler (i couldn't get any better ) ...
ideas and feedback are welcome
Said
Code: Select all
;- MyEditor PB Module
; PB compiler : 5.40 LTS / 5.30 onwards (ascii/unicode) - All native PB
; Created on : Sep-2015
;
; Description : simple editor, defines an editing area inside a canvas object; this area in OpenEditor() is defined by X,Y,W,H
; multi-lines, multi-font, multi-colors, handles editing and navigation keys
; Using timer to blink carat - no need for threads
;
DeclareModule MyEditor
Enumeration
#MyEditor_BackColor
#MyEditor_SelectionBackColor
#MyEditor_SelectionTextColor
#MyEditor_CaratColor
#MyEditor_Align
#MyEditor_ReadOnly
#MyEditor_LineSpacing
EndEnumeration
#MyEditor_FirstCaratPos = 0
#MyEditor_LastCaratPos = -1
Declare.i NoRedraw(Editor)
Declare.i Redraw(Editor)
Declare.i ManageEvent(Editor.i, eType)
Declare.i Resize(Editor.i, X = #PB_Ignore, Y = #PB_Ignore, W = #PB_Ignore, H = #PB_Ignore)
Declare.i AddStyle(Editor.i, FontNbr, ForeColor.i)
Declare.i AddLineOfText(Editor.i, Txt.s, Align = #PB_Default)
Declare.i AssignStyle(Editor, Style, StartPos, Length = -1)
Declare.s GetText(Editor)
Declare.i SetText(Editor, Txt.s)
Declare.i SetCaratPos(Editor, Pos = #MyEditor_LastCaratPos)
Declare.i SelectText(Editor, Style, StartPos, Length = -1)
Declare.i UseStyle(Editor, Style)
Declare.i SetAttribute(Editor, Attribute = #MyEditor_BackColor, Value = $FFFFFF)
Declare.i GetAttribute(Editor, Attribute = #MyEditor_BackColor)
Declare.s CloseEditor(Editor)
Declare.i OpenEditor(WinNbr, Gadget, Text.s, X=#PB_Default, Y=#PB_Default, W=#PB_Default, H=#PB_Default, FontNbr = -1, LineSep.s = #LF$)
EndDeclareModule
Module MyEditor
EnableExplicit
; internal constants:
#LineStart = 1 ; Line start - preceeds each hard line
#RowStart = 2 ; Row start - preceeds each artificial row due to wrapping
#TimerNumber = 4321
Enumeration
#Move_Right
#Move_Left
#Move_Up
#Move_Down
#Move_PageUp
#Move_PageDown
#Move_NextWord
#Move_PreviousWord
#Move_FirstInRow
#Move_LastInRow
#Move_First
#Move_Last
EndEnumeration
Structure TChar ; rectangle in the drawing area, can be associated with a real char or a line-start
C.c ; actual char/line-start/row-start
W.u ;
H.u ; for a line-start contain max-height
S.u ; style index in Styles()
A.a ; alignment - valid only for Line (C = LineStart)
EndStructure
Structure TCell ; a cell is part of the drawing area associated with (Line, Row, Char)
X.u ; X,Y are calculated while drawing, H,W are read from associated Row/Character
Y.u
H.i
R.u ; index of Row - starting at 1
I.i ; address of associated Char within List: @*E\Chars()
EndStructure
Structure TStyle
Font_Id.i ; Font ID
TxtColor.i ; text color
RowHeight.i
EndStructure
Structure TEditor
Window.i
Gadget.i
Cursor.i ; original canvas cursor, to be restored when exiting editor
X.i ; coord of the editor within its parent canvas-gadget
Y.i
W.i
H.i
BkgColor.i ; background color
SelBColor.i ; selection/highlight color
SelFColor.i ; selection/highlight color
CaratColor.i
Align.i ; 0 / #PB_Text_Right / #PB_Text_Center
ReadOnly.i ; 0/1
LineSpacing.i ; vertical spacing in pixel (default 2)
AcceptTab.i ; 0/1 ???? canvas cant intercept tab-key!
LineSeparator.s ; multi-char text
MarginLeft.i ; margins within editor area in pixel
MarginRight.i ;
MarginTop.i ;
MarginDown.i ;
NoDrawing.i
; --------------- internal data / use
List Chars.TChar()
List Cells.TCell()
Array Styles.TStyle(0)
TopRow.i
TokenCarat.i ; address of Char holding the carat
UserStyle.i
SelStart.i ;
SelEnd.i ;
LastUsedX.i ; needed when moving up/down
CellCarat_X.i
CellCarat_Y.i
CellCarat_H.i
CellCarat_V.i
EndStructure
Global NewMap Editors() ; list of currently opened editors
Declare ShowRow(*E.TEditor, Row)
Declare RowHavingCarat(*E.TEditor)
Procedure.i MySplitString(s.s, multiCharSep.s, Array a.s(1))
Protected count, i, soc, lnStr,lnBStr, lnSep,lnBSep, ss, ee
soc = SizeOf(Character)
lnSep = Len(multiCharSep) : lnBSep = lnSep * soc
lnStr = Len(s) : lnBStr = lnStr * soc
If lnStr <= 0 : ProcedureReturn 0 : EndIf
count = CountString(s,multiCharSep)
If count <= 0
Dim a(0) : a(0) = s
ProcedureReturn 0
EndIf
Dim a(count)
i = 0 : ss = 0 : ee = 0
While ee < lnBStr
If CompareMemory(@s + ee, @multiCharSep, lnBSep)
a(i) = PeekS(@s + ss, (ee-ss)/soc)
ss = ee + lnBSep: ee = ss: i+1
Else
ee + soc
EndIf
Wend
If i < count : a(count) = PeekS(@s + ss, (ee-ss)/soc) : EndIf
ProcedureReturn count
EndProcedure
; ----
Macro ResetSelection(Editor)
Editor\SelStart = 0 : Editor\SelEnd = 0
EndMacro
Macro HasSelection(Editor)
Bool( (Editor\SelStart <> 0) And (Editor\SelEnd <> 0) And (Editor\SelStart <> Editor\SelEnd) )
EndMacro
Macro IsValueInRange(_I, _S, _E)
Bool( ((_S < _I) And ( _I < _E)) Or ((_E < _I) And (_I < _S)) )
EndMacro
Procedure AdjustSelection(*E.TEditor, Token)
If Token
If *E\SelStart = 0
*E\SelStart = Token
*E\SelEnd = Token
Else
*E\SelEnd = Token
EndIf
EndIf
EndProcedure
Procedure IndexOfToken(*E.TEditor, Token)
Protected i
If Token
PushListPosition(*E\Chars())
ChangeCurrentElement(*E\Chars(), Token)
i = ListIndex(*E\Chars())
PopListPosition(*E\Chars())
EndIf
ProcedureReturn i
EndProcedure
;---- Lines
Procedure NextWord(*E.TEditor, Starting, MaxWidth)
; find the suitable next word that fits in MaxWidth (a space is a word)
; Return : 0 --> error / +i ---> Valid / -1 ---> word is too big
Protected wdt
If Starting
ChangeCurrentElement(*E\Chars(), Starting)
If *E\Chars()\C = #LineStart : ProcedureReturn 0 : EndIf
If *E\Chars()\C = 32
If *E\Chars()\W <= MaxWidth
ProcedureReturn Starting
Else
ProcedureReturn -1
EndIf
EndIf
Repeat
If *E\Chars()\C = 32 Or *E\Chars()\C = #LineStart
PreviousElement(*E\Chars())
ProcedureReturn @*E\Chars()
EndIf
If wdt + *E\Chars()\W <= MaxWidth
wdt + *E\Chars()\W
Else
ProcedureReturn -1 ; next word is too big to fit in available width
EndIf
If ListIndex(*E\Chars()) = (ListSize(*E\Chars()) - 1 )
ProcedureReturn @*E\Chars()
EndIf
Until NextElement(*E\Chars()) = 0
EndIf
ProcedureReturn 0 ; wrong Starting
EndProcedure
Procedure CutWord(*E.TEditor, Starting, MaxWidth)
; called when NextWord() fails with -1
Protected wdt, ret
If Starting
ChangeCurrentElement(*E\Chars(), Starting)
Repeat
If (*E\Chars()\C = 32) Or (*E\Chars()\C = #LineStart) Or (wdt + *E\Chars()\W > MaxWidth)
PreviousElement(*E\Chars())
ProcedureReturn @*E\Chars()
EndIf
wdt + *E\Chars()\W
ret = @*E\Chars()
Until NextElement(*E\Chars()) = 0
EndIf
ProcedureReturn ret
EndProcedure
Procedure StartOfCurrentLine(*E.TEditor)
; return the address of line-start
Repeat
If *E\Chars()\C = #LineStart : ProcedureReturn @*E\Chars() : EndIf
Until PreviousElement(*E\Chars()) = 0
ProcedureReturn 0
EndProcedure
Procedure BuildRowsOfLine(*E.TEditor, StartLine)
; creates Rows of Line starting at StartLine ---> inserts additional #RowStart
Protected i,j, wdt_max, wdt_row, str_row, alg_row, s,h, *token
If StartLine = 0 : ProcedureReturn : EndIf
ChangeCurrentElement(*E\Chars(), StartLine)
alg_row = *E\Chars()\A
; clean any previous row-start in that line
While NextElement(*E\Chars())
; skip the first #LineStart defining the start of this line
If str_row = 0 : str_row = @*E\Chars() : EndIf
If *E\Chars()\C = #LineStart : Break : EndIf
If *E\Chars()\C = #RowStart
If *E\TokenCarat = @*E\Chars()
If PreviousElement(*E\Chars())
*token = @*E\Chars() ; temporarly places the token on previous element
NextElement(*E\Chars())
EndIf
EndIf
DeleteElement(*E\Chars())
EndIf
Wend
If str_row = 0 ; empty last line
*E\Chars()\H = *E\Styles(0)\RowHeight
ProcedureReturn
EndIf
wdt_max = *E\W - (*E\MarginLeft + *E\MarginRight)
i = str_row
Repeat
j = NextWord(*E, i, wdt_max - wdt_row)
If j = 0 : Break : EndIf
If j > 0
ChangeCurrentElement(*E\Chars(), i)
Repeat
wdt_row + *E\Chars()\W
If @*E\Chars() = j : Break : EndIf
Until NextElement(*E\Chars()) = 0
; i= j+1
ChangeCurrentElement(*E\Chars(), j)
If NextElement(*E\Chars()) = 0 : Break : EndIf
i = @*E\Chars()
Else
ChangeCurrentElement(*E\Chars(), i)
If wdt_row = 0
j = CutWord(*E, i, wdt_max)
ChangeCurrentElement(*E\Chars(), i)
Repeat
wdt_row + *E\Chars()\W
If @*E\Chars() = j : Break : EndIf
Until NextElement(*E\Chars()) = 0
; i= j+1
ChangeCurrentElement(*E\Chars(), j)
If NextElement(*E\Chars()) = 0 : Break : EndIf
i = @*E\Chars()
Else
s = *E\Chars()\S
h = *E\Chars()\H
InsertElement(*E\Chars())
*E\Chars()\C = #RowStart
*E\Chars()\A = alg_row
*E\Chars()\S = s
*E\Chars()\H = h
wdt_row = 0
EndIf
EndIf
ForEver
;
If *token
ChangeCurrentElement(*E\Chars(), *token)
*E\TokenCarat = *token
If NextElement(*E\Chars())
If *E\Chars()\C = #RowStart : *E\TokenCarat = @*E\Chars() : EndIf
EndIf
EndIf
EndProcedure
Procedure BuildRows(*E.TEditor)
; creates all Rows
Protected NewList lst()
ForEach *E\Chars()
If *E\Chars()\C = #LineStart
AddElement(lst()) : lst() = @*E\Chars()
EndIf
Next
ForEach lst()
BuildRowsOfLine(*E, lst())
Next
EndProcedure
Procedure DeleteSelection(*E.TEditor)
Protected i, n, i1, i2, t1,t2, L
If HasSelection(*E) = #False : ProcedureReturn : EndIf
t1 = *E\SelStart : i1 = IndexOfToken(*E, t1)
t2 = *E\SelEnd : i2 = IndexOfToken(*E, t2)
If i1 > i2
Swap i1, i2 : Swap t1, t2
EndIf
n = i2 - i1
If ChangeCurrentElement(*E\Chars(), t2)
Repeat
DeleteElement(*E\Chars()) : i + 1
*E\TokenCarat = @*E\Chars()
If i >= n : Break : EndIf
Until ListSize(*E\Chars()) <= 1
EndIf
L = StartOfCurrentLine(*E)
BuildRowsOfLine(*E, L)
ResetSelection(*E)
EndProcedure
Procedure.i StartOfRow(*E.TEditor, Row)
Protected n
If Row > 0
ForEach *E\Chars()
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : n+1 : EndIf
If n = Row
ProcedureReturn @*E\Chars()
EndIf
Next
EndIf
ProcedureReturn 0
EndProcedure
Procedure.i EndOfRow(*E.TEditor, StartRow)
Protected ret
If StartRow
ChangeCurrentElement(*E\Chars(), StartRow)
ret = StartRow
While NextElement(*E\Chars())
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : Break : EndIf
ret = @*E\Chars()
Wend
EndIf
ProcedureReturn ret
EndProcedure
Procedure.i NextRow(*E.TEditor, Token)
; return the start of row coming AFTER Token, Token is an address in Chars()
If Token
ChangeCurrentElement(*E\Chars(), Token)
While NextElement(*E\Chars())
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : ProcedureReturn @*E\Chars() : EndIf
Wend
EndIf
ProcedureReturn 0
EndProcedure
Procedure.i AddChar(*E.TEditor, C.c)
; user input, usual text adds the char C after current Carat
Protected i,n, img, L, s
If *E\ReadOnly : ProcedureReturn : EndIf
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
s = *E\UserStyle : If s < 0 : s = *E\Chars()\S : EndIf
L = StartOfCurrentLine(*E)
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
If AddElement(*E\Chars())
*E\TokenCarat = @*E\Chars()
Img = CreateImage(#PB_Any, 1, 1)
If Img And StartDrawing(ImageOutput(Img))
DrawingFont( *E\Styles(s)\Font_Id )
*E\Chars()\W = TextWidth( Chr(C) )
*E\Chars()\H = TextHeight( Chr(C) )
*E\Chars()\C = C
*E\Chars()\S = s
StopDrawing()
FreeImage(Img)
EndIf
BuildRowsOfLine(*E, L)
ProcedureReturn #True
EndIf
ProcedureReturn #False
EndProcedure
Procedure.i ProcessKey(*E.TEditor, Ky)
; user input, special key (Backspace, del, enter, ...)
Protected i,a,s,h, L
If *E\ReadOnly : ProcedureReturn : EndIf
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
h = *E\Chars()\H
Select ky
Case #PB_Shortcut_Back
If ListIndex(*E\Chars()) > 0
While *E\Chars()\C = #RowStart : PreviousElement(*E\Chars()) : Wend
DeleteElement(*E\Chars())
*E\TokenCarat = @*E\Chars()
L = StartOfCurrentLine(*E)
BuildRowsOfLine(*E, L)
EndIf
Case #PB_Shortcut_Delete
If NextElement(*E\Chars())
While *E\Chars()\C = #RowStart : NextElement(*E\Chars()) : Wend
DeleteElement(*E\Chars())
L = StartOfCurrentLine(*E)
BuildRowsOfLine(*E, L)
EndIf
Case #PB_Shortcut_Return
L = StartOfCurrentLine(*E)
a = *E\Chars()\A
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
If AddElement(*E\Chars())
*E\Chars()\C = #LineStart
*E\Chars()\H = h
*E\Chars()\A = a
*E\TokenCarat = @*E\Chars()
;BuildRows(*E)
BuildRowsOfLine(*E, @*E\Chars())
BuildRowsOfLine(*E, L)
ProcedureReturn #True
EndIf
Case #PB_Shortcut_Tab ; ?
EndSelect
ProcedureReturn #False
EndProcedure
Macro AddCell(Editor, _X,_Y,_H, _R,_I)
AddElement(Editor\Cells())
Editor\Cells()\X = _X
Editor\Cells()\Y = _Y
Editor\Cells()\H = _H
Editor\Cells()\R = _R
Editor\Cells()\I = _I
EndMacro
Procedure.i BuildCells(*E.TEditor)
;
Protected i, x, y, w, h, row, s,e, a, wdt_max, hgt_max
wdt_max = *E\W - (*E\MarginLeft + *E\MarginRight)
hgt_max = *E\H - (*E\MarginTop + *E\MarginDown)
x = *E\MarginLeft
y = *E\MarginTop
; we start drawing at TopRow
If *E\TopRow < 1 : *E\TopRow = 1 : EndIf
row = *E\TopRow
s = StartOfRow(*E, row)
ClearList(*E\Cells())
Repeat
If s <= 0 : Break : EndIf
e = EndOfRow(*E, s)
w = 0 : h = 0
ChangeCurrentElement(*E\Chars(), s)
a = *E\Chars()\A
h = *E\Chars()\H
Repeat
w + *E\Chars()\W
If h < *E\Chars()\H : h = *E\Chars()\H : EndIf
If @*E\Chars() = e : Break : EndIf
Until NextElement(*E\Chars()) = 0
If h <= 0 : h = *E\Styles(0)\RowHeight : EndIf
If (y + h + *E\LineSpacing) > hgt_max : Break : EndIf
Select a ; we set starting x
Case #PB_Text_Right
x = *E\MarginLeft + (wdt_max - w)
Case #PB_Text_Center
x = *E\MarginLeft + Round((wdt_max - w)/2, #PB_Round_Up)
Default
x = *E\MarginLeft
EndSelect
ChangeCurrentElement(*E\Chars(), s)
Repeat
x + *E\Chars()\W
AddCell(*E, x, y + (h - *E\Chars()\H), *E\Chars()\H, row, @*E\Chars())
If @*E\Chars() = e : Break : EndIf
Until NextElement(*E\Chars()) = 0
s = NextRow(*E, e)
row + 1
y = y + h + *E\LineSpacing
ForEver
EndProcedure
Procedure CellOfCarat(*E.TEditor)
ForEach *E\Cells()
If *E\Cells()\I = *E\TokenCarat
*E\CellCarat_X = *E\Cells()\X
*E\CellCarat_Y = *E\Cells()\Y
*E\CellCarat_H = *E\Cells()\H
*E\CellCarat_V = #True
ProcedureReturn #True
EndIf
Next
*E\CellCarat_V = #False
ProcedureReturn #False
EndProcedure
Procedure BlinkCaratTimer()
Protected *E.TEditor
Static i
If GetActiveWindow() = EventWindow()
ForEach Editors()
*E = Val(MapKey(Editors()))
If *E\Window <> EventWindow() : Continue : EndIf
If *E\Gadget <> GetActiveGadget() : Continue : EndIf
If *E\CellCarat_V And StartDrawing(CanvasOutput(*E\Gadget))
SetOrigin(*E\X, *E\Y)
DrawingMode(#PB_2DDrawing_Default)
If i
Line(*E\CellCarat_X, *E\CellCarat_Y, 1, *E\CellCarat_H, *E\BkgColor)
i = 0
Else
Line(*E\CellCarat_X, *E\CellCarat_Y, 1, *E\CellCarat_H, *E\CaratColor)
i = 1
EndIf
StopDrawing()
EndIf
Next
EndIf
EndProcedure
Procedure AddCaratTimer(WinNbr)
; adds a timer to WinNbr with value #TimerNumber if not previousely added
Protected *E.TEditor, exist
ForEach Editors()
*E = Val(MapKey(Editors()))
If *E\Window = WinNbr : exist = #True : Break : EndIf
Next
If (exist = #False) And IsWindow(WinNbr)
AddWindowTimer(WinNbr, #TimerNumber, 400)
BindEvent(#PB_Event_Timer , @BlinkCaratTimer(), WinNbr)
EndIf
EndProcedure
Procedure RemoveCaratTimer(*E.TEditor)
; removes the timer #TimerNumber if no longer needed - last editor in that window about to be closed
Protected *t.TEditor, n
ForEach Editors()
*t = Val(MapKey(Editors()))
If *t\Window = *E\Window : n+1 : EndIf
Next
If n = 1
UnbindEvent(#PB_Event_Timer , @BlinkCaratTimer(), *E\Window)
RemoveWindowTimer(*E\Window, #TimerNumber)
EndIf
EndProcedure
Procedure.i DrawEditor(*E.TEditor, OptBuildCells = #True, OptShowRowHavingCarat = #True)
; draw as per current settings/counters
Protected i,j,n, x, y, h, Img, c.c, fClr, bClr, t, sel_s.f, sel_e.f
Protected carat_pos, cur_row, wdt_max, hgt_max
If *E\NoDrawing = #True : ProcedureReturn : EndIf
wdt_max = *E\W - (*E\MarginLeft + *E\MarginRight)
hgt_max = *E\H - (*E\MarginTop + *E\MarginDown)
x = *E\MarginLeft
y = *E\MarginTop
t = ElapsedMilliseconds()
If OptBuildCells : BuildCells(*E) : EndIf
If OptShowRowHavingCarat
If ShowRow(*E, RowHavingCarat(*E)) : BuildCells(*E) : EndIf
EndIf
If HasSelection(*E)
sel_s = IndexOfToken(*E, *E\SelStart) + 0.5
sel_e = IndexOfToken(*E, *E\SelEnd) + 0.5
EndIf
If StartDrawing(CanvasOutput(*E\Gadget))
SetOrigin(*E\X, *E\Y)
DrawingMode(#PB_2DDrawing_Default)
bClr = *E\BkgColor
Box(0,0,*E\W, *E\H,bClr)
If FirstElement(*E\Cells())
If *E\Cells()\I
ChangeCurrentElement(*E\Chars(), *E\Cells()\I)
Repeat
c = *E\Chars()\C
If c = #LineStart Or c = #RowStart
h = *E\Chars()\H
PushListPosition(*E\Chars())
While NextElement(*E\Chars())
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : Break : EndIf
If h < *E\Chars()\H : h = *E\Chars()\H : EndIf
Wend
PopListPosition(*E\Chars())
If h <= 0 : h = *E\Styles(*E\Chars()\S)\RowHeight : EndIf
Else
x = *E\Cells()\X - *E\Chars()\W
y = *E\Cells()\Y
DrawingFont( *E\Styles(*E\Chars()\S)\Font_Id )
fClr = *E\Styles(*E\Chars()\S)\TxtColor
If HasSelection(*E) And IsValueInRange(0.0 + ListIndex(*E\Chars()), sel_s, sel_e)
Box(x, y - (h - *E\Chars()\H), *E\Chars()\W, h, *E\SelBColor)
DrawText(x, y, Chr(c), *E\SelFColor, *E\SelBColor)
Else
DrawText(x, y, Chr(c), fClr, bClr)
EndIf
EndIf
NextElement(*E\Chars())
Until NextElement(*E\Cells()) = 0
EndIf
EndIf
; drawing carat
If CellOfCarat(*E)
Line(*E\Cells()\X, *E\Cells()\Y, 1, *E\Cells()\H, *E\CaratColor)
EndIf
StopDrawing()
EndIf
Debug " >>>>>>>>>>>>>>>>> DrawEditor time: " + Str( ElapsedMilliseconds() - t)
EndProcedure
;-------------------------------------------------------------------------------
Procedure LastVisibleRow(*E.TEditor)
If LastElement(*E\Cells()) : ProcedureReturn *E\Cells()\R : EndIf
EndProcedure
Procedure RowHavingCarat(*E.TEditor)
Protected n
ForEach *E\Chars()
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : n + 1 : EndIf
If @*E\Chars() = *E\TokenCarat : Break : EndIf
Next
ProcedureReturn n
EndProcedure
Procedure.i ShowRow(*E.TEditor, Row)
; makes sure the row defined by Row is visible, scrolls if needed ---> cahnges *E\TopRow
Protected i, y, hgt_max, h, old_top, s,e
If Row <= 0 : ProcedureReturn #False : EndIf
old_top = *E\TopRow
If Row < *E\TopRow
*E\TopRow = Row
Else
If LastElement(*E\Cells())
If Row > *E\Cells()\R
hgt_max = *E\H - (*E\MarginTop + *E\MarginDown)
y = *E\MarginTop
i = Row
s = StartOfRow(*E, Row)
If s
e = EndOfRow(*E, s)
ChangeCurrentElement(*E\Chars(), e)
While i >= 1
h = 0
Repeat
If *E\Chars()\C = #LineStart Or *E\Chars()\C = #RowStart : Break : EndIf
If h < *E\Chars()\H : h = *E\Chars()\H : EndIf
Until PreviousElement(*E\Chars()) = 0
If h <= 0 : h = *E\Styles(*E\Chars()\S)\RowHeight : EndIf
y = y + (h + *E\LineSpacing)
If y > hgt_max : Break : EndIf
*E\TopRow = i
i - 1
If PreviousElement(*E\Chars()) = 0 : Break : EndIf
Wend
EndIf
EndIf
EndIf
EndIf
ProcedureReturn Bool(old_top <> *E\TopRow)
EndProcedure
Procedure.i RelativeX(*E.TEditor, Canvas_X)
; return the reltive x within editor of the passed x based on parent-canvas
Protected x
If Canvas_X < *E\X : x = 0
ElseIf Canvas_X > *E\X + *E\W : x = *E\W
Else : x = Canvas_X - *E\X
EndIf
ProcedureReturn x
EndProcedure
Procedure.i RelativeY(*E.TEditor, Canvas_Y)
Protected y
If Canvas_Y < *E\Y : y = 0
ElseIf Canvas_Y > *E\Y + *E\H : y = *E\H
Else : y = Canvas_Y - *E\Y
EndIf
ProcedureReturn y
EndProcedure
; X, Y are coordinates within editor area (relative to editor coord)
; ----
Procedure SetCaratFromXY(*E.TEditor, X, Y)
Protected min_x, min_y, dlt, idx_r, idx_s, idx_v, rr
min_x = *E\W : min_y = *E\H
idx_s = -1 : idx_v = -1
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
ForEach *E\Cells() ; locating closest row
If *E\Cells()\R > rr ; rr = last checked row
dlt = Abs(*E\Cells()\Y + (*E\Cells()\H / 2) - Y)
If dlt < min_y
min_y = dlt
idx_s = ListIndex(*E\Cells())
idx_r = *E\Cells()\R
EndIf
rr = *E\Cells()\R
EndIf
Next
If idx_s >= 0 And SelectElement(*E\Cells(), idx_s)
Repeat
If *E\Cells()\R > idx_r : Break : EndIf
dlt = Abs(*E\Cells()\X - x)
If dlt < min_x
min_x = dlt
idx_v = ListIndex(*E\Cells())
EndIf
Until NextElement(*E\Cells()) = 0
EndIf
If idx_v >= 0 And SelectElement(*E\Cells(), idx_v)
If *E\TokenCarat <> *E\Cells()\I
*E\TokenCarat = *E\Cells()\I
*E\LastUsedX = *E\Cells()\X
*E\UserStyle = -1
EndIf
EndIf
EndProcedure
Procedure NextCarat(*E.TEditor)
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
If NextElement(*E\Chars()) : *E\TokenCarat = @*E\Chars() : EndIf
EndProcedure
Procedure PreviousCarat(*E.TEditor)
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
If PreviousElement(*E\Chars()) : *E\TokenCarat = @*E\Chars() : EndIf
EndProcedure
Procedure DownCarat(*E.TEditor, NbrRows = 1)
Protected i, x, dlt, mn = *E\W
If CellOfCarat(*E)
i = *E\Cells()\R + NbrRows
Else
i = RowHavingCarat(*E) + NbrRows
EndIf
x = *E\LastUsedX
If ShowRow(*E, i) : BuildCells(*E) : EndIf
ForEach *E\Cells()
If *E\Cells()\R = i
dlt = Abs(*E\Cells()\X - x)
If dlt <= mn : *E\TokenCarat = *E\Cells()\I : mn = dlt : EndIf
EndIf
If *E\Cells()\R > i : Break : EndIf
Next
EndProcedure
Procedure UpCarat(*E.TEditor, NbrRows = 1)
Protected i, x, dlt, mn = *E\W
If CellOfCarat(*E)
i = *E\Cells()\R - NbrRows
Else
i = RowHavingCarat(*E) - NbrRows
EndIf
x = *E\LastUsedX
If ShowRow(*E, i) : BuildCells(*E) : EndIf
ForEach *E\Cells()
If *E\Cells()\R = i
dlt = Abs(*E\Cells()\X - x)
If dlt <= mn : *E\TokenCarat = *E\Cells()\I : mn = dlt : EndIf
EndIf
If *E\Cells()\R > i : Break : EndIf
Next
EndProcedure
Procedure FirstCaratInRow(*E.TEditor)
Protected R
If CellOfCarat(*E)
R = *E\Cells()\R
While PreviousElement(*E\Cells())
If R <> *E\Cells()\R : Break : EndIf
*E\TokenCarat = *E\Cells()\I
Wend
EndIf
EndProcedure
Procedure LastCaratInRow(*E.TEditor)
Protected R
If CellOfCarat(*E)
R = *E\Cells()\R
While NextElement(*E\Cells())
If R <> *E\Cells()\R : Break : EndIf
*E\TokenCarat = *E\Cells()\I
Wend
EndIf
EndProcedure
Procedure NextWordCarat(*E.TEditor)
Protected i = -1
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
While NextElement(*E\Chars())
If *E\Chars()\C = 32 Or *E\Chars()\C = #LineStart : i = ListIndex(*E\Chars()) : Break : EndIf
Wend
If i >= 0
While *E\Chars()\C = 32
i = ListIndex(*E\Chars())
If NextElement(*E\Chars()) = 0 : Break : EndIf
Wend
SelectElement(*E\Chars(), i)
*E\TokenCarat = @*E\Chars()
EndIf
EndProcedure
Procedure PreviousWordCarat(*E.TEditor)
ChangeCurrentElement(*E\Chars(), *E\TokenCarat)
While PreviousElement(*E\Chars())
If *E\Chars()\C = 32 Or *E\Chars()\C = #LineStart : *E\TokenCarat = @*E\Chars() : Break : EndIf
Wend
EndProcedure
Procedure MoveCarat(*E.TEditor, Direction, sel = 0)
Protected upd_x = #True, old_c = *E\TokenCarat
If sel
AdjustSelection(*E, *E\TokenCarat)
Else
ResetSelection(*E)
EndIf
Select Direction
Case #Move_Right
NextCarat(*E)
Case #Move_Left
PreviousCarat(*E)
Case #Move_Down
DownCarat(*E) : upd_x = #False
Case #Move_Up
UpCarat(*E) : upd_x = #False
Case #Move_PageDown
DownCarat(*E, 20) : upd_x = #False
Case #Move_PageUp
UpCarat(*E, 20) : upd_x = #False
Case #Move_FirstInRow
FirstCaratInRow(*E)
Case #Move_LastInRow
LastCaratInRow(*E)
Case #Move_First
*E\TopRow = 1
FirstElement(*E\Chars())
*E\TokenCarat = @*E\Chars()
Case #Move_Last
LastElement(*E\Chars())
*E\TokenCarat = @*E\Chars()
Case #Move_NextWord
NextWordCarat(*E)
Case #Move_PreviousWord
PreviousWordCarat(*E)
EndSelect
If sel : AdjustSelection(*E, *E\TokenCarat) : EndIf
If old_c <> *E\TokenCarat : DrawEditor(*E) : EndIf
If upd_x
If CellOfCarat(*E) : *E\LastUsedX = *E\Cells()\X : EndIf
EndIf
*E\UserStyle = -1
EndProcedure
Procedure RelativePos(*E.TEditor, Pos)
Protected n, sep = Len(*E\LineSeparator)
If Pos <= 0 Or Pos > ListSize(*E\Chars()) : ProcedureReturn -1 : EndIf
If FirstElement(*E\Chars())
While NextElement(*E\Chars())
If *E\Chars()\C = #RowStart : Continue : EndIf
If *E\Chars()\C = #LineStart
n + sep
Else
n + 1
EndIf
If n = Pos : ProcedureReturn ListIndex(*E\Chars()) : EndIf
Wend
EndIf
ProcedureReturn -1
EndProcedure
;---- Interface
Procedure.i NoRedraw(Editor)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
*E\NoDrawing = #True
EndProcedure
Procedure.i Redraw(Editor)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
*E\NoDrawing = #False
DrawEditor(*E)
EndProcedure
Procedure.i SetAttribute(Editor, Attribute = #MyEditor_BackColor, Value = $FFFFFF)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
If Value < 0 : ProcedureReturn : EndIf
Select Attribute
Case #MyEditor_BackColor : *E\BkgColor = Value
Case #MyEditor_SelectionBackColor : *E\SelBColor = Value
Case #MyEditor_SelectionTextColor : *E\SelFColor = Value
Case #MyEditor_CaratColor : *E\CaratColor = Value
Case #MyEditor_Align : *E\Align = Value
Case #MyEditor_ReadOnly : *E\ReadOnly = Value
Case #MyEditor_LineSpacing : *E\LineSpacing = Value
EndSelect
EndProcedure
Procedure.i GetAttribute(Editor, Attribute = #MyEditor_BackColor)
Protected V, *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
Select Attribute
Case #MyEditor_BackColor : V = *E\BkgColor
Case #MyEditor_SelectionBackColor : V = *E\SelBColor
Case #MyEditor_SelectionTextColor : V = *E\SelFColor
Case #MyEditor_CaratColor : V = *E\CaratColor
Case #MyEditor_Align : V = *E\Align
Case #MyEditor_ReadOnly : V = *E\ReadOnly
Case #MyEditor_LineSpacing : V = *E\LineSpacing
EndSelect
ProcedureReturn V
EndProcedure
Procedure.i SetCaratPos(Editor, Pos = #MyEditor_LastCaratPos)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
Protected i
Select Pos
Case #MyEditor_FirstCaratPos
If FirstElement(*E\Chars()) : *E\TokenCarat = @*E\Chars() : EndIf
Case #MyEditor_LastCaratPos
If LastElement(*E\Chars()) : *E\TokenCarat = @*E\Chars() : EndIf
Default
i = RelativePos(*E, Pos)
If i >= 0 And SelectElement(*E\Chars(), i)
*E\TokenCarat = @*E\Chars() :
EndIf
EndSelect
DrawEditor(*E)
EndProcedure
Procedure.i SelectText(Editor, Style, Start, Length = -1)
Protected *E.TEditor = Editor
Protected s,e
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
s = RelativePos(*E, Start)
If Length = -1
e = ListSize(*E\Chars()) - 1
Else
e = RelativePos(*E, Start+Length-1)
EndIf
If s = -1 Or e = -1 : ProcedureReturn : EndIf
If SelectElement(*E\Chars(), s)
PreviousElement(*E\Chars())
s = @*E\Chars()
If SelectElement(*E\Chars(), e)
*E\SelStart = s
*E\SelEnd = @*E\Chars()
EndIf
EndIf
DrawEditor(*E)
EndProcedure
Procedure.i ManageEvent(Editor.i, eType)
; Gdt is the canvas gadget that first received this event and passed it to its associated Editor
Protected Gdt.i, *E.TEditor = Editor
Protected ky,mf, x,y, dlt,i, sel
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
Gdt = *E\Gadget
Select eType
Case #PB_EventType_KeyDown
ky = GetGadgetAttribute(gdt, #PB_Canvas_Key )
mf = GetGadgetAttribute(gdt, #PB_Canvas_Modifiers )
If mf & #PB_Canvas_Shift : sel = 1 : EndIf
;If sel = 0 : ResetSelection(*E) : EndIf
If mf & #PB_Canvas_Control
Select ky
Case #PB_Shortcut_Left : MoveCarat(*E, #Move_PreviousWord, sel)
Case #PB_Shortcut_Right : MoveCarat(*E, #Move_NextWord, sel)
Case #PB_Shortcut_Up :
Case #PB_Shortcut_Down :
Case #PB_Shortcut_Home : MoveCarat(*E, #Move_First, sel)
Case #PB_Shortcut_End : MoveCarat(*E, #Move_Last, sel)
Case #PB_Shortcut_PageUp : MoveCarat(*E, #Move_PageUp, sel)
Case #PB_Shortcut_PageDown : MoveCarat(*E, #Move_PageDown, sel)
EndSelect
Else
Select ky
Case #PB_Shortcut_Left : MoveCarat(*E, #Move_Left, sel)
Case #PB_Shortcut_Right : MoveCarat(*E, #Move_Right, sel)
Case #PB_Shortcut_Up : MoveCarat(*E, #Move_Up, sel)
Case #PB_Shortcut_Down : MoveCarat(*E, #Move_Down, sel)
Case #PB_Shortcut_Home : MoveCarat(*E, #Move_FirstInRow, sel)
Case #PB_Shortcut_End : MoveCarat(*E, #Move_LastInRow, sel)
Case #PB_Shortcut_PageUp : MoveCarat(*E, #Move_PageUp, sel)
Case #PB_Shortcut_PageDown : MoveCarat(*E, #Move_PageDown, sel)
EndSelect
EndIf
Select ky
Case #PB_Shortcut_Delete, #PB_Shortcut_Back, #PB_Shortcut_Tab
If HasSelection(*E)
DeleteSelection(*E)
Else
ProcessKey(*E, Ky)
EndIf
DrawEditor(*E)
Case #PB_Shortcut_Return
DeleteSelection(*E)
ProcessKey(*E, Ky)
DrawEditor(*E)
EndSelect
Case #PB_EventType_Input ; add a new char after current element
DeleteSelection(*E)
AddChar(*E, GetGadgetAttribute(gdt, #PB_Canvas_Input))
DrawEditor(*E)
Case #PB_EventType_MouseWheel
dlt = GetGadgetAttribute(gdt, #PB_Canvas_WheelDelta)
If dlt < 0
If LastElement(*E\Cells())
If ShowRow(*E, *E\Cells()\R + 1)
DrawEditor(*E, #True, #False)
EndIf
EndIf
ElseIf dlt > 0
If FirstElement(*E\Cells())
If ShowRow(*E, *E\Cells()\R - 1)
DrawEditor(*E, #True, #False)
EndIf
EndIf
EndIf
Case #PB_EventType_LeftDoubleClick
Case #PB_EventType_MouseEnter
Case #PB_EventType_MouseMove ; selecting via mouse
x = RelativeX(*E, GetGadgetAttribute(gdt, #PB_Canvas_MouseX))
y = RelativeY(*E, GetGadgetAttribute(gdt, #PB_Canvas_MouseY))
If GetGadgetAttribute(gdt, #PB_Canvas_Buttons) = #PB_Canvas_LeftButton
If y >= *E\H - *E\MarginDown
i = LastVisibleRow(*E) + 1
If ShowRow(*E, i)
BuildCells(*E)
EndIf
ElseIf y <= *E\MarginTop
i = *E\TopRow - 1
If ShowRow(*E, i)
BuildCells(*E)
EndIf
EndIf
SetCaratFromXY(*E, x, y)
AdjustSelection(*E, *E\TokenCarat)
DrawEditor(*E, #False, #True)
EndIf
Case #PB_EventType_MouseLeave
Case #PB_EventType_LeftButtonUp
Case #PB_EventType_LeftButtonDown
x = RelativeX(*E, GetGadgetAttribute(gdt, #PB_Canvas_MouseX))
y = RelativeY(*E, GetGadgetAttribute(gdt, #PB_Canvas_MouseY))
ResetSelection(*E)
SetCaratFromXY(*E, x, y)
DrawEditor(*E, #False, #False)
Case #PB_EventType_RightButtonDown
Default
ProcedureReturn #False
EndSelect
EndProcedure
Procedure.i Resize(Editor.i, X = #PB_Ignore, Y = #PB_Ignore, W = #PB_Ignore, H = #PB_Ignore)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
If X <> #PB_Ignore : *E\X = X : EndIf
If Y <> #PB_Ignore : *E\Y = Y : EndIf
If W <> #PB_Ignore : *E\W = W : EndIf
If H <> #PB_Ignore : *E\H = H : EndIf
BuildRows(*E)
DrawEditor(*E)
EndProcedure
Procedure.i AddStyle(Editor.i, FontNbr, ForeColor.i)
Protected *E.TEditor = Editor, img, n
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
n = ArraySize(*E\Styles())
If *E\Styles(n)\RowHeight > 0
n = n + 1
ReDim *E\Styles(n)
EndIf
*E\Styles(n)\TxtColor = ForeColor
If IsFont(FontNbr)
*E\Styles(n)\Font_Id = FontID(FontNbr)
Else
*E\Styles(n)\Font_Id = #PB_Default
EndIf
Img = CreateImage(#PB_Any, 1, 1)
If Img
If StartDrawing(ImageOutput(Img))
DrawingFont(*E\Styles(n)\Font_Id)
*E\Styles(n)\RowHeight = TextHeight("A")
StopDrawing()
EndIf
FreeImage(Img)
EndIf
ProcedureReturn n
EndProcedure
Procedure.i AddLineOfText(Editor.i, Txt.s, Align = #PB_Default)
; adds a new line of text at the end
Protected *E.TEditor = Editor
Protected i, n, img, s, *c.Character, L
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
If Align = #PB_Default : Align = *E\Align : EndIf
n = FindString(Txt, *E\LineSeparator) - 1
If n = -1 : n = Len(Txt) : EndIf
LastElement(*E\Chars())
s = *E\UserStyle : If s < 0 : s = *E\Chars()\S : EndIf
If AddElement(*E\Chars())
*E\Chars()\C = #LineStart
*E\Chars()\A = Align
*E\Chars()\S = s
*E\Chars()\H = *E\Styles(s)\RowHeight
L = @*E\Chars()
EndIf
Img = CreateImage(#PB_Any, 1, 1)
If Img
If StartDrawing(ImageOutput(Img))
DrawingFont(*E\Styles(s)\Font_Id)
For i = 1 To n
*c = @Txt + ((i-1) * SizeOf(Character))
If AddElement(*E\Chars())
*E\Chars()\C = *c\c
*E\Chars()\H = TextHeight( Chr(*c\c) )
*E\Chars()\W = TextWidth( Chr(*c\c) )
*E\Chars()\S = s
*E\TokenCarat = @*E\Chars()
EndIf
Next
StopDrawing()
EndIf
FreeImage(Img)
EndIf
BuildRowsOfLine(*E, L)
ProcedureReturn #True
EndProcedure
Procedure.i SetText(Editor, Txt.s)
Protected *E.TEditor = Editor
Protected i, Dim a.s(0)
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
MySplitString(Txt, *E\LineSeparator, a())
For i=0 To ArraySize(a())
AddLineOfText(*E, a(i))
Next
EndProcedure
Procedure.s GetText(Editor)
Protected *E.TEditor = Editor
Protected i, ret.s, NewList sp.c(), NewList txt.c()
If Editors(Str(*E)) = 0 : ProcedureReturn "" : EndIf
For i = 1 To Len(*E\LineSeparator)
If AddElement(sp()) : sp() = Asc(Mid(*E\LineSeparator, i, 1)) : EndIf
Next
If FirstElement(*E\Chars())
While NextElement(*E\Chars())
If *E\Chars()\C = #RowStart : Continue : EndIf
If *E\Chars()\C = #LineStart
ForEach sp()
AddElement(txt()) : txt() = sp()
Next
;MergeLists(sp(), txt(), #PB_List_Last) : LastElement(txt())
Else
AddElement(txt())
txt() = *E\Chars()\C
EndIf
Wend
EndIf
ret = Space(ListSize(txt()))
i = 0
ForEach txt()
PokeC(@ret + (i * SizeOf(Character)), txt())
i+1
Next
ProcedureReturn ret
EndProcedure
Procedure.i AssignStyle(Editor, Style, Start, Length = -1)
Protected *E.TEditor = Editor
Protected s,e, L, img
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
If Style < 0 Or Style > ArraySize(*E\Styles()) : ProcedureReturn : EndIf
s = RelativePos(*E, Start)
If Length = -1
e = ListSize(*E\Chars()) - 1
Else
e = RelativePos(*E, Start+Length-1)
EndIf
If s = -1 Or e = -1 : ProcedureReturn : EndIf
Img = CreateImage(#PB_Any, 1, 1)
If Img
If StartDrawing(ImageOutput(Img))
DrawingFont(*E\Styles(Style)\Font_Id)
If SelectElement(*E\Chars(), s)
PushListPosition(*E\Chars())
L = StartOfCurrentLine(*E)
PopListPosition(*E\Chars())
Repeat
If ListIndex(*E\Chars()) > e : Break : EndIf
*E\Chars()\S = Style
*E\Chars()\H = TextHeight( Chr( *E\Chars()\C ) )
*E\Chars()\W = TextWidth( Chr( *E\Chars()\C ) )
Until NextElement(*E\Chars()) = 0
EndIf
StopDrawing()
EndIf
FreeImage(Img)
EndIf
BuildRowsOfLine(*E, L)
DrawEditor(*E)
EndProcedure
Procedure.i UseStyle(Editor, Style)
Protected *E.TEditor = Editor
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
If Style >= 0 And Style <= ArraySize(*E\Styles()) : *E\UserStyle = Style : EndIf
EndProcedure
Procedure.s CloseEditor(Editor)
; close the editor and return its content
Protected *E.TEditor = Editor
Protected ret.s
If Editors(Str(*E)) = 0 : ProcedureReturn : EndIf
ret = GetText(*E)
; restore origin in parent canvas to (0,0)
If StartDrawing(CanvasOutput(*E\Gadget))
SetOrigin(0, 0)
StopDrawing()
EndIf
RemoveCaratTimer(*E)
SetGadgetAttribute(*E\Gadget, #PB_Canvas_Cursor, *E\Cursor)
FreeStructure(*E)
DeleteMapElement(Editors(), Str(Editor))
ProcedureReturn ret
EndProcedure
Procedure.i OpenEditor(WinNbr, Gadget, Text.s, X=#PB_Default, Y=#PB_Default, W=#PB_Default, H=#PB_Default, FontNbr = -1, LineSep.s = #LF$)
; return an Editor (a raw pointer to TEditor)
Protected *E.TEditor
If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Canvas
If X = #PB_Default : X = 0 : EndIf
If Y = #PB_Default : Y = 0 : EndIf
If W = #PB_Default : W = GadgetWidth(Gadget) : EndIf
If H = #PB_Default : H = GadgetHeight(Gadget) : EndIf
Else
MessageRequester("Error","Gadget needs to be an existing canvas gadget!")
ProcedureReturn 0
EndIf
*E = AllocateStructure(TEditor)
Editors(Str(*E))= 1
AddCaratTimer(WinNbr) ; adds a carat-timer if not added to that window already
*E\Window = WinNbr
*E\Gadget = Gadget
*E\X = X
*E\Y = Y
*E\W = W
*E\H = H
*E\LineSeparator= LineSep
*E\MarginTop = 2
*E\MarginDown = 2
*E\MarginLeft = 1
*E\MarginRight = 1
*E\BkgColor = RGB(255,255,255)
*E\SelBColor = RGB(30, 144, 255)
*E\SelFColor = *E\BkgColor
*E\CaratColor = RGB(255, 0, 0)
;*E\Align = #PB_Text_Center
*E\LineSpacing = 0
If *E\Align <> #PB_Text_Right And *E\Align <> #PB_Text_Center : *E\Align = 0 : EndIf
*E\Cursor = GetGadgetAttribute(Gadget, #PB_Canvas_Cursor)
SetGadgetAttribute(Gadget, #PB_Canvas_Cursor, #PB_Cursor_IBeam)
AddStyle(*E, FontNbr, RGB(0, 0, 139)) ; adding default style
SetText(*E, Text)
If FirstElement(*E\Chars()) : *E\TokenCarat = @*E\Chars() : EndIf
*E\UserStyle = -1
ResetSelection(*E)
DrawEditor(*E)
ProcedureReturn *E
EndProcedure
EndModule