MODULE WMEditors;
IMPORT
Inputs, Strings, XML, PositionDebugging, UTF8Strings, Texts, TextUtilities, UndoManager, HostClipboard, KernelLog,
Types, Models,
Raster, WMRectangles, WMGraphics, WMGraphicUtilities, WMEvents, WMProperties, WMDropTarget,
WMWindowManager, WMComponents, WMStandardComponents, WMTextView, WMInputMethods, D:= Debugging, Reals;
CONST
None* = 0;
Decimal* = 1;
Hex* = 2;
Ascii* = 3;
Unicode* = 4;
InitialStringSize= 32;
MaxStringSize = 2048;
InterclickNone = 0;
Interclick01 = 1;
Interclick02 = 2;
InterclickCancelled = 99;
DragMinDistance = 5;
TextBorder = 4;
TYPE
SetStringProcedure = PROCEDURE {DELEGATE} (CONST string : ARRAY OF CHAR; position : LONGINT; VAR res : LONGINT);
DropTarget = OBJECT(WMDropTarget.DropTarget)
VAR
originator : ANY;
setString : SetStringProcedure;
position : LONGINT;
PROCEDURE &Init(originator : ANY; setString : SetStringProcedure; position : LONGINT);
BEGIN
ASSERT(setString # NIL);
SELF.originator := originator;
SELF.setString := setString;
SELF.position := position;
END Init;
PROCEDURE GetInterface(type : LONGINT) : WMDropTarget.DropInterface;
VAR sdi : DropString;
BEGIN
IF (type = WMDropTarget.TypeString) THEN
NEW(sdi, originator, setString, position); RETURN sdi;
ELSE
RETURN NIL;
END;
END GetInterface;
END DropTarget;
DropString = OBJECT(WMDropTarget.DropString)
VAR
originator : ANY;
setString : SetStringProcedure;
position : LONGINT;
PROCEDURE &Init(originator : ANY; setString : SetStringProcedure; position : LONGINT);
BEGIN
ASSERT(setString # NIL);
SELF.originator := originator;
SELF.setString := setString;
SELF.position := position;
END Init;
PROCEDURE Set(CONST string : ARRAY OF CHAR; VAR res : LONGINT);
BEGIN
setString(string, position, res);
END Set;
END DropString;
TextField* = OBJECT(WMStandardComponents.Panel)
VAR
type- : WMProperties.Int32Property;
typeI : LONGINT;
readOnly- : WMProperties.BooleanProperty;
readOnlyI : BOOLEAN;
textBorder-: WMProperties.Int32Property;
textColor- : WMProperties.ColorProperty;
textColorI : LONGINT;
alignH- : WMProperties.Int32Property;
alignV- : WMProperties.Int32Property;
onEscape-, onEnter-, onChanged- : WMEvents.EventSource;
ime : WMInputMethods.IME;
currentFlags : SET;
string : Texts.PUCS32String;
cursorPosition : LONGINT;
curLen : LONGINT;
selection : RECORD start, end : LONGINT; END;
cursorIsVisible : BOOLEAN;
selecting, doubleClicking : BOOLEAN;
selectingCursorPos : LONGINT;
interclick : LONGINT;
dragPossible : BOOLEAN;
dragString : Strings.String;
dragCopy : BOOLEAN;
changeDue: BOOLEAN;
renderOffsetX : LONGINT;
lastX, lastY : LONGINT;
PROCEDURE &Init*;
BEGIN
Init^;
SetNameAsString(GSTextField);
SetGenerator("WMEditors.GenTextField");
NEW(type, typeProto, NIL, NIL); properties.Add(type);
NEW(readOnly, readOnlyProto, NIL, NIL); properties.Add(readOnly);
NEW(textColor, textColorProto, NIL, NIL); properties.Add(textColor);
NEW(textBorder, PrototypeTextBorder,NIL,NIL); properties.Add(textBorder);
NEW(onEscape, SELF, GSonEscape, GSonEscapeInfo, SELF.StringToCompCommand); events.Add(onEscape);
NEW(onEnter, SELF, GSonEnter, GSonEnterInfo, SELF.StringToCompCommand); events.Add(onEnter);
NEW(onChanged, SELF, NIL, NIL, NIL); events.Add(onChanged);
NEW(alignH, PrototypeAlignH, NIL, NIL); properties.Add(alignH);
NEW(alignV, PrototypeAlignV, NIL, NIL); properties.Add(alignV);
ime := WMInputMethods.activeIME;
currentFlags := {};
string := NIL;
cursorPosition := 0;
curLen := 0;
selection.start := -1; selection.end := -1;
cursorIsVisible := FALSE;
selecting := FALSE; doubleClicking := FALSE;
selectingCursorPos := -1;
interclick := InterclickNone;
dragPossible := FALSE;
dragString := NIL;
dragCopy := FALSE;
renderOffsetX := textBorder.Get();
lastX := -1; lastY := -1;
SetPointerInfo(manager.pointerText);
takesFocus.Set(TRUE);
SetAttributeValue("Value","");
changeDue := FALSE;
END Init;
PROCEDURE Initialize;
BEGIN
Initialize^;
Resized;
RecacheProperties;
END Initialize;
PROCEDURE SetCurrentCursorVisibility(isVisible : BOOLEAN);
BEGIN
IF (cursorIsVisible # isVisible) THEN
cursorIsVisible := isVisible;
Invalidate;
END;
END SetCurrentCursorVisibility;
PROCEDURE PropertyChanged(sender, property : ANY);
VAR f: WMGraphics.Font;
PROCEDURE TextHeight(): LONGINT;
BEGIN
RETURN MAX (bounds.GetHeight()-2*textBorder.Get(),4);
END TextHeight;
BEGIN
IF (property = type) THEN
typeI := type.Get();
ELSIF (property = readOnly) THEN
readOnlyI := readOnly.Get();
Invalidate;
ELSIF (property = textColor) THEN
textColorI := textColor.Get();
Invalidate;
ELSIF (property = font) THEN Invalidate;
ELSIF (property = alignH) OR (property = alignV) THEN Invalidate
ELSIF (property = model) THEN Invalidate
ELSIF (property = scaleFont)& (scaleFont.Get()>0) THEN
ScaleFont(TextHeight(),scaleFont.Get());
ELSE
IF (property = bounds) & (scaleFont.Get()>0) THEN ScaleFont(TextHeight(),scaleFont.Get()); END;
PropertyChanged^(sender, property);
END;
END PropertyChanged;
PROCEDURE RecacheProperties;
BEGIN
RecacheProperties^;
typeI := type.Get();
readOnlyI := readOnly.Get();
textColorI := textColor.Get();
Invalidate;
END RecacheProperties;
PROCEDURE CheckSize(min : LONGINT);
VAR size : LONGINT;
PROCEDURE RoundUpSize(min : LONGINT) : LONGINT;
VAR size : LONGINT;
BEGIN
size := InitialStringSize;
WHILE (min > size) DO size := size * 2; END;
IF (size > MaxStringSize) THEN size := MaxStringSize; END;
RETURN size;
END RoundUpSize;
PROCEDURE GrowTo(size : LONGINT);
VAR newString : Texts.PUCS32String; i : LONGINT;
BEGIN
ASSERT((size > LEN(string)) & (size <= MaxStringSize));
NEW(newString, size);
FOR i := 0 TO LEN(string)-1 DO newString[i] := string[i]; END;
string := newString;
END GrowTo;
BEGIN
IF (string = NIL) THEN
size := RoundUpSize(min);
NEW(string, size); string[0] := 0;
ELSIF (LEN(string) < min) THEN
size := RoundUpSize(min);
IF (size > LEN(string)) THEN GrowTo(size); END;
END;
END CheckSize;
PROCEDURE GetAsString*(VAR x : ARRAY OF CHAR);
BEGIN
Acquire;
IF (string = NIL) THEN
x[0] := 0X;
ELSE
UTF8Strings.UnicodetoUTF8(string^, x);
x[LEN(x)-1] := 0X;
END;
Release;
END GetAsString;
PROCEDURE SetAsString*(CONST x : ARRAY OF CHAR);
VAR i, length : LONGINT;
BEGIN
ASSERT(UTF8Strings.Valid(x));
IF IsAllowedString(x) THEN
Acquire;
IF (string # NIL) & (UTF8Strings.CompareToUnicode(x, string^) = UTF8Strings.CmpEqual) THEN Release; RETURN; END;
ResetSelection;
length := UTF8Strings.Length(x) + 1;
CheckSize(length);
i := 0; UTF8Strings.UTF8toUnicode(x, string^, i);
IF (length <= LEN(string)) THEN
curLen := TextUtilities.UCS32StrLength(string^);
ELSE
curLen := LEN(string) - 1;
string[curLen] := 0;
END;
cursorPosition := curLen;
Release;
ASSERT(string[curLen] = 0);
Invalidate;
END;
END SetAsString;
PROCEDURE LinkChanged(sender, object : ANY);
VAR string : Types.String256; res : LONGINT; real: Types.Longreal; m: Models.Model;
BEGIN
IF (sender = model) & WMProperties.GetModel(model,m) THEN
m.GetGeneric(string, res);
IF (res = Models.Ok) THEN
SetAsString(string.value);
SetAttributeValue("Value",string.value);
END;
END;
END LinkChanged;
PROCEDURE UpdateModel;
VAR string : Types.String256; res : LONGINT; str: Strings.String; m: Models.Model;
BEGIN
GetAsString(string.value);
IF WMProperties.GetModel(model,m) THEN
m.SetGeneric(string, res);
END;
SetAttributeValue("Value",string.value);
END UpdateModel;
PROCEDURE Changed;
BEGIN
changeDue := FALSE;
UpdateModel;
IF (onChanged # NIL) THEN onChanged.Call(NIL); END;
END Changed;
PROCEDURE GetCursorPosition(x: LONGINT) : LONGINT;
VAR
cursorPosition, code, i : LONGINT;
found : BOOLEAN; lastDistance : REAL;
font : WMGraphics.Font;
g : WMGraphics.GlyphSpacings; gx : REAL;
BEGIN
font := GetFont();
cursorPosition := 0;
x := x + ABS(renderOffsetX);
lastDistance := x;
i := 0; found := FALSE; gx := textBorder.Get();
WHILE ~found & (i < curLen) DO
code := string[i];
IF font.HasChar(code) THEN
font.GetGlyphSpacings(code, g);
ELSE
WMGraphics.FBGetGlyphSpacings(code, g);
END;
gx := gx + g.bearing.l + g.width + g.bearing.r;
IF (ABS(gx - x) < lastDistance) THEN
lastDistance := ABS(gx - x);
cursorPosition := i + 1;
ELSE
found := TRUE;
END;
INC(i);
END;
RETURN cursorPosition;
END GetCursorPosition;
PROCEDURE GetCursorCoordinateX() : LONGINT;
VAR font : WMGraphics.Font; g : WMGraphics.GlyphSpacings; x : REAL; i, code : LONGINT;
BEGIN
font := GetFont();
x := 0.0;
i := 0;
WHILE (i < cursorPosition) DO
code := string[i];
IF font.HasChar(code) THEN
font.GetGlyphSpacings(code, g);
ELSE
WMGraphics.FBGetGlyphSpacings(code, g);
END;
x := x + g.bearing.l + g.width + g.bearing.r;
INC(i);
END;
RETURN ENTIER(x + 0.5);
END GetCursorCoordinateX;
PROCEDURE Draw(canvas : WMGraphics.Canvas);
CONST CursorWidth = 2; BorderWidth = 1;
VAR
font : WMGraphics.Font; g : WMGraphics.GlyphSpacings;
x, y : LONGINT; cursorX, selectionX0, selectionX1, tw, th: LONGINT; code, i, width, height : LONGINT;
selectionIsValid : BOOLEAN;
halign: LONGINT;
TextBorder: LONGINT;
PROCEDURE StringSize(VAR w,h: LONGINT);
VAR code,i: LONGINT;
BEGIN
w := 0; h := 0;
FOR i := 0 TO curLen-1 DO
code := string[i];
IF font.HasChar(code) THEN
font.GetGlyphSpacings(code, g);
ELSE
WMGraphics.FBGetGlyphSpacings(code, g);
END;
w := w + g.bearing.l + g.width + g.bearing.r;
h := MAX(h, g.height);
END;
END StringSize;
BEGIN
Draw^(canvas);
IF ~visible.Get() OR ~show THEN RETURN END;
IF ~readOnlyI THEN
WMGraphicUtilities.DrawBevel(canvas, GetClientRect(), BorderWidth, TRUE, LONGINT(0808080FFH), WMGraphics.ModeCopy)
END;
font := GetFont();
canvas.SetColor(textColorI);
selectionIsValid := SelectionIsValid();
cursorX := GetCursorCoordinateX();
width := bounds.GetWidth();
height := bounds.GetHeight();
TextBorder := textBorder.Get();
StringSize(tw,th);
CASE alignH.Get() OF
WMGraphics.AlignRight:
renderOffsetX := width-TextBorder-tw;
| WMGraphics.AlignCenter:
renderOffsetX := width DIV 2 - tw DIV 2;
ELSE
renderOffsetX := TextBorder;
END;
CASE alignV.Get() OF
WMGraphics.AlignBottom:
y := height -1 - font.GetDescent() - TextBorder;
| WMGraphics.AlignCenter:
y := (height - (font.GetAscent() + font.GetDescent()) ) DIV 2 + font.GetAscent();
ELSE
y := TextBorder + font.GetAscent();
END;
IF hasFocus & ~readOnlyI THEN
IF (renderOffsetX + cursorX < TextBorder) THEN
renderOffsetX := -cursorX + TextBorder;
ELSIF (cursorX + CursorWidth + renderOffsetX > width - TextBorder) THEN
renderOffsetX := width - cursorX - CursorWidth - TextBorder;
END;
cursorX := TextBorder;
END;
IF (renderOffsetX < TextBorder) THEN selectionX0 := 0; ELSE selectionX0 := TextBorder; END;
x := renderOffsetX;
FOR i := 0 TO curLen - 1 DO
code := string[i];
IF font.HasChar(code) THEN
font.GetGlyphSpacings(code, g);
font.RenderChar(canvas, x, y, code)
ELSE
WMGraphics.FBGetGlyphSpacings(code, g);
WMGraphics.FBRenderChar(canvas, x, y, code)
END;
x := x + g.bearing.l + g.width + g.bearing.r;
IF (cursorPosition = i + 1) THEN cursorX := ENTIER(x*1.0); END;
IF (selectionIsValid) THEN
IF (selection.start = i + 1) THEN selectionX0 := ENTIER(x*1.0); END;
IF (selection.end = i) THEN selectionX1 := ENTIER(x*1.0 + 0.5); END;
END;
END;
IF selectionIsValid THEN
canvas.Fill(WMRectangles.MakeRect(selectionX0, y - font.GetAscent() , selectionX1, y + font.GetDescent() ), 0FF66H, WMGraphics.ModeSrcOverDst);
END;
IF cursorIsVisible & ~readOnlyI THEN
canvas.Fill(WMRectangles.MakeRect(cursorX, y - th , cursorX + CursorWidth, y + font.GetDescent() ), LONGINT(0FF0000CCH), WMGraphics.ModeSrcOverDst);
END;
IF changeDue THEN
canvas.Fill(WMRectangles.MakeRect(renderOffsetX, y - font.GetAscent() , x, y + font.GetDescent() ), LONGINT(0FF000066H), WMGraphics.ModeSrcOverDst);
END;
END Draw;
PROCEDURE SelectionIsValid() : BOOLEAN;
BEGIN
RETURN (selection.start # -1) & (selection.end # -1) & (selection.end >= selection.start);
END SelectionIsValid;
PROCEDURE IsAllowedCharacter(ch : Texts.Char32) : BOOLEAN;
BEGIN
IF (typeI = None) THEN
RETURN TRUE;
ELSIF (typeI = Decimal) THEN
RETURN (ORD("0") <= ch) & (ch <= ORD("9")) OR (ch = ORD("-"));
ELSIF (typeI = Hex) THEN
RETURN ((ORD("0") <= ch) & (ch <= ORD("9"))) OR ((ORD("a") <= ch) & (ch <=ORD( "f"))) OR ((ORD("A") <= ch) & (ch <= ORD("F")));
ELSIF (typeI = Ascii) THEN
RETURN (32 <= ch) & (ch < 128)
ELSIF (typeI = Unicode) THEN
RETURN (32 <= ch);
ELSE
RETURN FALSE;
END;
END IsAllowedCharacter;
PROCEDURE IsAllowedString(CONST string : ARRAY OF CHAR) : BOOLEAN;
VAR i : LONGINT; valid : BOOLEAN; ucs : Texts.Char32;
BEGIN
i := 0; valid := TRUE;
REPEAT
valid := UTF8Strings.DecodeChar(string, i, ucs);
valid := valid & ((ucs = 0) OR IsAllowedCharacter(ucs));
UNTIL ~valid OR (ucs = 0);
RETURN valid;
END IsAllowedString;
PROCEDURE IsValidUCS32String(CONST string : Texts.UCS32String) : BOOLEAN;
VAR i : LONGINT;
BEGIN
i := 0;
WHILE (i < LEN(string)) & (string[i] # 0) & (IsAllowedCharacter(string[i])) DO INC(i); END;
RETURN (i < LEN(string)) & (string[i] = 0);
END IsValidUCS32String;
PROCEDURE InsertChar(ch : LONGINT);
VAR i : LONGINT;
BEGIN
IF IsAllowedCharacter(ch) THEN
DeleteSelection;
CheckSize(curLen + 1 + 1);
IF (curLen + 1 < LEN(string)) THEN
FOR i := curLen TO cursorPosition BY -1 DO
string[i + 1] := string[i];
END;
string[cursorPosition] := ch;
INC(curLen);
INC(cursorPosition);
WMTextView.cursorBlinker.Show(SELF);
Invalidate;
END;
ASSERT(string[curLen] = 0);
changeDue := TRUE;
END;
END InsertChar;
PROCEDURE InsertStringAt(CONST x : ARRAY OF CHAR; position : LONGINT; VAR res : LONGINT);
VAR length, i, j, idx : LONGINT; changed, valid : BOOLEAN;
BEGIN
ASSERT(UTF8Strings.Valid(x));
ASSERT(position >= 0);
res := WMDropTarget.Failed;
IF IsAllowedString(x) THEN
length := UTF8Strings.Length(x);
IF (length > 0) THEN
Acquire;
CheckSize(curLen + length + 1);
IF (curLen + length + 1 <= LEN(string)) THEN
i := curLen - 1;
WHILE (i >= position) DO
string[i + length] := string[i];
DEC(i);
END;
j := 0; idx := 0;
FOR i := position TO position + length - 1 DO
valid := UTF8Strings.DecodeChar(x, idx, string[i]);
INC(j);
END;
curLen := curLen + length;
string[curLen] := 0;
cursorPosition := position + length;
changed := TRUE;
ELSE
changed := FALSE;
END;
Release;
IF changed THEN
Invalidate;
changeDue := TRUE;
END;
END;
END;
END InsertStringAt;
PROCEDURE InsertUCS32(atPosition : LONGINT; CONST string : Texts.UCS32String);
VAR length, i, j : LONGINT; changed : BOOLEAN;
BEGIN
ASSERT(atPosition >= 0);
IF IsValidUCS32String(string) THEN
length := TextUtilities.UCS32StrLength(string);
IF (length > 0) THEN
DeleteSelection;
Acquire;
CheckSize(curLen + length + 1);
IF (curLen + length < LEN(SELF.string)) THEN
i := curLen - 1;
WHILE (i >= atPosition) DO
SELF.string[i + length] := SELF.string[i];
DEC(i);
END;
j := 0;
FOR i := atPosition TO atPosition + length - 1 DO
SELF.string[i] := string[j];
INC(j);
END;
curLen := curLen + length;
SELF.string[curLen] := 0;
cursorPosition := atPosition + length;
changed := TRUE;
ELSE
changed := FALSE;
END;
Release;
IF changed THEN
Invalidate;
changeDue := TRUE;
END;
END;
END;
END InsertUCS32;
PROCEDURE Backspace;
VAR i : LONGINT;
BEGIN
IF SelectionIsValid() THEN
DeleteSelection; changeDue := TRUE;
ELSIF (curLen > 0) & (cursorPosition > 0) THEN
FOR i := cursorPosition-1 TO curLen-1 DO string[i] := string[i+1]; END;
DEC(curLen);
DEC(cursorPosition);
string[curLen] := 0;
Invalidate;
changeDue := TRUE;
END;
END Backspace;
PROCEDURE Delete;
VAR i : LONGINT;
BEGIN
IF SelectionIsValid() THEN
DeleteSelection; changeDue := TRUE;
ELSIF (curLen > 0) & (cursorPosition < curLen) THEN
FOR i := cursorPosition TO curLen-1 DO string[i] := string[i+1]; END;
DEC(curLen);
string[curLen] := 0;
Invalidate;
changeDue := TRUE;
END;
END Delete;
PROCEDURE Home;
BEGIN
IF (Inputs.Shift * currentFlags # {}) THEN
IF (cursorPosition > 0) THEN
selection.start := 0;
IF (selection.end = -1) THEN selection.end := cursorPosition - 1; END;
END;
ELSE
ResetSelection;
END;
cursorPosition := 0;
Invalidate;
END Home;
PROCEDURE End;
BEGIN
IF (Inputs.Shift * currentFlags # {}) THEN
IF (cursorPosition < curLen) THEN
selection.end := curLen - 1;
IF (selection.start = -1) THEN selection.start := cursorPosition; END;
END;
ELSE
ResetSelection;
END;
cursorPosition := curLen;
Invalidate;
END End;
PROCEDURE CursorLeft;
BEGIN
IF (cursorPosition > 0) THEN
IF (Inputs.Shift * currentFlags # {}) THEN
IF SelectionIsValid() THEN
IF (cursorPosition = selection.start + 1) THEN
selection.start := -1; selection.end := -1;
ELSIF (cursorPosition <= selection.start) THEN
selection.start := cursorPosition - 1;
ELSE
IF (cursorPosition > 1) THEN
selection.end := cursorPosition - 2;
ELSE
selection.start := -1; selection.end := -1;
END;
END;
ELSE
selection.start := cursorPosition - 1;
selection.end := cursorPosition - 1;
END;
ELSE
selection.start := -1; selection.end := -1;
END;
DEC(cursorPosition);
Invalidate;
ELSIF (Inputs.Shift * currentFlags = {}) THEN
ResetSelection;
END;
END CursorLeft;
PROCEDURE CursorRight;
BEGIN
IF (cursorPosition < curLen ) THEN
IF (Inputs.Shift * currentFlags # {}) THEN
IF SelectionIsValid() THEN
IF (cursorPosition = selection.end) THEN
selection.start := -1; selection.end := -1;
ELSIF (cursorPosition > selection.end) THEN
selection.end := cursorPosition;
ELSE
IF (cursorPosition < curLen - 1) THEN
selection.start := cursorPosition + 1;
ELSE
selection.start := -1; selection.end := -1;
END;
END;
ELSE
selection.start := cursorPosition;
selection.end := cursorPosition;
END;
ELSE
selection.start := -1; selection.end := -1;
END;
INC(cursorPosition);
Invalidate;
ELSIF (Inputs.Shift * currentFlags = {}) THEN
ResetSelection;
END;
END CursorRight;
PROCEDURE DeleteSelection;
VAR nofSelectedCharacters, i : LONGINT;
BEGIN
IF SelectionIsValid() THEN
nofSelectedCharacters := selection.end - selection.start + 1;
i := selection.start;
WHILE (i + nofSelectedCharacters <= curLen) DO
string[i] := string[i + nofSelectedCharacters];
INC(i);
END;
cursorPosition := selection.start;
curLen := curLen - nofSelectedCharacters;
selection.start := -1; selection.end := -1;
ASSERT(string[curLen] = 0);
Invalidate;
END;
END DeleteSelection;
PROCEDURE CopySelection;
VAR i : LONGINT; ucs32 : Texts.PUCS32String;
BEGIN
IF SelectionIsValid() THEN
Texts.clipboard.AcquireWrite;
IF (Texts.clipboard.GetLength() > 0) THEN Texts.clipboard.Delete(0, Texts.clipboard.GetLength()) END;
NEW(ucs32, selection.end - selection.start + 1 + 1);
FOR i := selection.start TO selection.end DO
ucs32[i - selection.start] := string[i];
END;
ucs32[LEN(ucs32)-1] := 0;
Texts.clipboard.InsertUCS32(0, ucs32^);
Texts.clipboard.ReleaseWrite;
END;
END CopySelection;
PROCEDURE GetSelectionAsString(VAR string : Strings.String) : BOOLEAN;
VAR i, idx : LONGINT; valid : BOOLEAN;
BEGIN
IF SelectionIsValid() THEN
Acquire;
NEW(string, 6*LEN(SELF.string));
i := selection.start; idx := 0; valid := TRUE;
WHILE valid & (i < LEN(string)-1) & (i <= selection.end) DO
valid := UTF8Strings.EncodeChar(SELF.string[i], string^, idx);
INC(i);
END;
string[i] := 0X;
Release;
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END GetSelectionAsString;
PROCEDURE ResetSelection;
BEGIN
IF SelectionIsValid() THEN
selection.start := -1; selection.end := -1;
Invalidate;
END;
END ResetSelection;
PROCEDURE SelectAll;
BEGIN
IF (curLen > 0) THEN
selection.start := 0;
selection.end := curLen - 1;
END;
Invalidate;
END SelectAll;
PROCEDURE Paste;
VAR text : ARRAY MaxStringSize OF CHAR; valid : BOOLEAN; ignore : LONGINT;
BEGIN
Texts.clipboard.AcquireRead;
IF (0 < Texts.clipboard.GetLength()) & (Texts.clipboard.GetLength() < MaxStringSize) THEN
TextUtilities.TextToStr(Texts.clipboard, text);
valid := IsAllowedString(text);
ELSE
valid := FALSE;
END;
Texts.clipboard.ReleaseRead;
IF valid THEN
DeleteSelection;
InsertStringAt(text, cursorPosition, ignore);
END;
END Paste;
PROCEDURE KeyEvent(ucs : LONGINT; flags : SET; VAR keySym : LONGINT);
BEGIN
currentFlags := flags;
IF readOnlyI THEN RETURN END;
IF Inputs.Release IN flags THEN RETURN END;
IF ~enabled.Get() THEN RETURN; END;
IF keySym = 01H THEN
SelectAll;
ELSIF keySym = 03H THEN
CopySelection;
ELSIF (keySym = 0FF63H) & (flags * Inputs.Ctrl # {}) THEN
ELSIF keySym = 0FF51H THEN
CursorLeft;
ELSIF keySym = 0FF53H THEN
CursorRight;
ELSIF keySym = 0FF54H THEN
ELSIF keySym = 0FF52H THEN
ELSIF keySym = 0FF56H THEN
ELSIF keySym = 0FF55H THEN
ELSIF keySym = 0FF50H THEN
Home;
ELSIF keySym = 0FF57H THEN
End;
ELSIF keySym = 016H THEN
Paste;
ELSIF keySym = 17H THEN
ELSIF keySym = 018H THEN
CopySelection;
DeleteSelection;
ELSIF keySym = 0FFFFH THEN
Delete;
ELSIF keySym = 0FF08H THEN
Backspace;
ELSIF (keySym = 0FF63H) & (flags * Inputs.Shift # {}) THEN
Paste;
ELSIF (keySym = 0FF0DH) THEN
onEnter.Call(NIL); Changed;
ELSIF (keySym = 0FF1BH) THEN
onEscape.Call(NIL);
ELSIF (keySym = 00020H) & (flags * Inputs.Ctrl # {}) THEN
ime := WMInputMethods.SwitchIME();
IF ime # NIL THEN
SetIMEInterface(ime);
END
ELSE
ime := WMInputMethods.activeIME;
IF (ime # NIL) & (ucs > 26) THEN
DeleteSelection;
SetIMEInterface(ime);
ime.KeyEvent(ucs, flags, keySym)
ELSIF (0 <= ucs) & (ucs <= 255) THEN
InsertChar(ucs);
END;
END;
WMTextView.cursorBlinker.Show(SELF);
END KeyEvent;
PROCEDURE PointerDown(x, y : LONGINT; keys : SET);
VAR newCursorPosition, oldInterclick : LONGINT;
BEGIN
PointerDown^(x, y, keys);
oldInterclick := interclick;
IF (keys * {0, 1} = {0,1}) THEN interclick := Interclick01;
ELSIF (keys * {0,2} = {0,2}) THEN interclick := Interclick02;
ELSE interclick := InterclickNone;
END;
IF (oldInterclick = InterclickCancelled) OR
((oldInterclick # InterclickNone) & (interclick # InterclickNone)) THEN
interclick := InterclickCancelled;
END;
newCursorPosition := cursorPosition;
IF (keys * {0, 1, 2} = {0}) & (interclick = InterclickNone) THEN
dragPossible := FALSE;
newCursorPosition := GetCursorPosition(x);
IF SelectionIsValid() & (selection.start <= newCursorPosition) & (newCursorPosition <= selection.end + 1) THEN
dragPossible := TRUE;
lastX := x; lastY := y;
ELSE
IF (lastX # -1) & (lastY # -1) & (ABS(lastX - x) < 2) & (ABS(lastY - y) < 2) THEN
SelectAll;
lastX := -1; lastY := -1;
ELSE
selecting := TRUE;
selectingCursorPos := newCursorPosition;
END;
lastX := x; lastY := y;
END;
ELSE
ResetSelection;
selecting := FALSE;
lastX := -1; lastY := -1;
END;
IF (cursorPosition # newCursorPosition) THEN
cursorPosition := newCursorPosition;
Invalidate;
END;
END PointerDown;
PROCEDURE PointerUp(x, y : LONGINT; keys : SET);
BEGIN
PointerUp^(x, y, keys);
selecting := FALSE;
selectingCursorPos := -1;
doubleClicking := FALSE;
IF (keys * {0,1,2} = {}) THEN
IF (interclick = Interclick02) THEN
DeleteSelection;
END;
interclick := InterclickNone;
END;
IF dragPossible THEN ResetSelection; dragPossible := FALSE; END;
END PointerUp;
PROCEDURE PointerMove(x, y : LONGINT; keys : SET);
VAR posX : LONGINT; valid : BOOLEAN; start, end : LONGINT;
BEGIN
IF dragPossible THEN
IF (ABS(x - lastX) > DragMinDistance) OR (ABS(y - lastY) > DragMinDistance) THEN dragPossible := FALSE; AutoStartDrag; END;
ELSIF selecting THEN
posX := GetCursorPosition(x);
IF (0 <= posX) & (posX <= curLen) & (selectingCursorPos # -1) THEN
valid := TRUE;
IF (posX > selectingCursorPos) & (posX > 1)THEN
start := selectingCursorPos; end := posX - 1;
ELSIF (posX < selectingCursorPos) & (selectingCursorPos > 0) THEN
start := posX; end := selectingCursorPos - 1;
ELSE
valid := FALSE;
END;
IF valid & ((start # selection.start) OR (end # selection.end)) THEN
selection.start := start; selection.end := end;
END;
Invalidate;
END;
END;
END PointerMove;
PROCEDURE PointerLeave;
BEGIN
PointerLeave^;
IF changeDue THEN Changed END;
lastX := -1; lastY := -1;
END PointerLeave;
PROCEDURE AutoStartDrag;
VAR
image : WMGraphics.Image; canvas : WMGraphics.BufferCanvas;
font : WMGraphics.Font;
w, h : LONGINT;
BEGIN
IF GetSelectionAsString(dragString) THEN
w := 0; h := 0;
font := GetFont();
font.GetStringSize(dragString^, w, h);
IF (w > 0) & (h > 0) THEN
NEW(image); Raster.Create(image, w, h, Raster.BGRA8888);
NEW(canvas, image);
canvas.Fill(WMRectangles.MakeRect(0, 0, w, h), 0FF60H, WMGraphics.ModeSrcOverDst);
canvas.SetColor(textColorI);
WMGraphics.DrawStringInRect(canvas, WMRectangles.MakeRect(0, 0, w, h), FALSE, WMGraphics.AlignCenter, WMGraphics.AlignCenter, dragString^)
ELSE
image := NIL;
END;
IF ~StartDrag(NIL, image, 0,0,DragWasAccepted, NIL) THEN KernelLog.String("ERROR"); END;
END;
END AutoStartDrag;
PROCEDURE DragWasAccepted(sender, data : ANY);
VAR di : WMWindowManager.DragInfo;
dt : WMDropTarget.DropTarget;
ds : DropString;
itf : WMDropTarget.DropInterface;
targetText, temp : Texts.Text;
pos, value, res : LONGINT;
BEGIN
IF (dragString = NIL) THEN RETURN; END;
IF (data # NIL) & (data IS WMWindowManager.DragInfo) THEN
di := data(WMWindowManager.DragInfo);
IF (di.data # NIL) & (di.data IS WMDropTarget.DropTarget) THEN
dt := di.data(WMDropTarget.DropTarget);
ELSE RETURN;
END;
ELSE RETURN;
END;
IF (typeI = Decimal) OR (typeI = Hex) THEN
itf := dt.GetInterface(WMDropTarget.TypeInt32);
IF (itf # NIL) THEN
Strings.StrToInt(dragString^, value);
itf(WMDropTarget.DropInt32).Set(value);
dragString := NIL;
RETURN;
END;
END;
itf := dt.GetInterface(WMDropTarget.TypeText);
IF itf # NIL THEN
targetText := itf(WMDropTarget.DropText).text;
IF (targetText # NIL) THEN
targetText.AcquireWrite;
pos := itf(WMDropTarget.DropText).pos.GetPosition();
NEW(temp);
temp.AcquireWrite;
TextUtilities.StrToText(temp, 0, dragString^);
targetText.CopyFromText(temp, 0, temp.GetLength(), pos);
temp.ReleaseWrite;
targetText.ReleaseWrite;
END;
dragString := NIL;
RETURN;
END;
itf := dt.GetInterface(WMDropTarget.TypeString);
IF (itf # NIL) THEN
ds := itf(DropString);
IF (ds.originator # SELF) OR ~SelectionIsValid() OR ((ds.position < selection.start) & (selection.end < ds.position)) THEN
itf(WMDropTarget.DropString).Set(dragString^, res);
dragString := NIL;
END;
END;
END DragWasAccepted;
PROCEDURE DragOver(x, y : LONGINT; dragInfo : WMWindowManager.DragInfo);
VAR pos : LONGINT; doInvalidate : BOOLEAN;
BEGIN
IF takesFocus.Get() THEN
doInvalidate := FALSE;
Acquire;
pos := GetCursorPosition(x);
IF (0 <= pos) & (pos <= curLen) THEN
IF (cursorIsVisible = FALSE) THEN cursorIsVisible := TRUE; doInvalidate := TRUE; END;
IF (cursorPosition # pos) THEN
cursorPosition := pos;
doInvalidate := TRUE;
END;
END;
Release;
IF doInvalidate THEN Invalidate; END;
END;
END DragOver;
PROCEDURE DragDropped(x, y : LONGINT; dragInfo : WMWindowManager.DragInfo);
VAR dt : DropTarget; pos : LONGINT;
BEGIN
IF takesFocus.Get() THEN
pos := GetCursorPosition(x);
NEW(dt, SELF, SetDroppedString, pos);
dragInfo.data := dt;
IF ~hasFocus & cursorIsVisible THEN cursorIsVisible := FALSE; Invalidate; END;
ConfirmDrag(TRUE, dragInfo);
ELSE
ConfirmDrag(FALSE, dragInfo);
END;
END DragDropped;
PROCEDURE SetDroppedString( CONST string : ARRAY OF CHAR; position : LONGINT; VAR res : LONGINT);
BEGIN
Acquire;
ResetSelection;
InsertStringAt(string, position, res);
Release;
END SetDroppedString;
PROCEDURE SetCursorInfo(charsAdded : LONGINT);
BEGIN
END SetCursorInfo;
PROCEDURE GetCurrentCursorPosition() : LONGINT;
BEGIN
RETURN cursorPosition;
END GetCurrentCursorPosition;
PROCEDURE GetCursorScreenPosition(VAR x, y : LONGINT);
BEGIN
Acquire;
x := GetCursorCoordinateX() + renderOffsetX;
y := 10;
ToWMCoordinates(x, y, x, y);
Release;
END GetCursorScreenPosition;
PROCEDURE SetIMEInterface(ime : WMInputMethods.IME);
VAR i : WMInputMethods.IMEInterface;
BEGIN
ASSERT((ime # NIL));
i.AcquireText := Acquire;
i.ReleaseText := Release;
i.InsertUCS32 := InsertUCS32;
i.GetCursorPosition := GetCurrentCursorPosition;
i.GetCursorScreenPosition := GetCursorScreenPosition;
i.SetCursorInfo := SetCursorInfo;
ime.SetInterface(i);
END SetIMEInterface;
PROCEDURE FocusReceived;
BEGIN
FocusReceived^;
cursorIsVisible := TRUE;
WMTextView.cursorBlinker.Set(SELF, SetCurrentCursorVisibility);
Invalidate;
END FocusReceived;
PROCEDURE FocusLost;
BEGIN
FocusLost^;
currentFlags := {};
WMTextView.cursorBlinker.Remove(SELF);
cursorIsVisible := FALSE;
selectingCursorPos := -1;
Invalidate;
IF changeDue THEN Changed END;
IF ime # NIL THEN ime.Hide END;
END FocusLost;
END TextField;
NumberField= OBJECT (TextField)
VAR
fractionalDigits-: WMProperties.Int32Property;
unitModel-: WMProperties.ReferenceProperty;
PROCEDURE & Init*;
BEGIN
Init^;
SetNameAsString(GSNumberField);
SetGenerator("WMEditors.GenNumberField");
NEW(fractionalDigits, PrototypeFractionalDigits, NIL, NIL); properties.Add(fractionalDigits);
NEW(unitModel, PrototypeUnit, NIL, NIL); properties.Add(unitModel)
END Init;
PROCEDURE GetUnit(VAR u: LONGREAL): BOOLEAN;
VAR res: LONGINT; real: Types.Longreal; m: Models.Model;
BEGIN
IF WMProperties.GetModel(unitModel, m) THEN
m.GetGeneric(real, res);
IF (res = Models.Ok) & (real.value # 0) THEN
u := real.value; RETURN TRUE
END;
END;
RETURN FALSE
END GetUnit;
PROCEDURE RecacheProperties;
BEGIN
RecacheProperties^;
IF unitModel.Get()# NIL THEN LinkChanged(unitModel, unitModel.Get()) END;
IF model.Get()# NIL THEN LinkChanged(model, model.Get()) END;
END RecacheProperties;
PROCEDURE LinkChanged(sender, data : ANY);
VAR string : Types.String256; res : LONGINT; real: Types.Longreal; unit: LONGREAL; m: Models.Model;
BEGIN
IF (sender = model) OR (sender = unitModel) THEN
res := -1;
IF WMProperties.GetModel(model,m) THEN
m.GetGeneric(real, res);
IF res = Models.Ok THEN
IF Reals.IsNaNL(real.value) THEN
SetAsString("--")
ELSE
IF GetUnit(unit) THEN real.value := unit * real.value END;
Types.FloatToString(real.value, fractionalDigits.Get(), string.value);
SetAsString(string.value);
END
ELSE
m.GetGeneric(string,res);
IF res = Models.Ok THEN
SetAsString(string.value);
END;
END;
END;
END;
END LinkChanged;
PROCEDURE UpdateModel;
VAR string : Types.String256; res : LONGINT; str: Strings.String; real: Types.Longreal; u: LONGREAL; m: Models.Model;
BEGIN
GetAsString(string.value);
IF WMProperties.GetModel(model, m) & ~(m IS Models.String) THEN
Strings.StrToFloat(string.value, real.value);
IF GetUnit(u) THEN real.value := real.value / u END;
m.SetGeneric(real, res);
END;
END UpdateModel;
PROCEDURE PropertyChanged*(sender, property : ANY);
VAR reference: XML.Element; unitModel: XML.Element;
BEGIN
IF property = fractionalDigits THEN
LinkChanged(model, NIL); Invalidate;
ELSIF property = unitModel THEN
LinkChanged(model, NIL); Invalidate
ELSE PropertyChanged^(sender, property)
END;
END PropertyChanged;
END NumberField;
TYPE
MacroData* = OBJECT
VAR
text* : Texts.Text;
cursor* : WMTextView.PositionMarker;
keySym* : LONGINT;
flags* : SET;
handled* : BOOLEAN;
END MacroData;
Editor* = OBJECT(WMComponents.VisualComponent)
VAR
tv- : WMTextView.TextView;
vScrollbar- : WMStandardComponents.Scrollbar;
hScrollbar- : WMStandardComponents.Scrollbar;
ime : WMInputMethods.IME;
multiLine-, readOnly- : WMProperties.BooleanProperty;
highlighting- : WMProperties.StringProperty;
text- : Texts.Text;
utilreader : Texts.TextReader;
onEnter-, onEscape- : WMEvents.EventSource;
macros- : WMEvents.EventSource;
macroData : MacroData;
currentFlags : SET;
allowIME* : BOOLEAN;
allowScrollbars- : WMProperties.BooleanProperty;
undoMgr*: UndoManager.UndoManager;
PROCEDURE &Init*;
BEGIN
Init^;
SetNameAsString(GSEditor);
SetGenerator("WMEditors.GenEditor");
allowIME := TRUE;
ime := WMInputMethods.activeIME;
NEW(multiLine, multiLineProto, NIL, NIL); properties.Add(multiLine);
NEW(readOnly, readOnlyProto, NIL, NIL); properties.Add(readOnly);
NEW(highlighting, highlightingProto, NIL, NIL); properties.Add(highlighting);
NEW(allowScrollbars, allowScrollbarsProto, NIL, NIL); properties.Add(allowScrollbars);
NEW(onEnter, SELF, GSonEnter, GSonEnterInfo, SELF.StringToCompCommand); events.Add(onEnter);
NEW(onEscape, SELF, GSonEscape, GSonEscapeInfo, SELF.StringToCompCommand); events.Add(onEscape);
NEW(macros, SELF, GSmacros, GSmacrosInfo, SELF.StringToCompCommand); events.Add(macros);
NEW(vScrollbar);
vScrollbar.alignment.Set(WMComponents.AlignRight); AddInternalComponent(vScrollbar);
NEW(hScrollbar);
hScrollbar.alignment.Set(WMComponents.AlignBottom);
hScrollbar.vertical.Set(FALSE); AddInternalComponent(hScrollbar);
takesFocus.Set(TRUE);
needsTab.Set(TRUE);
NEW(text);
NEW(utilreader, text);
NEW(macroData);
NEW(tv);
tv.alignment.Set(WMComponents.AlignClient); AddInternalComponent(tv);
tv.SetScrollbars(hScrollbar, vScrollbar);
tv.SetText(text);
tv.SetExtKeyEventHandler(KeyPressed);
tv.SetExtPointerDownHandler(MousePressed);
END Init;
PROCEDURE Initialize*;
BEGIN
Initialize^;
tv.focusPrevious.Set(focusPrevious.Get());
tv.focusNext.Set(focusNext.Get());
tv.takesFocus.Set(takesFocus.Get());
tv.needsTab.Set(needsTab.Get());
tv.highlighting.Set(highlighting.Get());
Resized;
END Initialize;
PROCEDURE SetUndoManager*(uMgr: UndoManager.UndoManager);
BEGIN
undoMgr := uMgr;
END SetUndoManager;
PROCEDURE Undo*;
BEGIN
IF undoMgr # NIL THEN
undoMgr.Undo();
tv.cursor.SetPosition(undoMgr.actualPos)
END
END Undo;
PROCEDURE Redo*;
BEGIN
IF undoMgr # NIL THEN
undoMgr.Redo();
tv.cursor.SetPosition(undoMgr.actualPos)
END
END Redo;
PROCEDURE SetFocus*;
BEGIN
tv.SetFocus
END SetFocus;
PROCEDURE CheckScrollbars;
BEGIN
IF allowScrollbars.Get() & multiLine.Get() THEN
vScrollbar.visible.Set(TRUE); hScrollbar.visible.Set(TRUE)
ELSE
vScrollbar.visible.Set(FALSE); hScrollbar.visible.Set(FALSE)
END
END CheckScrollbars;
PROCEDURE RecacheProperties;
BEGIN
RecacheProperties^;
tv.isMultiLine.Set(multiLine.Get());
tv.focusPrevious.Set(focusPrevious.Get());
tv.focusNext.Set(focusNext.Get());
tv.takesFocus.Set(takesFocus.Get());
tv.needsTab.Set(needsTab.Get());
tv.highlighting.Set(highlighting.Get());
CheckScrollbars
END RecacheProperties;
PROCEDURE PropertyChanged*(sender, property : ANY);
VAR reference: XML.Element;
BEGIN
IF (property = multiLine) THEN tv.isMultiLine.Set(multiLine.Get()); CheckScrollbars;
ELSIF (property = focusPrevious) THEN tv.focusPrevious.Set(focusPrevious.Get());
ELSIF (property = focusNext) THEN tv.focusNext.Set(focusNext.Get());
ELSIF (property = takesFocus) THEN tv.takesFocus.Set(takesFocus.Get());
ELSIF (property = needsTab) THEN tv.needsTab.Set(needsTab.Get());
ELSIF (property = highlighting) THEN tv.highlighting.Set(highlighting.Get());
ELSIF (property = model) THEN
reference := model.Get();
IF (reference # NIL) & (reference IS Models.Text) THEN
SetText(reference(Models.Text).Get());
END;
ELSE PropertyChanged^(sender, property)
END;
END PropertyChanged;
PROCEDURE LinkChanged(sender, data : ANY);
VAR string : Types.String256; res : LONGINT; real: Types.Longreal; unit: LONGREAL; m: Models.Model;
BEGIN
LinkChanged^(sender, data);
END LinkChanged;
PROCEDURE SetText*(t : Texts.Text);
VAR reference: XML.Element; textModel: Models.Text;
BEGIN
IF t = NIL THEN RETURN END;
Acquire;
text := t;
NEW(utilreader, text);
tv.SetText(t);
Release;
reference := model.Get();
IF reference # NIL THEN
IF reference(Models.Text).Get() # text THEN
reference(Models.Text).SetReference(text);
END;
ELSE
NEW(textModel); textModel.SetReference(text); model.Set(textModel);
END;
END SetText;
PROCEDURE SetCursorInfo(charsAdded : LONGINT);
BEGIN
IF text.isUTF THEN
tv.cursor.SetNextInternalPosition(tv.GetInternalPos(tv.cursor.GetPosition()) + charsAdded);
END;
END SetCursorInfo;
PROCEDURE GetCursorPosition() : LONGINT;
BEGIN
RETURN tv.GetInternalPos(tv.cursor.GetPosition());
END GetCursorPosition;
PROCEDURE GetCursorScreenPosition(VAR x, y : LONGINT);
BEGIN
tv.Acquire;
text.AcquireRead;
IF tv.FindScreenPos(tv.cursor.GetPosition(), x, y) THEN END;
tv.ToWMCoordinates(x, y, x, y);
text.ReleaseRead;
tv.Release;
END GetCursorScreenPosition;
PROCEDURE SetIMEInterface(ime : WMInputMethods.IME; text : Texts.Text);
VAR i : WMInputMethods.IMEInterface;
BEGIN
ASSERT((ime # NIL) & (text # NIL));
i.AcquireText := text.AcquireWrite;
i.ReleaseText := text.ReleaseWrite;
i.InsertUCS32 := text.InsertUCS32;
i.GetCursorPosition := GetCursorPosition;
i.GetCursorScreenPosition := GetCursorScreenPosition;
i.SetCursorInfo := SetCursorInfo;
ime.SetInterface(i);
END SetIMEInterface;
PROCEDURE FocusReceived;
BEGIN
FocusReceived^;
tv.SetFocus
END FocusReceived;
PROCEDURE FocusLost;
BEGIN
tv.FocusLost;
IF ime # NIL THEN ime.Hide END;
currentFlags := {};
END FocusLost;
PROCEDURE InsertChar*(ch : Texts.Char32);
VAR
buf : ARRAY 2 OF Texts.Char32;
internalPos : LONGINT;
BEGIN
IF tv.selection.b - tv.selection.a # 0 THEN DeleteSelection END;
buf[0] := ch; buf[1] := 0;
internalPos := tv.GetInternalPos(tv.cursor.GetPosition());
tv.cursor.SetNextInternalPosition(internalPos + 1);
text.InsertUCS32(internalPos, buf);
PositionDebugging.SetPos(tv.GetInternalPos(tv.cursor.GetPosition()),tv.cursor.GetPosition());
END InsertChar;
PROCEDURE InsertString*(CONST string : Texts.UCS32String);
VAR internalPos : LONGINT;
BEGIN
IF tv.selection.b - tv.selection.a # 0 THEN DeleteSelection END;
internalPos := tv.GetInternalPos(tv.cursor.GetPosition());
tv.cursor.SetNextInternalPosition(internalPos + 1);
text.InsertUCS32(internalPos, string);
PositionDebugging.SetPos(tv.GetInternalPos(tv.cursor.GetPosition()),tv.cursor.GetPosition());
END InsertString;
PROCEDURE CopySelection*;
BEGIN
tv.CopySelection
END CopySelection;
PROCEDURE DeleteSelection*;
BEGIN
text.AcquireWrite;
tv.selection.Sort;
tv.cursor.SetNextInternalPosition(tv.selection.a);
text.Delete(tv.selection.a, tv.selection.b - tv.selection.a);
text.ReleaseWrite
END DeleteSelection;
PROCEDURE PasteToHostClipboard*;
VAR clipboard: Texts.UnicodeText; res : LONGINT;
BEGIN
text.AcquireWrite;
NEW(clipboard);
clipboard.AcquireWrite;
HostClipboard.Get(clipboard, res);
IF (res = HostClipboard.Ok) & (clipboard.GetLength() > 0) THEN
IF tv.selection.b - tv.selection.a # 0 THEN DeleteSelection() END;
text.CopyFromText(clipboard, 0, clipboard.GetLength(), tv.cursor.GetPosition());
END;
clipboard.ReleaseWrite;
text.ReleaseWrite
END PasteToHostClipboard;
PROCEDURE Paste*;
VAR
pos : LONGINT;
BEGIN
text.AcquireWrite;
Texts.clipboard.AcquireRead;
IF Texts.clipboard.GetLength() > 0 THEN
IF tv.selection.b - tv.selection.a # 0 THEN DeleteSelection() END;
pos := tv.GetInternalPos(tv.cursor.GetPosition());
tv.cursor.SetNextInternalPosition(pos);
text.CopyFromText(Texts.clipboard, 0, Texts.clipboard.GetLength(),pos)
END;
Texts.clipboard.ReleaseRead;
text.ReleaseWrite
END Paste;
PROCEDURE Delete(flags : SET);
VAR pos : LONGINT;
BEGIN
pos := tv.GetInternalPos(tv.cursor.GetPosition());
IF flags * Inputs.Shift # {} THEN
tv.selection.Sort;
IF tv.selection.active & (pos >= tv.selection.a) & (pos <= tv.selection.b) THEN
CopySelection
END;
END;
IF flags * Inputs.Ctrl # {} THEN
text.Delete(pos, TextUtilities.FindPosWordRight(utilreader, pos) - pos)
ELSE
tv.selection.Sort;
IF tv.selection.active & (pos >= tv.selection.a) & (pos <= tv.selection.b) THEN
DeleteSelection
ELSE
tv.cursor.SetNextInternalPosition(pos);
text.Delete(pos, 1)
END
END
END Delete;
PROCEDURE Backspace(word : BOOLEAN);
VAR pos, np : LONGINT;
BEGIN
pos := tv.GetInternalPos(tv.cursor.GetPosition());
IF word THEN
np := TextUtilities.FindPosWordLeft(utilreader, pos - 1);
text.Delete(np, pos - np)
ELSE
tv.selection.Sort;
IF tv.selection.active & (pos >= tv.selection.a) & (pos <= tv.selection.b) THEN
DeleteSelection
ELSE
tv.cursor.SetNextInternalPosition(pos-1);
text.Delete(pos - 1, 1)
END
END
END Backspace;
PROCEDURE Enter(flags : SET);
VAR
ctrl : BOOLEAN;
pos, lineStart, nofWhitespace, i : LONGINT;
ch : Texts.Char32; buffer : ARRAY 32 OF Texts.Char32; idx : LONGINT;
BEGIN
ctrl := flags * Inputs.Ctrl # {};
IF ctrl THEN
pos := tv.cursor.GetPosition();
tv.StartCommand(pos, flags * Inputs.Shift # {});
ELSE
IF multiLine.Get() THEN
pos := tv.GetInternalPos(tv.cursor.GetPosition());
tv.cursor.SetNextInternalPosition(pos+1);
lineStart := TextUtilities.FindPosLineStart(utilreader, pos);
nofWhitespace := TextUtilities.CountWhitespace(utilreader, lineStart);
nofWhitespace := Strings.Min(nofWhitespace, pos - lineStart);
IF (nofWhitespace > 0) THEN
idx := 0;
buffer[idx] := Texts.NewLineChar;
INC(idx);
utilreader.SetPosition(lineStart);
utilreader.SetDirection(1);
utilreader.ReadCh(ch);
i := 0;
WHILE (i < nofWhitespace) & ~utilreader.eot & (idx < LEN(buffer) - 1) DO
buffer[idx] := ch;
INC(idx); INC(i);
utilreader.ReadCh(ch);
END;
buffer[idx] := 0;
InsertString(buffer);
WHILE (i < nofWhitespace) & ~utilreader.eot DO
InsertChar(ch);
INC(i); utilreader.ReadCh(ch);
END;
ELSE
InsertChar(Texts.NewLineChar);
END;
END
END;
onEnter.Call(NIL)
END Enter;
PROCEDURE IndentLeft;
BEGIN
text.AcquireWrite;
tv.selection.Sort;
TextUtilities.IndentText(text, tv.selection.a, tv.selection.b, TRUE);
text.ReleaseWrite
END IndentLeft;
PROCEDURE IndentRight;
BEGIN
text.AcquireWrite;
tv.selection.Sort;
TextUtilities.IndentText(text, tv.selection.a, tv.selection.b, FALSE);
text.ReleaseWrite
END IndentRight;
PROCEDURE MousePressed(x, y : LONGINT; keys : SET; VAR handled : BOOLEAN);
VAR
pos, internalPos, a, b : LONGINT; ch : Texts.Char32;
selectionText : Texts.Text;
from, to : Texts.TextPosition;
BEGIN
IF (Inputs.Alt * currentFlags # {}) & (0 IN keys) THEN
text.AcquireWrite;
IF Texts.GetLastSelection(selectionText, from, to) THEN
selectionText.AcquireWrite;
a := Strings.Min(from.GetPosition(), to.GetPosition());
b := Strings.Max(from.GetPosition(), to.GetPosition());
tv.ViewToTextPos(x, y, pos);
internalPos := tv.GetInternalPos(pos);
utilreader.SetPosition(internalPos);
utilreader.ReadCh(ch);
IF utilreader.cstyle # NIL THEN
handled := TRUE;
selectionText.SetCharacterStyle(a, b - a, utilreader.cstyle);
ELSIF utilreader.attributes # NIL THEN
handled := TRUE;
selectionText.SetAttributes(a, b - a, utilreader.attributes.Clone())
END;
IF utilreader.pstyle # NIL THEN
selectionText.SetParagraphStyle(a, b - a, utilreader.pstyle);
END;
selectionText.ReleaseWrite
END;
text.ReleaseWrite
END
END MousePressed;
PROCEDURE KeyPressed*(ucs : LONGINT; flags : SET; VAR keySym : LONGINT; VAR handled : BOOLEAN);
VAR
textChanged : BOOLEAN;
PROCEDURE HandleTab;
BEGIN
tv.selection.Sort;
IF tv.selection.active THEN
IF (flags = {}) THEN IndentRight; ELSE IndentLeft; END;
ELSE
InsertChar(Texts.TabChar);
END;
END HandleTab;
BEGIN
textChanged := FALSE;
handled := TRUE;
currentFlags := flags;
tv.SetFlags(flags);
IF readOnly.Get() THEN RETURN END;
IF Inputs.Release IN flags THEN RETURN END;
text.AcquireWrite;
IF keySym = 14H THEN
text.CheckHealth
ELSIF keySym = 01H THEN
tv.SelectAll
ELSIF keySym = 03H THEN
tv.CopySelection
ELSIF (keySym = Inputs.KsTab) & (flags - Inputs.Shift = {}) THEN
HandleTab;
ELSIF keySym = 19H THEN
IF undoMgr # NIL THEN
undoMgr.Redo
END
ELSIF keySym = 1AH THEN
IF undoMgr # NIL THEN
undoMgr.Undo
END
ELSIF (keySym = 0FF63H) & (flags * Inputs.Ctrl # {}) THEN
tv.CopySelection
ELSIF keySym = 0FF51H THEN
IF flags * Inputs.Alt # {} THEN IndentLeft
ELSE tv.CursorLeft(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
END
ELSIF keySym = 0FF53H THEN
IF flags * Inputs.Alt # {} THEN IndentRight
ELSE tv.CursorRight(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
END
ELSIF keySym = 0FF54H THEN
tv.CursorDown(flags * Inputs.Shift # {})
ELSIF keySym = 0FF52H THEN
tv.CursorUp(flags * Inputs.Shift # {})
ELSIF keySym = 0FF56H THEN
tv.PageDown(flags * Inputs.Shift # {})
ELSIF keySym = 0FF55H THEN
tv.PageUp(flags * Inputs.Shift # {})
ELSIF keySym = 0FF50H THEN
tv.Home(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
ELSIF keySym = 0FF57H THEN
tv.End(flags * Inputs.Ctrl # {}, flags * Inputs.Shift # {})
ELSIF keySym = 016H THEN
textChanged := TRUE;
Paste
ELSIF keySym = 17H THEN
PasteToHostClipboard;
ELSIF keySym = 018H THEN
textChanged := TRUE;
CopySelection;
DeleteSelection
ELSIF keySym = 0FFFFH THEN
textChanged := TRUE;
Delete(flags)
ELSIF keySym = 0FF08H THEN
textChanged := TRUE;
Backspace(flags * Inputs.Ctrl # {})
ELSIF keySym = 0FF0DH THEN
textChanged := TRUE;
Enter(flags);
ELSIF (keySym = 0FF63H) & (flags * Inputs.Shift # {}) THEN
textChanged := TRUE;
Paste
ELSIF (keySym = 0FF1BH) THEN onEscape.Call(NIL); FocusNext
ELSIF (keySym = 00020H) & (flags * Inputs.Ctrl # {}) & allowIME THEN
ime := WMInputMethods.SwitchIME();
IF ime # NIL THEN
text.ReleaseWrite;
SetIMEInterface(ime, text);
text.AcquireWrite
END
ELSE
ime := WMInputMethods.activeIME;
IF allowIME & (ime # NIL) & (ucs > 26) THEN
IF tv.selection.b - tv.selection.a # 0 THEN DeleteSelection END;
SetIMEInterface(ime, text);
ime.KeyEvent(ucs, flags, keySym);
textChanged := TRUE;
ELSE
macroData.handled := FALSE;
macroData.flags := flags;
macroData.keySym := keySym;
macroData.text := text;
macroData.cursor := tv.cursor;
macros.Call(macroData);
IF ~macroData.handled & (ucs > 26) THEN
textChanged := TRUE;
InsertChar(ucs);
END
END;
END;
text.ReleaseWrite;
IF text.isUTF & textChanged THEN
text.AcquireRead;
tv.cursor.SetPosition(tv.GetDisplayPos(tv.cursor.GetNextInternalPosition()));
text.ReleaseRead;
END;
END KeyPressed;
PROCEDURE GetAsString*(VAR x : ARRAY OF CHAR);
BEGIN
TextUtilities.TextToStr(text, x)
END GetAsString;
PROCEDURE SetAsString*(CONST x : ARRAY OF CHAR);
BEGIN
text.AcquireWrite;
text.Delete(0, text.GetLength());
TextUtilities.StrToText(text, 0, x);
text.ReleaseWrite
END SetAsString;
PROCEDURE Finalize;
BEGIN
Finalize^;
IF ime # NIL THEN ime.Hide END;
IF tv # NIL THEN tv.Finalize; END;
END Finalize;
END Editor;
VAR
manager : WMWindowManager.WindowManager;
typeProto : WMProperties.Int32Property;
textColorProto : WMProperties.ColorProperty;
multiLineProto, readOnlyProto, allowScrollbarsProto : WMProperties.BooleanProperty;
highlightingProto : WMProperties.StringProperty;
GSonEnter, GSonEnterInfo : Strings.String;
GSonEscape, GSonEscapeInfo : Strings.String;
GSmacros, GSmacrosInfo : Strings.String;
GSTextField, GSEditor, GSNumberField : Strings.String;
GSModel, GSModelInfo : Strings.String;
PrototypeAlignH, PrototypeAlignV, PrototypeTextBorder: WMProperties.Int32Property;
PrototypeFractionalDigits*: WMProperties.Int32Property;
PrototypeUnit*: WMProperties.ReferenceProperty;
PROCEDURE InitStrings;
BEGIN
GSonEnter := Strings.NewString("onEnter");
GSonEnterInfo := Strings.NewString("called after the Enter key is pressed in the editor");
GSonEscape := Strings.NewString("onEscape");
GSonEscapeInfo := Strings.NewString("called when the Escape key is pressed in the editor");
GSmacros := Strings.NewString("macros");
GSmacrosInfo := Strings.NewString("must be handled directly without queuing, text has writelock, may not trap");
GSTextField := Strings.NewString("TextField");
GSNumberField := Strings.NewString("NumberField");
GSEditor := Strings.NewString("Editor");
GSModel := Strings.NewString("Model");
GSModelInfo := Strings.NewString("Model used by component");
END InitStrings;
PROCEDURE InitPrototypes;
BEGIN
NEW(typeProto, NIL, Strings.NewString("type"), NIL); typeProto.Set(Unicode);
NEW(textColorProto, NIL, Strings.NewString("textColor"), Strings.NewString("Text color")); textColorProto.Set(WMGraphics.Black);
NEW(multiLineProto, NIL, Strings.NewString("multiLine"), NIL); multiLineProto.Set(TRUE);
NEW(readOnlyProto, NIL, Strings.NewString("readOnly"), NIL); readOnlyProto.Set(FALSE);
NEW(allowScrollbarsProto, NIL, Strings.NewString("allowScrollbars"), NIL); allowScrollbarsProto.Set(TRUE);
NEW(highlightingProto, NIL, Strings.NewString("Highlighting"), Strings.NewString("Name of highlighting to be applied"));
highlightingProto.Set(NIL);
NEW(PrototypeAlignH, NIL, Strings.NewString("AlignH"), Strings.NewString("horizontal alignment"));
PrototypeAlignH.Set(WMGraphics.AlignLeft);
NEW(PrototypeAlignV, NIL, Strings.NewString("AlignV"), Strings.NewString("vertical alignment"));
PrototypeAlignV.Set(WMGraphics.AlignCenter);
NEW(PrototypeTextBorder, NIL, Strings.NewString("TextBorder"), Strings.NewString("text boundary"));
PrototypeTextBorder.Set(4);
NEW(PrototypeFractionalDigits, NIL, Strings.NewString("FractionalDigits"), Strings.NewString("fractional digits"));
PrototypeFractionalDigits.Set(0);
NEW(PrototypeUnit, NIL, Strings.NewString("Unit"), Strings.NewString("unit conversion factor"));
END InitPrototypes;
PROCEDURE GenTextField*() : XML.Element;
VAR t : TextField;
BEGIN
NEW(t); RETURN t;
END GenTextField;
PROCEDURE GenNumberField*() : XML.Element;
VAR t : NumberField;
BEGIN
NEW(t); RETURN t;
END GenNumberField;
PROCEDURE GenEditor*() : XML.Element;
VAR e : Editor;
BEGIN
NEW(e); RETURN e
END GenEditor;
PROCEDURE FindTextField*(CONST uid : ARRAY OF CHAR; component : WMComponents.Component) : TextField;
VAR c : WMComponents.Component;
BEGIN
ASSERT(component # NIL);
c := component.FindByUID(uid);
IF (c # NIL) & (c IS TextField) THEN
RETURN c (TextField);
ELSE
RETURN NIL;
END;
END FindTextField;
PROCEDURE FindEditor*(CONST uid : ARRAY OF CHAR; component : WMComponents.Component) : Editor;
VAR c : WMComponents.Component;
BEGIN
ASSERT(component # NIL);
c := component.FindByUID(uid);
IF (c # NIL) & (c IS Editor) THEN
RETURN c (Editor);
ELSE
RETURN NIL;
END;
END FindEditor;
BEGIN
manager := WMWindowManager.GetDefaultManager();
InitStrings;
InitPrototypes;
END WMEditors.
ComponentViewer.Open WMStandardComponents.GenPanel ~
ComponentViewer.Open WMEditors.GenTextField ~