{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 2013 - 2014                                 }
{            Email : info@tmssoftware.com                            }
{            Web : http://www.tmssoftware.com                        }
{                                                                    }
{ The source code is given as is. The author is not responsible      }
{ for any possible damage done due to the use of this code.          }
{ The complete source code remains property of the author and may    }
{ not be distributed, published, given or sold in any form as such.  }
{ No parts of the source code can be included in any other component }
{ or application without written authorization of the author.        }
{********************************************************************}

unit FMX.TMSMemo;

interface

{$I TMSDEFS.INC}

{$IFDEF MACOS}
  {$DEFINE GENERALOSX}
{$ENDIF}
{$IFDEF IOS}
  {$DEFINE GENERALOSX}
{$ENDIF}

uses
  {$IFDEF WIN32}Windows, {$ENDIF} Classes, Types, UITypes, FMX.Forms, SysUtils, FMX.Platform, FMX.TMSBaseControl,
  FMX.Objects, FMX.Types, FMX.Controls, FMX.Dialogs, Math, FMX.ListBox, FMX.Layouts, FMX.Menus, FMX.TMSFindDialog
  , Generics.Collections, FMX.TMSRTFEngine, FMX.TMSXUtil
  , FMX.ActnList, TypInfo, FMX.TMSPopup
  , FMX.StdCtrls, FMX.Text
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;

const
  MAJ_VER = 1; // Major version nr.
  MIN_VER = 5; // Minor version nr.
  REL_VER = 0; // Release nr.
  BLD_VER = 5; // Build nr.

  // version history
  // v1.0.0.0 : First release
  // v1.0.0.1 : Fixed : incorrect Gutter behavior when gutter is visible false or width = 0
  // v1.0.0.2 : Improved : Published events for Find and Replaced dialog
  // v1.5.0.0 : New : support for XE4
  //          : Fixed : Issue with triggering OnCursorChange
  //          : Fixed : Issue with UpdateMemo call when client-aligning
  // v1.5.0.1 : Fixed : Issue with autocompletion popup
  //          : Fixed : Issue with font persistence
  // v1.5.0.2 : Fixed : Issue with use of memo on frames
  // v1.5.0.3 : Fixed : Issue with horizontal scrolling and text trimming
  // v1.5.0.4 : Fixed : Issue with memo find & replace dialog up and down search
  // v1.5.0.5 : Improved : Performance of working with large blocks of text

type
  {$IFDEF DELPHI_LLVM}
  ansichar = char;
  {$ENDIF}
  _AnsiChar = byte;

  TTMSFMXMemoCustomStyler = class;
  TElementStyles = class;
  TTMSFMXMemo = class;

  TRegionType = (rtOpen, rtClosed, rtFile, rtIgnore);
  TStyleType = (stKeyword, stBracket, stSymbol, stComment);

  TDrawMode = (dmScreen, dmHTML, dmPrinter, dmPrintPreview, dmRTF);

  TTokenType = (ttNone, ttVar, ttProp, ttEvent, ttMethod, ttFunc, ttProc);

  TSelectionMode = (smText, smBlock);

  TMemoClipboardAction = (caCopy,caCut,caPaste);
  TMemoClipboardEvent = procedure(Sender: TObject; ClipboardAction: TMemoClipboardAction; var Allow: boolean) of object;
  TMemoInsertCompletionEntryEvent = procedure(Sender: TObject; var AEntry: string) of object;

  TAnchorEvent = procedure(Sender: TObject; var Anchor: string; X,Y: integer; var Handled: boolean) of object;

  TCaretPosition = record
    Line, Pos: Integer;
  end;

  TCommand = Integer;

  TCellSize = record
    W, H: single;
  end;

  TCellPos = record
    X, Y: integer;
  end;

  TFullPos = record
    LineNo, Pos: Single;
  end;

  TStyle = record
    isComment,
    isBracket,
    isnumber,
    iskeyWord,
    isdelimiter,
    isURL,
    isHighlight: boolean;
    EndBracket: char;
    StartBracket: char;
    index: integer;
  end;

  TLineModifiedState = (lmUnmodified, lmModified, lmSaved);

  TIntList = class(TList<Integer>);

  TLineProp = class
  private
    FObject: TObject;
    FErrStart: TIntList;
    FErrLen: TIntList;
    FExpanded: Boolean;
    FLastChildOfParents: integer;
    FHasParent: Boolean;
    FHasChildren: Boolean;
    procedure SetExpanded(const Value: Boolean);
    function GetLastChild: Boolean;
    procedure SetLastChildOfParents(Value: Integer);
  public
    BreakPoint: Boolean;
    Executable: Boolean;
    Modified: TLineModifiedState;
    ImageIndex: integer;
    Bookmark: Boolean;
    BookmarkIndex: Integer;
    Style: TStyle;
    CachedStyle: Boolean;
    Wrapped: Boolean;
    constructor Create;
    destructor Destroy; override;
    property HasParent: Boolean read FHasParent write FHasParent;
    property HasChildren: Boolean read FHasChildren write FHasChildren;
    property Expanded: Boolean read FExpanded write SetExpanded;
    property LastChild: Boolean read GetLastChild;
    property LastChildOfParents: Integer read FLastChildOfParents write SetLastChildOfParents;
  end;

  TUndo = class
  private
    Memo: TTMSFMXMemo;
    FUndoCurX0, FUndoCurY0: integer;
    FUndoCurX, FUndoCurY: integer;
    FUndoText: string;
    FLinkedUndo: boolean;
  public
    constructor Create(ACurX0, ACurY0, ACurX, ACurY: integer; AText: string);
    function Append(NewUndo: TUndo): boolean; virtual;
    procedure Undo;
    procedure Redo;
    procedure PerformUndo; virtual; abstract;
    procedure PerformRedo; virtual; abstract;
    property LinkedUndo: boolean read FLinkedUndo write FLinkedUndo;
    property UndoCurX0: integer read FUndoCurX0 write FUndoCurX0;
    property UndoCurY0: integer read FUndoCurY0 write FUndoCurY0;
    property UndoCurX: integer read FUndoCurX write FUndoCurX;
    property UndoCurY: integer read FUndoCurY write FUndoCurY;
  end;

  TInsertCharUndo = class(TUndo)
  public
    function Append(NewUndo: TUndo): boolean; override;
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TNewLineUndo = class(TUndo)
    function Append(NewUndo: TUndo): boolean; override;
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TOverwriteCharUndo = class(TUndo)
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TDeleteCharUndo = class(TUndo)
  private
    FIsBackspace: boolean;
  public
    function Append(NewUndo: TUndo): boolean; override;
    procedure PerformUndo; override;
    procedure PerformRedo; override;
    property IsBackspace: boolean read FIsBackspace write FIsBackspace;
  end;

  TDeleteLineUndo = class(TUndo)
  public
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TSelUndo = class(TUndo)
  private
    FUndoSelStartX, FUndoSelStartY,
    FUndoSelEndX, FUndoSelEndY: integer;
  public
    property UndoSelStartX: integer read FUndoSelStartX write FUndoSelStartX;
    property UndoSelStartY: integer read FUndoSelStartY write FUndoSelStartY;
    property UndoSelEndX: integer read FUndoSelEndX write FUndoSelEndX;
    property UndoSelEndY: integer read FUndoSelEndY write FUndoSelEndY;
  end;

  TDeleteBufUndo = class(TSelUndo)
  public
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TPasteUndo = class(TUndo)
  public
    constructor Create(ACurX0, ACurY0, ACurX, ACurY: integer; AText: string; IsLinked: boolean);
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TInsertOverwriteUndo = class(TUndo)
  private
    FInsertMode: boolean;
  public
    constructor Create(AInsertMode: boolean);
    procedure PerformUndo; override;
    procedure PerformRedo; override;
    property InsertMode: boolean read FInsertMode write FInsertMode;
  end;

  TIndentUndo = class(TUndo)
  private
    FIndent: integer;
    FSelRowFrom: integer;
    FSelRowTo: integer;
  public
    constructor Create(SelFrom, SelTo, Indent: integer);
    property Indent: Integer read FIndent write FIndent;
    property SelRowFrom: integer read FSelRowFrom write FSelRowFrom;
    property SelRowTo: integer read FSelRowTo write FSelRowTo;
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TCorrectUndo = class(TUndo)
  public
    constructor Create(LineNo: integer; UndoLine: string);
    procedure PerformUndo; override;
    procedure PerformRedo; override;
  end;

  TAdvUndoList = class(TList<TUndo>)
  private
    FPos: integer;
    FMemo: TTMSFMXMemo;
    FIsPerforming: boolean;
    FLimit: integer;
  protected
    procedure SetLimit(Value: Integer);
  public
    constructor Create;
    destructor Destroy; override;
    function Add(AItem: TUndo): Integer;
    procedure Clear;
    procedure Delete(Index: integer);
    procedure Undo;
    procedure Redo;
    property IsPerforming: boolean read FIsPerforming write FIsPerforming;
    property Memo: TTMSFMXMemo read FMemo write FMemo;
    property Pos: integer read FPos write FPos;
    property Limit: integer read FLimit write SetLimit;
  end;

  TUndoChangeEvent = procedure(Sender: TObject; CanUndo, CanRedo: Boolean) of object;

  TFMXMemoStrings = class(TStringList)
  private
    Memo: TTMSFMXMemo;
    FDeleting: Boolean;
    FLinesProp: TList<TLineProp>;
    FListLengths: TIntList;
    FNoObjCreate: boolean;
    function GetRealCount: integer;
    function GetTextEx: string;
    procedure SetTextEx(const Value: string);
  protected
    function Get(Index: Integer): string; override;
    procedure Put(Index: Integer; const S: string); override;
    function GetObject(Index: Integer): TObject; override;
    procedure PutObject(Index: Integer; AObject: TObject); override;
    procedure RestoreObject(Index: Integer; AObject: TObject);
    function CreateProp(Index: integer): TLineProp;
    procedure ClearLinesProp(UpdateObject: Boolean = True);
    procedure AddLineProp(p: TObject);
    function GetLineProp(Index: Integer): TLineProp;
    procedure SetLineProp(Index: Integer; const Value: TLineProp);
    procedure AssignEx(Source: TPersistent);
    property PropList: TList<TLineProp> read FLinesProp;
  public
    constructor Create;
    destructor Destroy; override;
    function DoAdd(const S: string): integer;
    function Add(const S: string): integer; override;
    function AddObject(const S: string; AObject: TObject): Integer; override;
    procedure Clear; override;
    procedure ClearStrings;
    procedure AddStrings(strings: TStrings); override;
    procedure DoInsert(Index: integer; const S: string);
    procedure Assign(Source: TPersistent); override;
    procedure Delete(Index: integer); override;
    procedure Insert(Index: integer; const S: string); override;
    procedure LoadFromFile(const FileName: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToStream(Stream: TStream); override;
    procedure SaveToFile(const FileName: string); overload; override;
    procedure SaveToFile(const FileName: string;  Encoding: TEncoding); overload; override;
    property RealCount: Integer read GetRealcount;
    property Text: string read GetTextEx write SetTextEx;
  published
    property OnChange;
    property OnChanging;
  end;

  TMemoComboListBox = class(TListBox)
  public
    function AutoAdaptWidth: Single;
  end;

  TMemoGetCompletionListEvent = procedure(Sender: TObject; AToken: string; AList: TStringList) of object;
  TMemoAutoCompletionCustomizeItem = procedure(Sender: TObject; AAutoCompletionItem: TListBoxItem) of object;
  TMemoAutoCompletionCustomizeList = procedure(Sender: TObject; AAutoCompletionList: TMemoComboListBox) of object;
  TMemoBeforeCompleteEvent = procedure(Sender: TObject; AToken: string; var Show: Boolean) of object;
  TMemoOverwriteToggleEvent = procedure(Sender: TObject; Overwrite: boolean; var Allow: boolean) of object;
  TMemoGutterEvent = procedure(Sender: TObject; LineNo: integer) of object;
  TMemoContextMenuEvent = procedure(Sender: TObject; Menu: TPopupMenu) of object;
  TMemoContextMenuItemEvent = procedure(Sender: TObject; Menu: TPopupMenu; Item: TMenuItem) of object;

  TAutoCompletion = class(TPersistent)
  private
    FOwner: TTMSFMXMemo;
    FStartToken: string;
    FSizeDropDown: Boolean;
    FWidth: Integer;
    FMaxWidth: Integer;
    FFromFirstChar: boolean;
    FAutoWidth: Boolean;
    FActive: Boolean;
    FHeight: Integer;
    FAutoCompletionCount: Integer;
  public
    constructor Create(AOwner: TTMSFMXMemo);
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property Active: Boolean read FActive write FActive default True;
    property AutoWidth: Boolean read FAutoWidth write FAutoWidth default true;
    property AutoCompletionCount: Integer read FAutoCompletionCount write FAutoCompletionCount default 8;
    property FromFirstChar: boolean read FFromFirstChar write FFromFirstChar default true;
    property Height: Integer read FHeight write FHeight default 100;
    property SizeDropDown: Boolean read FSizeDropDown write FSizeDropDown default True;
    property StartToken: string read FStartToken write FStartToken;
    property Width: Integer read FWidth write FWidth default 200;
    property MaxWidth: Integer read FMaxWidth write FMaxWidth default 0;
  end;

  TMemoPopup = class(TPopup);

  TTMSFMXActiveLineSettings = class(TPersistent)
  private
    FMemo: TTMSFMXMemo;
    FActiveLineAtCursor: Boolean;
    FShowActiveLine: Boolean;
    FShowActiveLineIndicator: Boolean;
    procedure SetShowActiveLine(const Value: boolean);
    procedure SetShowActiveLineIndicator(const Value: boolean);
    procedure SetActiveLineAtCursor(const Value: Boolean);
  public
    constructor Create(AOwner: TTMSFMXMemo);
    procedure Assign(Source: TPersistent); override;
  published
    property ShowActiveLine: boolean read FShowActiveLine write SetShowActiveLine;
    property ShowActiveLineIndicator: boolean read FShowActiveLineIndicator write SetShowActiveLineIndicator;
    property ActiveLineAtCursor: Boolean read FActiveLineAtCursor write SetActiveLineAtCursor default False;
  end;

  TTMSFMXGutter = class(TPersistent)
  private
    FOwner: TTMSFMXMemo;
    FShowLeadingZeros: boolean;
    FDigitCount: integer;
    FNumberSuffix: string;
    FLineNumberAt: Integer;
    FShowLineNumbers: Boolean;
    FLineNumberStart: Integer;
    FOnChange: TNotifyEvent;
    FFont: TFont;
    procedure SetDigitCount(const Value: integer);
    procedure SetLineNumberAt(const Value: Integer);
    procedure SetLineNumberStart(const Value: Integer);
    procedure SetNumberSuffix(const Value: string);
    procedure SetShowLeadingZeros(const Value: boolean);
    procedure SetShowLineNumbers(const Value: Boolean);
    procedure SetFont(const Value: TFont);
  protected
    procedure FontChanged(Sender: TObject);
    procedure Changed; virtual;

  public
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    procedure Assign(Source: TPersistent); override;
    constructor Create(AOwner: TTMSFMXMemo);
    destructor Destroy; override;
  published
    property DigitCount: integer read FDigitCount write SetDigitCount default 4;
    property Font: TFont read FFont write SetFont;

    property LineNumberAt: Integer read FLineNumberAt write SetLineNumberAt default 1;
    property LineNumberStart: Integer read FLineNumberStart write SetLineNumberStart default 1; // min value is 1

    property NumberSuffix: string read FNumberSuffix write SetNumberSuffix;
    property ShowLineNumbers: Boolean read FShowLineNumbers write SetShowLineNumbers default true;
    property ShowLeadingZeros: boolean read FShowLeadingZeros write SetShowLeadingZeros default false;
  end;

  TEditCharCase = (ecNormal, ecUpperCase, ecLowerCase);


  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXMemo = class(TTMSFMXBaseControl, IVirtualKeyboardControl{$IFDEF ANDROID}, ITextInput{$ENDIF})
  private
    FImeText: String;
    FDblClicked: Boolean;
    FCaret: TCaret;
    FMemoContext: TPopupMenu;
    FBookmarkList: TIntList;
    FUpdateCount: integer;
    FActiveLine: integer;
    FAutoCompleteList: TStringList;
    FAutoCompleteDot: Boolean;
    FDotPoint: TPoint;
    FDroppedDown: Boolean;
    FPopup: TTMSFMXPopup;
    FListBox: TMemoComboListBox;
    FCellSize: TCellSize;
    FGutter: TTMSFMXGutter;
    FActiveLineSettings: TTMSFMXActiveLineSettings;
    FSyntaxStyles: TTMSFMXMemoCustomStyler;
    FLeftCol: integer;
    FTopLine: integer;
    FLines: TFMXMemoStrings;
    FCaseSensitive: boolean;
    FHideSelection: boolean;
    FTempdelimiters: string;
    FtmpNoStart, FtmpNo, FtmpNoHex: string;
    FSelStartX, FSelStartY,
    FSelEndX, FSelEndY,
    FPrevSelX, FPrevSelY: Integer;
    FUseStyler: Boolean;
    FUrlAware: Boolean;
    FUrlDelimiters: string;
    FTabSize: integer;
    FbackupTopLine: Integer;
    FCurX: Integer;
    FCurY: Integer;
    FMaxLength: Integer;
    FTextService: TTextService;
    FKeyboardType: TVirtualKeyboardType;
    FCaretVisible: Boolean;
    FCachedDelimiters: string;
    FOverwrite: Boolean;
    FFont: TFont;
    FFontColor: TAlphaColor;
    FSelButtonDown: boolean;
    FLeftButtonDown: boolean;
    FSelClick: Boolean;
    FSelChange: boolean;
    FSelectSingleLine: boolean;
    FSelectionMode: TSelectionMode;
    FVertScrollBar, FHorzScrollBar: TScrollBar;
    FOnSelectionChange: TNotifyEvent;
    FTrimTrailingSpaces: boolean;
    FSelectionTextColor: TAlphaColor;
    FSelectionBackGroundColor: TAlphaColor;
    FInternalDelete: Boolean;
    FOnClipboardAction: TMemoClipboardEvent;
    FOnAnchorClick: TAnchorEvent;
    FDelErase: Boolean;
    FEnhancedHomeKey: Boolean;
    FClipLine: Integer; // -1 = all
    FRightMargin: Integer;
    FShowRightMargin: boolean;
    FUndoList: TAdvUndoList;
    FUndoLimit: Integer;
    FUndoLineByLine: Boolean;
    FOnUndoChange: TUndoChangeEvent;
    FAutoIndent: Boolean;
    FAutoExpand: Boolean;
    FWantTab: Boolean;
    FSmartTabs: Boolean;
    FOnGetAutoCompletionList: TMemoGetCompletionListEvent;
    FOnAutoCompletion: TNotifyEvent;
    FOnInsertAutoCompletionEntry: TMemoInsertCompletionEntryEvent;
    FAutoCompletion: TAutoCompletion;
    FOnCancelAutoCompletion: TNotifyEvent;
    FOnAutoCompletionCustomizeItem: TMemoAutoCompletionCustomizeItem;
    FOnGutterDblClick: TMemoGutterEvent;
    FOnGutterClick: TMemoGutterEvent;
    FOnAutoCompletionCustomizeList: TMemoAutoCompletionCustomizeList;
    FOnBeforeAutoCompletion: TMemoBeforeCompleteEvent;
    FBreakPointBackGroundColor: TAlphaColor;
    FBreakPointTextColor: TAlphaColor;
    FShowBands: boolean;
    FHtml: TStringList;
    FHtmlFont: string;
    FRTFEngine: TRTFEngine;
    FHighlightText: string;
    FCharCase: TEditCharCase;
    FGutterLine: integer;
    FOnCursorChange: TNotifyEvent;
    FOnOverWriteToggle: TMemoOverwriteToggleEvent;
    FOnShowContextMenu: TMemoContextMenuEvent;
    FOnContextMenuItemClick: TMemoContextMenuItemEvent;
    FReadOnly: boolean;
    FLineSpacing: integer;
    procedure CheckCodeInsightChar(Key: Char);
    procedure PerformAutoComplete;
    function IsCompletionListActivationChar(Ch: Char): boolean; virtual;
    procedure OnFontChanged(Sender: TObject);
    procedure GutterChanged(Sender: TObject);
    function InternalUndoList: TAdvUndoList;
    procedure SetUndoLimit(Value: integer);
    procedure SetLeftCol(const Value: integer);
    procedure SetTopLine(const Value: integer);
    procedure SetLines(const Value: TFMXMemoStrings);
    procedure SetSyntaxStyles(const Value: TTMSFMXMemoCustomStyler);
    procedure ExpandSelection;
    function DeleteSelectionInt: boolean;
    function GetSelText: string;
    procedure SetSelText(const AValue: string);
    function GetSelLength: Integer;
    function GetSelStart: Integer;
    procedure SetSelStart(const Value: Integer);
    procedure SetSelLength(const Value: Integer);
    procedure SetSelectionMode(const Value: TSelectionMode);
    function GetVersion: String;
    function GetVersionNr: Integer;
    procedure SetVersion(const Value: String);
    function GetUpStyle(stopat: Integer): TStyle;
    function GetInternalStyles: TTMSFMXMemoCustomStyler;
    procedure SetUseStyler(const Value: boolean);
    function GetInternalLines: TFMXMemoStrings;
    procedure SetInternalLines(const Value: TFMXMemoStrings);
    function GetLineStyle(Index: Integer; var LineStyle: TStyle): Boolean;
    procedure SetLineStyle(Index: Integer; const LineStyle: TStyle);
    procedure ExtractURL(s: string; var urls: TStringList);
    procedure SetUrlAware(const Value: boolean);
    function IsDelimiter(value: Char): boolean;
    procedure SelClickUpdate(X, Y: Single; Down: Boolean; Shift: TShiftState; Button: TMouseButton);
    procedure ResizeScrollBars;
    function EditorRect: TRectF;
    function CellRect(ACol, ARow: integer): TRectF;
    function LineRect(ARow: integer): TRectF;
    function TrimRightWW(LineNo: Integer): string;
    procedure DoCommand(cmd: TCommand; const AShift: TShiftState);
    procedure GoHome(Shift: TShiftState);
    procedure GoEnd(Shift: TShiftState);
    procedure DeleteChar(OldX, OldY: integer);
    procedure BackSpace;
    procedure NewLine;
    function IndentCurrLine: string;
    procedure MovePage(dP: integer; Shift: TShiftState);
    procedure ClearLineStylesFromTo(FromLine, ToLine: integer);
    procedure SetCurX(Value: Integer);
    procedure SetCurY(Value: Integer);
    function CaretVisible: Boolean;
    procedure ShowCaret(Value: boolean);
    procedure MoveCaret(dX, dY: integer; Shift: TShiftState);
    procedure MakeVisible;
    function GetCaretX: Single;
    function GetCaretY: Single;
    procedure SetCaretX(const Value: Single);
    procedure SetCaretY(const Value: Single);
    function GetVisibleN(const Index: Integer): Integer;
    procedure SetFont(const Value: TFont);
    procedure SetTrimTrailingSpaces(const Value: boolean);
    function GetBookmark(Index: Integer): Boolean;
    function GetBookmarkCount: integer;
    function GetBookmarkIndex(Index: Integer): Integer;
    function GetBookmarkInfo(Index: integer; var AValue: integer): boolean;
    function GetBookmarks(Index: Integer): Integer;
    function GetBreakPoint(Index: Integer): Boolean;
    procedure SetBookmark(Index: Integer; const Value: Boolean);
    procedure SetBookmarkIndex(Index: Integer; const Value: Integer);
    procedure SetBookmarks(Index: Integer; const Value: Integer);
    procedure SetBreakPoint(Index: Integer; const Value: Boolean);
    procedure SetRightMargin(const Value: integer);
    procedure SetShowRightMargin(const Value: boolean);
    procedure SetGutter(const Value: TTMSFMXGutter);
    procedure SetShowBands(const Value: boolean);
    procedure SetActiveLineSettings(const Value: TTMSFMXActiveLineSettings);
    procedure SetActiveLine(const Value: integer);
    procedure SetFontColor(const Value: TAlphaColor);
    procedure SetBreakLineColor(const Value: TAlphaColor);
    procedure SetBreakLineTextColor(const Value: TAlphaColor);
    procedure SetSelectionColor(const Value: TAlphaColor);
    procedure SetSelectionTextColor(const Value: TAlphaColor);
    procedure SetHighlightText(const Value: string);
    procedure SetLineSpacing(const Value: integer);
    property CaretX: Single read GetCaretX write SetCaretX;
    property CaretY: Single read GetCaretY write SetCaretY;
    property VisibleLineCount: Integer index 1 read GetVisibleN;
    property LastVisiblePos: Integer index 2 read GetVisibleN;
    { IVirtualKeyboardControl }
    procedure SetKeyboardType(Value: TVirtualKeyboardType);
    function GetKeyboardType: TVirtualKeyboardType;
    {$IFDEF DELPHIXE5_LVL}
    procedure SetReturnKeyType(Value: TReturnKeyType);
    function GetReturnKeyType: TReturnKeyType;
    {$ENDIF}
    procedure BackWord;
  protected
    FLetRefresh: Boolean;
    FSetMaxLength: Boolean;
    {$IFDEF ANDROID}
    function GetTextService: TTextService;
    procedure UpdateCaretPoint;
    function GetTargetClausePointF: TPointF;
    procedure StartIMEInput;
    procedure EndIMEInput;
    function GetSelection: string;
    function GetSelectionRect: TRectF;
    function GetSelectionBounds: TRect;
    function HasText: Boolean;
    function IsReadOnly: Boolean;
    {$ENDIF}

    {$IFDEF DELPHIXE6_LVL}
    function IsPassword: Boolean;
    {$ENDIF}

    procedure ApplyAdditionalStyle(AControl: TFMXObject); override;
    procedure ContextMenuItemClick(Sender: TObject);
    procedure DialogKey(var Key: Word; Shift: TShiftState); override;
    procedure CalculateCellSize;
    procedure DoClosePopup(Sender: TObject);
    function CreateListBox: TMemoComboListBox; virtual;
    procedure ListBoxItemClick(Sender: TObject);
    procedure ListBoxKeyDown(Sender: TObject; var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState);
    procedure ListBoxKeyUp(Sender: TObject; var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState);
    procedure ListBoxItemApplyStyleLookup(Sender: TObject);
    procedure ListBoxApplyStyleLookup(Sender: TObject);
    procedure ApplyStyle; override;
    procedure DoEnter; override;
    procedure DoExit; override;
    procedure PaintChildren; override;
    procedure DoGutterMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure DoGutterDblClick(Sender: TObject);
    procedure DoAnchorClick(var Anchor: string; X,Y: integer); virtual;
    procedure DblClick; override;
    function FindTextInt(SearchStr: string; Options: TTMSFMXFindDialogOptions; Count: boolean): integer;
    function NextToken(const s: string; StartFrom: integer; var Offset: integer): string;
    function FormatLineNumber(Line: integer): string;
    procedure PaintGutter(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
    procedure PaintLine(ACanvas: TCanvas; LineNo: Integer; var style: TStyle; DM: TDrawMode; PR: TRectF; VisLineNo: Integer = -1);
    procedure PaintHTML(Part: string; var Drawstyle: TStyle; lineno: integer);
    procedure PaintRTF(Part: string; var Drawstyle: TStyle; lineno: integer);
    procedure PaintMemo(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
    procedure Loaded; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
    procedure KeyDown(var Key: Word; var KeyChar: System.WideChar; Shift: TShiftState); override;
    procedure DoVScroll(Sender: TObject);
    procedure DoHScroll(Sender: TObject);
    procedure LinesChanged(Sender: TObject);
    procedure InitScrollbars;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    function WordIsURL(s: string): Boolean; virtual;
    procedure TabLine(AShift: TShiftState); //perform the tab and smarttab if set
    procedure SelectionChanged; virtual;
    procedure DoCopyToClipboard; virtual;
    procedure DoCutToClipboard; virtual;
    procedure DoPasteFromClipboard; virtual;
    procedure DoCursorChange; virtual;
    procedure DoOverwriteToggle(var Allow: boolean); virtual;
    procedure DoClipboardAction(ClipboardAction: TMemoClipboardAction; var Allow: boolean); virtual;
    procedure DoShowContextMenu(Menu: TPopupMenu); virtual;
    procedure DoContextMenuItemClick(Menu: TPopupMenu; Item: TMenuItem); virtual;
    function CreateCaret: TCaret;
    property Caret: TCaret read FCaret write FCaret;
    property InternalStyles: TTMSFMXMemoCustomStyler read GetInternalStyles;
    property InternalLines: TFMXMemoStrings read GetInternalLines write SetInternalLines;
    property MaxLength: Integer read FMaxLength;
    procedure SetMaxLength;
    procedure ClearLineStyles;
    procedure ResizeEditor;
    procedure ClearUndoList;
    procedure UndoChange;
    procedure OutputHTML(FixedFonts: Boolean; fromline: integer = -1; toline: integer = -1; fragment: boolean = false);
    procedure OutputRTF(FixedFonts: Boolean; fromline: integer = -1; toline: integer = -1);
  public
    property MemoContext: TPopupMenu read FMemoContext write FMemoContext;
    procedure InsertChar(C: char);
    procedure BlockUpdate;
    procedure EndBlockUpdate;
    procedure UpdateMemo;
    procedure ClearBookmarks;
    procedure ClearBreakpoints;
    procedure GotoBookmark(Index: Integer);
    function HasBookmarks: Boolean;

    function FindTextCount(SearchStr: string; Options: TTMSFMXFindDialogOptions): Integer;
    function FindText(SearchStr: string; Options: TTMSFMXFindDialogOptions): Integer;
    function FindAndReplace(SearchStr, NewStr: string; Options: TTMSFMXFindDialogOptions): Integer;

    procedure TextFromPos(X, Y: Integer; var TextPos: Integer);
    procedure PosFromText(TextPos: integer; var X, Y: Integer);
    function CharFromPos(X, Y: Single): TFullPos;
    function CellFromPos(X, Y: Single): TCellPos;

    property ActiveLine: integer read FActiveLine write SetActiveLine;
    property Bookmark[Index: Integer]: Boolean read GetBookmark write SetBookmark;
    property Bookmarks[Index: Integer]: Integer read GetBookmarks write SetBookmarks;
    property BookmarkIndex[Index: Integer]: Integer read GetBookmarkIndex write SetBookmarkIndex;
    property BookmarkCount: integer read GetBookmarkCount;
    property BreakPoint[Index: Integer]: Boolean read GetBreakPoint write SetBreakPoint;
    property CurX: Integer read FCurX write SetCurX;
    property CurY: Integer read FCurY write SetCurY;
    procedure SetCursor(ACurX, ACurY: integer);

    function GetContextMenuItem(Command: integer): TMenuItem;
    function GetAutoCompletionFont: TLabel;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DeleteLine;
    procedure ClearSelection;
    procedure Clear;
    procedure CopyToClipboard;
    procedure PasteFromClipboard;
    procedure CutToClipboard;
    procedure SelectAll;
    procedure DeleteSelection;
    procedure SelectWordAtCursor;
    procedure BlockIndent(FromLine, ToLine, Indent: Integer; AllowUndo: boolean = true);
    function FullWordAtCursor: String;
    function WordAtXY(X, Y: Integer): String;
    function TokenAtXY(X, Y: Integer): String;
    function IsTokenBoundary(ch: char): boolean; virtual;
    function IsWordBoundary(ch: char): boolean; virtual;

    property HighlightText: string read FHighlightText write SetHighlightText;

    property Selection: string read GetSelText write SetSelText;
    property SelStart: integer read GetSelStart write SetSelStart;
    property SelLength: integer read GetSelLength write SetSelLength;
    property SelStartX: Integer read FSelStartX;
    property SelStartY: Integer read FSelStartY;
    property SelEndX: Integer read FSelEndX;
    property SelEndY: Integer read FSelEndY;
    property SelectionMode: TSelectionMode read FSelectionMode write SetSelectionMode default smText;
    property SelectSingleLine: boolean read FSelectSingleLine write FSelectSingleLine default false;
    property Overwrite: Boolean read FOverwrite write FOverwrite;
    property Popup: TTMSFMXPopup read FPopup;
    property PopupList: TMemoComboListBox read FListBox;

    property LeftCol: integer read FLeftCol write SetLeftCol;
    property TopLine: integer read FTopLine write SetTopLine;
    function GetVScroll: TScrollBar;
    function GetHScroll: TScrollBar;
    function GetGutter: TRectangle;
    function GetMemoRect: TRectangle;
    function GetGutterWidth: Integer;
    function GetVertScrollWidth: Integer;
    function GetHorzScrollHeight: Integer;

    procedure HideCaret;
    procedure ShowCaretProc;
    function SaveToHTML(FileName: string; Fixedfonts: Boolean = True): Boolean;
    function SaveToHTMLStream(AStream: TMemoryStream; Fixedfonts: Boolean = True): Boolean;
    function SaveToRTF(FileName: string; Fixedfonts: Boolean = True): Boolean;
    function SaveToRTFStream(AStream: TMemoryStream;Fixedfonts: Boolean = True): Boolean;

    procedure DoInsertAutoCompletionEntry(var AEntry: string); virtual;
    procedure GetAutoCompletionList(AToken: String; AList: TStringList); virtual;
    procedure UpdateCompletionList(AToken: String); virtual;
    function BeforeAutoCompletion(AToken: String): boolean; virtual;
    procedure DoPopup(AUpdate: Boolean = False); virtual;
    procedure SelectPopupValue(AutoParenthesis: Boolean); virtual;
    function GetBookMarkBitmap: TBitmap;
    function GetBreakPointBitmap: TBitmap;
    function GetActiveLineBitmap: TBitmap;
    function GetActiveLineColor: TAlphaColor;
    function GetActiveLineTextColor: TAlphaColor;
    function GetBandColor: TAlphaColor;
    function GetBkgColor: TAlphaColor;
    function GetSelectionColor: TAlphaColor;
    function GetSelectionTextColor: TAlphaColor;
    function GetBreakLineColor: TAlphaColor;
    function GetBreakLineTextColor: TAlphaColor;

    property SelectionTextColor: TAlphaColor read GetSelectionTextColor write SetSelectionTextColor default TAlphaColorRec.White;
    property SelectionBackGroundColor: TAlphaColor read GetSelectionColor write SetSelectionColor default TAlphaColorRec.Navy;
    property BreakPointTextColor: TAlphaColor read GetBreakLineTextColor write SetBreakLineTextColor default TAlphaColorRec.White;
    property BreakPointBackGroundColor: TAlphaColor read GetBreakLineColor write SetBreakLineColor default TAlphaColorRec.Red;

    // Undo
    procedure ClearUndoRedo;
    procedure Undo;
    procedure Redo;
    procedure AddUndoStep(AUndoStep: TUndo);
    function CanUndo: boolean;
    function CanRedo: boolean;
    function CanCopy: boolean;
    function CanCut: boolean;
    property UndoList: TAdvUndoList read InternalUndoList;
    //---
  published
    property ActiveLineSettings: TTMSFMXActiveLineSettings read FActiveLineSettings write SetActiveLineSettings;
    property AutoCompletion: TAutoCompletion read FAutoCompletion write FAutoCompletion;
    property AutoIndent: Boolean read FAutoIndent write FAutoIndent default true;
    property AutoExpand: Boolean read FAutoExpand write FAutoExpand default true;
    property CaseSensitive: boolean read FCaseSensitive write FCaseSensitive;
    property CharCase: TEditCharCase read FCharCase write FCharCase default ecNormal;
    property DelErase: Boolean read FDelErase write FDelErase default True;
    property EnhancedHomeKey: Boolean read FEnhancedHomeKey write FEnhancedHomeKey default False;
    property Font: TFont read FFont write SetFont;
    property FontColor: TAlphaColor read FFontColor write SetFontColor default TAlphaColorRec.Black;
    property Gutter: TTMSFMXGutter read FGutter write SetGutter;
    property HideSelection: Boolean read FHideSelection write FHideSelection default False;
    property KeyboardType: TVirtualKeyboardType read GetKeyboardType write SetKeyboardType;
    property Lines: TFMXMemoStrings read FLines write SetLines;
    property LineSpacing: integer read FLineSpacing write SetLineSpacing default 2;
    property ReadOnly: boolean read FReadOnly write FReadOnly;
    property RightMargin: integer read FRightMargin write SetRightMargin default 80;
    property SyntaxStyles: TTMSFMXMemoCustomStyler read FSyntaxStyles write SetSyntaxStyles;
    property ShowBands: Boolean read FShowBands write SetShowBands default false;
    property ShowRightMargin: Boolean read FShowRightMargin write SetShowRightMargin default True;
    property SmartTabs: Boolean read FSmartTabs write FSmartTabs default False;
    property TabSize: integer read FTabSize write FTabSize;
    property TrimTrailingSpaces: boolean read FTrimTrailingSpaces write SetTrimTrailingSpaces;
    property UseStyler: Boolean read FUseStyler write SetUseStyler;
    property UrlAware: Boolean read FUrlAware write SetUrlAware default True;
    property Version: String read GetVersion write SetVersion;
    property WantTab: boolean read FWantTab write FWantTab default false;
    property UndoLimit: Integer read FUndoLimit write SetUndoLimit default 100;
    property UndoLineByLine: boolean read FUndoLineByLine write FUndoLineByLine default false;
    property OnAnchorClick: TAnchorEvent read FOnAnchorClick write FOnAnchorClick;
    property OnAutoCompletion: TNotifyEvent read FOnAutoCompletion write FOnAutoCompletion;
    property OnAutoCompletionCustomizeItem: TMemoAutoCompletionCustomizeItem read FOnAutoCompletionCustomizeItem write FOnAutoCompletionCustomizeItem;
    property OnAutoCompletionCustomizeList: TMemoAutoCompletionCustomizeList read FOnAutoCompletionCustomizeList write FOnAutoCompletionCustomizeList;
    property OnBeforeAutoCompletion: TMemoBeforeCompleteEvent read FOnBeforeAutoCompletion write FOnBeforeAutoCompletion;
    property OnCancelAutoCompletion: TNotifyEvent read FOnCancelAutoCompletion write FOnCancelAutoCompletion;
    property OnClipboardAction: TMemoClipboardEvent read FOnClipboardAction write FOnClipboardAction;
    property OnContextMenuItemClick: TMemoContextMenuItemEvent read FOnContextMenuItemClick write FOnContextMenuItemClick;
    property OnCursorChange: TNotifyEvent read FOnCursorChange write FOnCursorChange;
    property OnGetAutoCompletionList: TMemoGetCompletionListEvent read FOnGetAutoCompletionList write FOnGetAutoCompletionList;
    property OnGutterClick: TMemoGutterEvent read FOnGutterClick write FOnGutterClick;
    property OnGutterDblClick: TMemoGutterEvent read FOnGutterDblClick write FOnGutterDblClick;
    property OnInsertAutoCompletionEntry: TMemoInsertCompletionEntryEvent read FOnInsertAutoCompletionEntry write FOnInsertAutoCompletionEntry;
    property OnOverwriteToggle: TMemoOverwriteToggleEvent read FOnOverWriteToggle write FOnOverWriteToggle;
    property OnSelectionChange: TNotifyEvent read FOnSelectionChange write FOnSelectionChange;
    property OnShowContextMenu: TMemoContextMenuEvent read FOnShowContextMenu write FOnShowContextMenu;
    property OnUndoChange: TUndoChangeEvent read FOnUndoChange write FOnUndoChange;
  end;

  TRegionDefinition = class(TCollectionItem)
  private
    FRegionType: TRegionType;
    FRegionEnd: string;
    FIdentifier: string;
    FRegionStart: string;
    FShowComments: Boolean;
    procedure SetIdentifier(const Value: string);
    procedure SetRegionEnd(const Value: string);
    procedure SetRegionStart(const Value: string);
    procedure SetRegionType(const Value: TRegionType);
  protected
    function GetDisplayName: string; override;
  public
    constructor Create(Collection: TCollection); override;
    procedure Assign(Source: TPersistent); override;
  published
    property Identifier: string read FIdentifier write SetIdentifier;
    property RegionStart: string read FRegionStart write SetRegionStart;
    property RegionEnd: string read FRegionEnd write SetRegionEnd;
    property RegionType: TRegionType read FRegionType write SetRegionType;
    property ShowComments: Boolean read FShowComments write FShowComments;
  end;

  TElementStyle = class(TCollectionItem)
  private
    FOwner: TTMSFMXMemo;
    FKeyWords: TStringList;
    FFont: TFont;
    FBGColor: TAlphaColor;
    FInfo: string;
    FStyleType: TStyleType;
    StyleNo: integer;
    FBracketStart: Char;
    FBracketEnd: Char;
    FSymbols: string;
    FFontColor: TAlphaColor;
    FCommentLeft: string;
    FCommentRight: string;
    procedure SetColorbg(const Value: TAlphaColor);
    procedure SetFont(const Value: TFont);
    procedure SetKeyWords(const Value: TStringList);
    procedure SetStyleType(const Value: TStyleType);
    procedure SetBracketStart(const Value: Char);
    procedure SetBracketEnd(const Value: Char);
    procedure SetSymbols(const Value: string);
  protected
    function GetDisplayName: string; override;
  public
    procedure Changed;
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property CommentLeft: string read FCommentLeft write FCommentLeft;
    property CommentRight: string read FCommentRight write FCommentRight;
    property KeyWords: TStringList read FKeyWords write SetKeyWords;
    property Font: TFont read FFont write SetFont;
    property FontColor: TAlphaColor read FFontColor write FFontColor;
    property BGColor: TAlphaColor read FBGColor write SetColorbg;
    property StyleType: TStyleType read FStyleType write SetStyleType;
    property BracketStart: Char read FBracketStart write SetBracketStart;
    property BracketEnd: Char read FBracketEnd write SetBracketEnd;
    property Symbols: string read FSymbols write SetSymbols;
    property Info: string read Finfo write Finfo;
  end;

  TElementStyles = class(TOwnedCollection)
  private
    FOwner: TTMSFMXMemo;
    FModified: boolean;
    function GetItem(Index: integer): TElementStyle;
    procedure SetItem(Index: integer; const Value: TElementStyle);
  protected
    procedure Update(Item: TCollectionItem); override;
  public
    function CreateItemClass: TCollectionItemClass; virtual;
    constructor Create(AOwner: TComponent);
    procedure Init;
    function Add: TElementStyle;
    function Insert(Index: integer): TElementStyle;
    property Items[Index: integer]: TElementStyle read GetItem write SetItem; default;
    function IsWordBoundary(ch: char): boolean;
  published
  end;

  TRegionDefinitions = class(TTMSFMXBaseCollection)
  private
    FComponent: TPersistent;
    function GetItem(Index: Integer): TRegionDefinition;
    procedure SetItem(Index: Integer; Value: TRegionDefinition);
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(Component: TPersistent);
    destructor Destroy; override;
    property Items[Index: Integer]: TRegionDefinition read GetItem write SetItem; default;
    function Add: TRegionDefinition;
    function Insert(Index: Integer): TRegionDefinition;
  end;

  TCharStyle = class(TPersistent)
  private
    FTextColor, FBkColor: TAlphaColor;
    FStyle: TFontStyles;
  public
    procedure Assign(Source: TPersistent); override;
  published
    property TextColor: TAlphaColor read FTextColor write FTextColor;
    property BkColor: TAlphaColor read FBkColor write FBkColor;
    property Style: TFontStyles read FStyle write FStyle;
  end;

  THintParameter = class(TPersistent)
  private
    FTextColor, FBkColor: TAlphaColor;
    FStartchar: Char;
    FEndchar: Char;
    FDelimiterchar: Char;
    FParameters: Tstringlist;
    FWritedelimiterchar: char;
    FHintClassDelimiter: Char;
    procedure SetParameters(const Value: Tstringlist);
  public
    constructor Create;
    procedure Assign(Source: TPersistent); override;
    destructor Destroy; override;
  published
    property TextColor: TAlphaColor read FTextColor write FTextColor;
    property BkColor: TAlphaColor read FBkColor write FBkColor;
    property HintCharStart: Char read FStartchar write FStartChar;
    property HintCharEnd: Char read FEndchar write FEndChar;
    property HintCharDelimiter: Char read FDelimiterChar write FDelimiterChar;
    property HintClassDelimiter: Char read FHintClassDelimiter write FHintClassDelimiter;
    property HintCharWriteDelimiter: Char read FWriteDelimiterChar write FWritedelimiterChar;
    property Parameters: TStringlist read FParameters write SetParameters;
  end;

  TTMSFMXMemoCustomStyler = class(TComponent)
  private
    FAllStyles: TElementStyles;
    FLineComment: string;
    FMultiCommentLeft: string;
    FMultiCommentRight: string;
    FCommentStyle: TCharStyle;
    FNumberStyle: TCharStyle;
    FURLStyle: TCharStyle;
    FlistAuto: TStringList;
    FHintParameter: THintParameter;
    FNumericChars: string;
    FHexIdentifier: string;
    FBlockEnd: string;
    FBlockStart: string;
    FLiteral: string;
    FCustomDraw: Boolean;
    FFilter: string;
    FDefaultExtension: string;
    FExtensions: string;
    FDescription: string;
    FStylerName: string;
    FRegionDefinitions: TRegionDefinitions;
    FEscapeChar: string;
    FHighlightStyle: TCharStyle;
    procedure SetStyle(const Index: integer; const Value: TCharStyle); virtual;
    procedure SetStyles(const Value: TElementStyles);
    procedure SetListAuto(const Value: TStringList);
    procedure SetRegionDefinitions(Value: TRegionDefinitions);
    procedure SetHighlightStyle(const Value: TCharStyle);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure Loaded; override;
    function PosCommentLeft(s: string; var cl: integer): integer;
    function PosCommentRight(s: string; var cl: integer): integer;
    function HasMultiComment: boolean;
    property RegionDefinitions: TRegionDefinitions read FRegionDefinitions write SetRegionDefinitions;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;

    property Description: string read FDescription write FDescription;
    property Filter: string read FFilter write FFilter;
    property DefaultExtension: string read FDefaultExtension write FDefaultExtension;
    property Extensions: string read FExtensions write FExtensions;
    property StylerName: string read FStylerName write FStylerName;
    property BlockStart: string read FBlockStart write FBlockStart;
    property BlockEnd: string read FBlockEnd write FBlockEnd;
    property HighlightStyle: TCharStyle read FHighlightStyle write SetHighlightStyle;
    property LineComment: string read FLineComment write FLineComment;
    property Literal: string read FLiteral write FLiteral;
    property MultiCommentLeft: string read FMultiCommentLeft write FMultiCommentLeft;
    property MultiCommentRight: string read FMultiCommentRight write FMultiCommentRight;
    property AllStyles: TElementStyles read FAllStyles write SetStyles;
    property CommentStyle: TCharStyle index 1 read FCommentStyle write SetStyle;
    property NumberStyle: TCharStyle index 2 read FNumberStyle write SetStyle;
    property URLStyle: TCharStyle index 3 read FURLStyle write SetStyle;
    property AutoCompletion: TStringlist read FlistAuto write SetlistAuto;
    property HintParameter: THintParameter read fHintParameter write fhintparameter;
    property NumericChars: string read FNumericChars write FNumericChars;
    property HexIdentifier: string read FHexIdentifier write FHexIdentifier;
    property CustomDraw: Boolean read FCustomDraw write FCustomDraw;
    property EscapeChar: string read FEscapeChar write FEscapeChar;
  end;


  TTMSFMXMemoAction = class(TAction)
  private
    FMemo: TTMSFMXMemo;
    procedure SetMemo(Value: TTMSFMXMemo);
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    destructor Destroy; override;
    function HandlesTarget(Target: TObject): Boolean; override;
    procedure UpdateTarget(Target: TObject); override;
  published
    property Memo: TTMSFMXMemo read FMemo write SetMemo;
  end;

  TTMSFMXMemoCut = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
  end;

  TTMSFMXMemoCopy = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
  end;

  TTMSFMXMemoPaste = class(TTMSFMXMemoAction)
  public
    procedure UpdateTarget(Target: TObject); override;
    procedure ExecuteTarget(Target: TObject); override;
  end;

  TTMSFMXMemoSelectAll = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
    procedure UpdateTarget(Target: TObject); override;
  end;

  TTMSFMXMemoUndo = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
    procedure UpdateTarget(Target: TObject); override;
  end;

  TTMSFMXMemoRedo = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
    procedure UpdateTarget(Target: TObject); override;
  end;

  TTMSFMXMemoDelete = class(TTMSFMXMemoAction)
  public
    procedure ExecuteTarget(Target: TObject); override;
    { UpdateTarget is required because TEditAction.UpdateTarget specifically
      checks to see if the action is TEditCut or TEditCopy }
    procedure UpdateTarget(Target: TObject); override;
  end;


var
  iCount: Integer;

implementation

{$R FMX.TMSMemo.res}

uses
  Variants, FMX.Ani;

const
  cmDelete = vkDelete;
  cmBackSpace = vkBack;
  cmNewLine = vkReturn;
  cmHome = vkHome;
  cmEnd = vkEnd;
  cmPageUp = vkPrior;
  cmPageDown = vkNext;
  cmInsert = vkInsert;
  cmSelectAll = 1;
  cmCopy = 3;
  cmCut = 24;
  cmPaste = 22;
  cmTab = vkTab;
  cmEscape = vkEscape;

  menuRedo = 1;
  menuUndo = 2;
  menuCut = 3;
  menuCopy = 4;
  menuPaste = 5;
  menuSelWord = 6;
  menuSelAll = 7;
  menuDelSel = 8;
  menuDelLine = 9;


type
  TCharSet = array of char;

function CheckSeparator(ch: char): boolean;
begin
  Result := (ch = #32) or (ch = #39) or (ch = '"') or (ch ='(') or (ch = ')');
end;

function VarPos(su,s:string;var Res:Integer):Integer;
begin
  Res := Pos(su,s);
  Result := Res;
end;

function RemoveTrailingSpaces(const S: string): string;
var
  I: Integer;
begin
  I := Length(S);
  {$IFDEF DELPHI_LLVM}
  while (I >= 0) and (S[I - 1] = #32) do Dec(I);
  {$ELSE}
  while (I > 0) and (S[I] = #32) do Dec(I);
  {$ENDIF}
  Result := Copy(S, 1, I);
end;

function DupeString(str: string; count: integer): string;
var
  i: integer;
begin
  Result := '';
  for i := 1 to Count do
  begin
    Result := Result + str;
  end;
end;

function GetRValue(rgb: DWORD): Byte;
begin
  Result := Byte(rgb);
end;

function GetGValue(rgb: DWORD): Byte;
begin
  Result := Byte(rgb shr 8);
end;

function GetBValue(rgb: DWORD): Byte;
begin
  Result := Byte(rgb shr 16);
end;

function HTMLClr(color: TAlphaColor): string;
begin
  Result := '#' + inttohex(GetBValue(color), 2) +
    inttohex(GetGValue(color), 2) +
    inttohex(GetRValue(color), 2);
end;


function varposl(sub,s:string; sl:integer; from: integer; var vp: integer): integer;
var
  fnd: boolean;
begin
  Result := -1;
  fnd := false;

  while from <= sl do
  begin
    if fnd then
    begin
      {$IFDEF DELPHI_LLVM}
      fnd := sub[1] = s[from - 1];
      {$ELSE}
      fnd := sub[2] = s[from];
      {$ENDIF}
      if fnd then
      begin
        vp := from - 1;
        Result := vp;
        Exit;
      end;
    end;
    {$IFDEF DELPHI_LLVM}
    fnd := sub[0] = s[from - 1];
    {$ELSE}
    fnd := sub[1] = s[from];
    {$ENDIF}
    inc(from);
  end;
end;

{ TTMSFMXMemo }

constructor TTMSFMXMemo.Create(AOwner: TComponent);
var
  i: Integer;
  PlatformTextService: IFMXTextService;
  mni, mniRoot: TMenuItem;

begin
  inherited;

  FCaret := CreateCaret;
  FBookmarkList := TIntList.Create;
  for i := 1 to 10 do
    FBookmarkList.Add(-1);
  FAutoCompleteList := TStringList.Create;
  FPopup := TTMSFMXPopup.Create(Self);
  FPopup.ParentControl := Self;
  FPopup.PlacementTarget := Self;
  {$IFDEF DELPHIXE6_LVL}
  FPopup.Placement := TPlacement.Absolute;
  {$ELSE}
  FPopup.Placement := TPlacement.plAbsolute;
  {$ENDIF}
  FPopup.StaysOpen := False;
  FPopup.Stored := False;
//  FPopup.Parent := Self;
  FPopup.Locked := True;
  FPopup.DesignVisible := False;
  FPopup.DragWithParent := True;
  FPopup.Transparent := True;
  FPopup.Shadow := False;
  FPopup.OnClosePopup := DoClosePopup;
  FListBox := CreateListBox;
  FListBox.OnApplyStyleLookup := ListBoxApplyStyleLookUp;
  FListBox.OnClick := ListBoxItemClick;
  FListBox.Parent := Popup;
  FListBox.Stored := False;
  {$IFDEF DELPHIXE6_LVL}
  FListBox.Align := TAlignLayout.Client;
  {$ELSE}
  FListBox.Align := TAlignLayout.alClient;
  {$ENDIF}
  {$IFDEF GENERALOSX}
  FListBox.OnKeyUp := ListBoxKeyUp;
  FListBox.OnKeyDown := ListBoxKeyDown;
  {$ENDIF}

  FGutter := TTMSFMXGutter.Create(Self);
  FGutter.OnChange := GutterChanged;

  FActiveLineSettings := TTMSFMXActiveLineSettings.Create(Self);
  FActiveLine := -1;
  FLineSpacing := 2;

  FAutoCompletion := TAutoCompletion.Create(Self);

  FUndoList := TAdvUndoList.Create;
  FUndoList.Memo := Self;

  FHTML := TStringList.Create;

  FShowRightMargin := true;
  FRightMargin := 80;
  FUndoLimit := 100;
  FUndoLineByLine := false;

  FCharCase := ecNormal;

  FAutoIndent := True;
  FAutoExpand := True;

  FHideSelection := False;
  FClipLine := -1;
  FVertScrollBar := nil;
  FHorzScrollBar := nil;

  FFont := TFont.Create;
  if (csDesigning in ComponentState) and not
    ((csReading in Owner.ComponentState) or (csLoading in Owner.ComponentState)) then
  begin
    {$IFDEF DELPHI_LLVM}
    FFont.Family := 'Courier';
    {$ELSE}
    FFont.Family := 'Courier New';
    {$ENDIF}
    FFont.Size := 12;
  end;

  FFont.OnChanged := OnFontChanged;
  FFontColor := TAlphaColorRec.Black;

  if TPlatformServices.Current.SupportsPlatformService(IFMXTextService, IInterface(PlatformTextService)) then
  begin
    FTextService := PlatformTextService.GetTextServiceClass.Create(Self, False);
    FTextService.ImeMode := TImeMode.imDontCare;
  end;

  Width := 400;
  Height := 300;
  FUrlDelimiters := #32#34#39#44;
  FUseStyler := True;
  FUrlAware := True;
  FTabSize := 4;
  FCurX := 0;
  FCurY := 0;
  FLeftCol := 0;
  FTopLine := 0;
  FBackupTopLine := -1;
  FLetRefresh := True;
  {$IFDEF DELPHIXE6_LVL}
  FKeyboardType := TVirtualKeyboardType.Default;
  {$ELSE}
  FKeyboardType := TVirtualKeyboardType.vktDefault;
  {$ENDIF}
  FLines := TFMXMemoStrings.Create;
  FLines.OnChange := LinesChanged;
  FLines.Memo := Self;
  CanFocus := True;
  Cursor := crIBeam;
  FLeftButtonDown := False;
  FSetMaxLength := True;

//  FSelectionTextColor := TAlphaColorRec.White;
//  FSelectionBackGroundColor := TAlphaColorRec.Navy;
//  FBreakPointBackGroundColor := TAlphaColorRec.Red;
//  FBreakPointTextColor := TAlphaColorRec.White;

  FDelErase := True;
  FEnhancedHomeKey := False;

  FMemoContext := TPopupMenu.Create(Self);
  FMemoContext.PopupComponent := Self;
  mniRoot := TMenuItem.Create(FMemoContext);
  mniRoot.Text := 'ClipBoard';
  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Redo';
  mni.Tag := menuRedo;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);
  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Undo';
  mni.Tag := menuUndo;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);
  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Cut';
  mni.Tag := menuCut;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);
  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Copy';
  mni.Tag := menuCopy;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);
  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Paste';
  mni.Tag := menuPaste;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);

  FMemoContext.AddObject(mniroot);

  mniRoot := TMenuItem.Create(FMemoContext);
  mniRoot.Text := 'Select';

  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Select Word';
  mni.Tag := menuSelWord;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);

  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Select All';
  mni.Tag := menuSelAll;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);

  FMemoContext.AddObject(mniroot);

  mniRoot := TMenuItem.Create(FMemoContext);
  mniRoot.Text := 'Delete';

  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Delete Selection';
  mni.Tag := menuDelSel;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);

  mni := TMenuItem.Create(mniRoot);
  mni.Text := 'Delete Line';
  mni.Tag := menuDelLine;
  mni.OnClick := ContextMenuItemClick;
  mniRoot.AddObject(mni);

  FMemoContext.AddObject(mniroot);
end;

procedure TTMSFMXMemo.SetActiveLine(const Value: integer);
begin
  if (FActiveLine <> Value) then
  begin
    FActiveLine := Value;
    Repaint;
  end;
end;

procedure TTMSFMXMemo.SetActiveLineSettings(
  const Value: TTMSFMXActiveLineSettings);
begin
  FActiveLineSettings.Assign(Value);
end;

procedure TTMSFMXMemo.SetBookmark(Index: Integer;
  const Value: Boolean);
var
  Tlp: TlineProp;
begin
  if Index = -1 then
    Exit;
  if Index >= InternalLines.Count then
    raise Exception.Create('Invalid line index for bookmark');

  InternalLines.OnChange := nil;
  Tlp := InternalLines.GetLineProp(index);
  if Tlp = nil then
    tlp := InternalLines.CreateProp(index);
  tlp.Bookmark := Value;
  tlp.BookmarkIndex := -1;

  InternalLines.SetLineProp(index, tlp);
  InternalLines.OnChange := LinesChanged;
  UpdateMemo;
end;

procedure TTMSFMXMemo.SetBreakPoint(Index: Integer;
  const Value: Boolean);
var
  Tlp: TlineProp;
begin
  InternalLines.OnChange := nil;
  Tlp := InternalLines.GetLineProp(index);
  if Tlp = nil then
    tlp := InternalLines.CreateProp(index);
  tlp.BreakPoint := Value;
  InternalLines.SetLineProp(index, tlp);
  InternalLines.OnChange := LinesChanged;
  UpdateMemo;
end;

procedure TTMSFMXMemo.SetBreakLineColor(const Value: TAlphaColor);
var
  obj: TFMXObject;
begin
  FBreakPointBackGroundColor := Value;

  obj := FindStyleResource('breaklinecolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    (obj as TRectangle).Fill.Color := Value;
  end;
end;

procedure TTMSFMXMemo.SetBreakLineTextColor(const Value: TAlphaColor);
var
  obj: TFMXObject;
begin
  FBreakPointTextColor := Value;

  obj := FindStyleResource('breaklinetextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    (obj as TText).Color := Value;
  end;
end;

procedure TTMSFMXMemo.ClearBookmarks;
var
  i: Integer;
begin
  for i := 1 to 10 do
  begin
    Bookmark[FBookmarkList.Items[i - 1]] := False;
    FBookmarkList.Items[i - 1] := -1;
  end;
end;

procedure TTMSFMXMemo.GotoBookmark(Index: Integer);
begin
  if (Index < 10) and (Index >= 0) then
  begin
    if FBookmarkList.Items[Index] <> -1 then
    begin
      TopLine := FBookmarkList.Items[Index];
      CurX := 0;
      CurY := FBookmarkList.Items[Index];
    end;
  end
  else
    raise Exception.Create('Invalid bookmark index');
end;

function TTMSFMXMemo.HasBookmarks: Boolean;
var
  i: Integer;
begin
  Result := False;
  for i := 1 to 10 do
  begin
    if FBookmarkList.Items[i - 1] <> -1 then
    begin
      Result := True;
      Break;
    end;
  end;
end;

{$IFDEF ANDROID}
function TTMSFMXMemo.IsReadOnly: Boolean;
begin
  Result := False;
end;

function TTMSFMXMemo.HasText: Boolean;
begin
  Result := False;
end;
{$ENDIF}

procedure TTMSFMXMemo.ClearBreakpoints;
var
  i: Integer;
begin
  for i := 0 to InternalLines.FlinesProp.Count - 1 do
    TLineProp(InternalLines.FlinesProp[i]).BreakPoint := False;
  UpdateMemo;
end;

function TTMSFMXMemo.GetBookmarkInfo(Index: integer; var AValue: integer): boolean;
var
  Tlp: TlineProp;
begin
  Result := false;
  AValue := -1;
  Tlp := InternalLines.GetLineProp(Index);
  if Assigned(Tlp) and (tlp is TLineProp) then
  begin
    Result := tlp.Bookmark;
    if Result then
      AValue := tlp.BookmarkIndex;
  end;
end;

function TTMSFMXMemo.GetBookmarkIndex(Index: Integer): Integer;
var
  Tlp: TlineProp;
begin
  Result := -1;
  Tlp := InternalLines.GetLineProp(Index);
  if Assigned(Tlp) and (tlp is TLineProp) then
  begin
    if tlp.Bookmark then
      Result := tlp.BookmarkIndex;
  end;
end;

function TTMSFMXMemo.GetBookmarks(Index: Integer): Integer;
begin
  if (Index < 10) and (Index >= 0) then
  begin
    Result := FBookmarkList.Items[Index]
  end
  else
    raise Exception.Create('Invalid bookmark index');
end;

function TTMSFMXMemo.GetBreakPoint(Index: Integer): Boolean;
var
  Tlp: TlineProp;
begin
  Tlp := InternalLines.GetLineProp(Index);
  if Assigned(Tlp) and (tlp is TLineProp) then
    Result := tlp.BreakPoint
  else
    Result := False;
end;

function TTMSFMXMemo.GetBreakPointBitmap: TBitmap;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('breakpointbitmap');
  if Assigned(obj) and (obj is TImage) then
    Result := (obj as TImage).Bitmap;
end;

procedure TTMSFMXMemo.SetBookmarkIndex(Index: Integer; const Value: Integer);
var
  Tlp: TlineProp;
begin
  if Index >= InternalLines.Count then
    raise Exception.Create('Invalid line index for bookmark');

  InternalLines.OnChange := nil;
  Tlp := InternalLines.GetLineProp(index);
  if Tlp = nil then
    tlp := InternalLines.CreateProp(index);
  tlp.Bookmark := Value <> -1;
  tlp.BookmarkIndex := Value;
  InternalLines.SetLineProp(index, tlp);
  InternalLines.OnChange := LinesChanged;
  UpdateMemo;
end;

procedure TTMSFMXMemo.SetBookmarks(Index: Integer;
  const Value: Integer);
begin
  if (Index < 10) and (Index >= 0) then
  begin
    if FBookmarkList.Items[Index] <> -1 then
      Bookmark[FBookmarkList.Items[Index]] := False;
    FBookmarkList.Items[Index] := Value;
    if Value <> -1 then
      Bookmark[FBookmarkList.Items[Index]] := True;
  end
  else
    raise Exception.Create('Invalid bookmark index');
end;

{$IFDEF ANDROID}
function TTMSFMXMemo.GetSelection: string;
begin

end;

function TTMSFMXMemo.GetSelectionBounds: TRect;
begin

end;
{$ENDIF}

function TTMSFMXMemo.GetSelectionColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('selectioncolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    Result := (obj as TRectangle).Fill.Color;
  end;
end;

{$IFDEF ANDROID}
function TTMSFMXMemo.GetSelectionRect: TRectF;
begin

end;
{$ENDIF}

function TTMSFMXMemo.GetSelectionTextColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('selectiontextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    Result := (obj as TText).Color;
  end;
end;

function TTMSFMXMemo.GetBreakLineColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('breaklinecolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    Result := (obj as TRectangle).Fill.Color;
  end;
end;

function TTMSFMXMemo.GetBreakLineTextColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('breaklinetextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    Result := (obj as TText).Color;
  end;
end;

function TTMSFMXMemo.GetBandColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('bandcolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    Result := (obj as TRectangle).Fill.Color;
  end;
end;

function TTMSFMXMemo.GetBkgColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('memo');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    Result := (obj as TRectangle).Fill.Color;
  end;
end;

function TTMSFMXMemo.GetBookmark(Index: Integer): Boolean;
var
  Tlp: TlineProp;
begin
  Tlp := InternalLines.GetLineProp(Index);
  if Assigned(Tlp) and (tlp is TLineProp) then
    Result := tlp.Bookmark
  else
    Result := False;
end;

function TTMSFMXMemo.GetBookMarkBitmap: TBitmap;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('bookmarkbitmap');
  if Assigned(obj) and (obj is TImage) then
    Result := (obj as TImage).Bitmap;
end;

function TTMSFMXMemo.GetBookmarkCount: integer;
var
  i: integer;
  Tlp: TlineProp;
begin
  Result := 0;

  for i := 0 to InternalLines.Count - 1 do
  begin
    Tlp := InternalLines.GetLineProp(i);
    if Assigned(Tlp) and (tlp is TLineProp) then
    begin
      if tlp.Bookmark then
        inc(Result);
    end;
  end;
end;

function TTMSFMXMemo.CreateListBox: TMemoComboListBox;
begin
  Result := TMemoComboListBox.Create(Self);
end;

function TTMSFMXMemo.CreateCaret: TCaret;
begin
  Result := TCaret.Create(Self);
  Result.Visible := True;
end;

destructor TTMSFMXMemo.Destroy;
begin
  FCaret.Free;
  FHTML.Free;
  FActiveLineSettings.Free;
  FGutter.Free;
  FMemoContext.Free;
  FBookmarkList.Free;
  FAutoCompleteList.Free;
  FAutoCompletion.Free;
  FLines.Free;
  FTextService.Free;
  FUndoList.Clear;
  FUndoList.Capacity := 0;
  FUndoList.Free;
  FFont.Free;
  inherited;
end;

procedure TTMSFMXMemo.DialogKey(var Key: Word; Shift: TShiftState);
var
  sy: integer;
begin
  if (key = vkTab) and FWantTab and IsFocused then
  begin
    sy := FSelEndY;
    if FSelEndX = 0 then
      dec(sy);

    if FSelStartY < sy then
    begin
      if ssShift in Shift then
        BlockIndent(FSelStarty, sy,-TabSize)
      else
        BlockIndent(FSelStarty, sy,TabSize);
    end
    else
      DoCommand(Key, Shift);

    key := 0;
  end;
  inherited;
end;

procedure TTMSFMXMemo.ApplyAdditionalStyle(AControl: TFMXObject);
var
  obj: TFMXObject;
begin
  inherited;

  if not Assigned(AControl) then
    Exit;

  // set default active line background color
  obj := AControl.FindStyleResource('activelinetextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    (obj as TText).Color := TAlphaColorRec.Yellow;
  end;
  obj := AControl.FindStyleResource('selectiontextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    (obj as TText).Color := TAlphaColorRec.White;
  end;
  obj := AControl.FindStyleResource('breaklinetextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    (obj as TText).Color := TAlphaColorRec.White;
  end;
end;

procedure TTMSFMXMemo.ApplyStyle;
var
  tt: TFMXObject;
  mnu: TMenuItem;
begin
  inherited;

  tt := FindStyleResource('gutter');
  if Assigned(tt) and (tt is TRectangle) then
  begin
    (tt as TRectangle).OnPaint := PaintGutter;
    (tt as TRectangle).OnMouseDown := DoGutterMouseDown;
    (tt as TRectangle).OnDblClick := DoGutterDblClick;
  end;

  tt := FindStyleResource('memo');
  if Assigned(tt) and (tt is TRectangle) then
  begin
    (tt as TRectangle).OnPaint := PaintMemo;
    (tt as TRectangle).SendToBack;
  end;

  tt := FindStyleResource('hscroll');
  if Assigned(tt) and (tt is TScrollBar) then
  begin
    (tt as TScrollBar).OnChange := DoHScroll;
    FHorzScrollBar := TScrollBar(tt);
  end;

  tt := FindStyleResource('vscroll');
  if Assigned(tt) and (tt is TScrollBar) then
  begin
    (tt as TScrollBar).OnChange := DoVScroll;

    (tt as TScrollBar).Min := 0;
    (tt as TScrollBar).Max := FLines.Count - 1;
    FVertScrollBar := TScrollBar(tt);
  end;

  tt := FindStyleResource('cutbitmap');
  if Assigned(tt) and (tt is TImage) then
  begin
    mnu := GetContextMenuItem(menuCut);
    if Assigned(mnu) then
      mnu.Bitmap.Assign((tt as TImage).Bitmap);
  end;

  tt := FindStyleResource('copybitmap');
  if Assigned(tt) and (tt is TImage) then
  begin
    mnu := GetContextMenuItem(menuCopy);
    if Assigned(mnu) then
      mnu.Bitmap.Assign((tt as TImage).Bitmap);
  end;

  tt := FindStyleResource('pastebitmap');
  if Assigned(tt) and (tt is TImage) then
  begin
    mnu := GetContextMenuItem(menuPaste);
    if Assigned(mnu) then
      mnu.Bitmap.Assign((tt as TImage).Bitmap);
  end;

  tt := FindStyleResource('undobitmap');
  if Assigned(tt) and (tt is TImage) then
  begin
    mnu := GetContextMenuItem(menuUndo);
    if Assigned(mnu) then
      mnu.Bitmap.Assign((tt as TImage).Bitmap);
  end;

  tt := FindStyleResource('redobitmap');
  if Assigned(tt) and (tt is TImage) then
  begin
    mnu := GetContextMenuItem(menuRedo);
    if Assigned(mnu) then
      mnu.Bitmap.Assign((tt as TImage).Bitmap);
  end;

  CalculateCellSize;
  InitScrollbars;

  if Assigned(Caret) then
  begin
    Caret.Color := TAlphaColorRec.Black;
    Caret.Size := PointF(1, FCellSize.H);
    CaretX := GetGutterWidth;
  end;
end;

procedure TTMSFMXMemo.DoHScroll(Sender: TObject);
begin
  FLeftCol := Round(GetHScroll.Value);
  UpdateMemo;
end;

procedure TTMSFMXMemo.DoInsertAutoCompletionEntry(var AEntry: string);
begin
  if Assigned(FOnInsertAutoCompletionEntry) then
    FOnInsertAutoCompletionEntry(Self, AEntry);
end;

procedure TTMSFMXMemo.DoOverwriteToggle(var Allow: boolean);
begin
  if Assigned(OnOverwriteToggle) then
    OnOverWriteToggle(Self,Overwrite,Allow);
end;

procedure TTMSFMXMemo.DoVScroll(Sender: TObject);
var
  Old, vlc: Integer;
begin
  Old := FTopLine;
  FTopLine := Round(GetVScroll.Value);
  if Old <> FTopLine then
  begin
    BlockUpdate;
    if Old < FTopLine then
      ClearLineStylesFromTo(Old, FTopLine)
    else
      ClearLineStylesFromTo(FTopLine, Old);

    vlc := Round(EditorRect.Height / FCellSize.H);

    if (CurX >= FLeftCol) and (FTopLine <= CurY) and (CurY < FTopLine + vlc) then
      ShowCaret(True)
    else
      ShowCaret(false);

    EndBlockUpdate;
  end;
end;

procedure TTMSFMXMemo.ExtractURL(s: string; var urls: TStringList);
var
  s1: string;
  txt, i: integer;
  lit: char;
begin
  if not Assigned(urls) then
    Exit;

  urls.Clear;

  if not FUrlAware then
  begin
    urls.Add(s);
    Exit;
  end;

  s1 := '';
  if (Assigned(InternalStyles)) and UseStyler then
    s1 := InternalStyles.Literal;

  if length(s1) > 0 then
  begin
    {$IFDEF DELPHI_LLVM}
    lit := s1[0]
    {$ELSE}
    lit := s1[1]
    {$ENDIF}
  end
  else
    lit := #0;

  txt := -1;
  s1 := '';
  {$IFDEF DELPHI_LLVM}
  i := 0;
  while i < length(s) do
  {$ELSE}
  i := 1;
  while i <= length(s) do
  {$ENDIF}
  begin
    if s[i] = lit then
    begin
      s1 := s1 + s[i];
      inc(i);
      if i <= length(s) then
      begin
        s1 := s1 + s[i];
        inc(i);
      end;
      Continue;
    end;

    if AnsiPos(s[i], Furldelimiters) > 0 then
    begin
      if txt = 0 then
      begin
        urls.Add(s1);
        s1 := '';
      end;
      txt := 1;
      s1 := s1 + s[i];
    end
    else
    begin
      if txt = 1 then
      begin
        urls.Add(s1);
        s1 := '';
      end;
      txt := 0;
      s1 := s1 + s[i];
    end;
    inc(i);
  end;
  if s1 <> '' then urls.Add(s1);
end;

function TTMSFMXMemo.FullWordAtCursor: String;
var
  s: string;
  i: integer;
  fb, fe: integer;
begin
  Result := '';

  if InternalLines.Count = 0 then
    Exit
  else
    if InternalLines.Count <= CurY then
      CurY := InternalLines.Count - 1;

  s := InternalLines.Strings[CurY];

  if (CurX > Length(s)) then
    Exit;

  fe := CurX;
  fb := CurX + 1;

  {$IFDEF DELPHI_LLVM}
  for i := CurX to Length(s) - 1 do
  {$ELSE}
  for i := CurX + 1 to Length(s) do
  {$ENDIF}
  begin
    if not (CheckSeparator(s[i])) then
      fe := i
    else
      Break;
  end;

  {$IFDEF DELPHI_LLVM}
  for i := CurX - 1 downto 0 do
  {$ELSE}
  for i := CurX downto 1 do
  {$ENDIF}
  begin
    if not (CheckSeparator(s[i])) then
      fb := i
    else
      Break;
  end;

  Result := Copy(s, fb, fe - fb + 1);
end;

//------------------------------------------------------------------------------

procedure TTMSFMXMemo.DeleteLine;
var
  OldX, OldY: integer;
  s: string;
begin
  BlockUpdate;
  OldX := CurX;
  OldY := CurY;

  s := InternalLines[CurY];

  CurX := 0;

  if InternalLines.Count = 1 then
    TFMXMemoStrings(InternalLines)[0] := ''
  else if CurY = InternalLines.Count - 1 then
  begin
    if IsUpdating then
      FCurY := FCurY - 1
    else
      CurY := CurY - 1;
    InternalLines.Delete(CurY + 1);
  end
  else
    InternalLines.Delete(CurY);

  InternalUndoList.Add(TDeleteLineUndo.Create(OldX, OldY, CurX, CurY, S));

  EndBlockUpdate;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXMemo.Clear;
begin
  BlockUpdate;
  CurX := 0;
  CurY := 0;
  FLeftCol := 0;
  FTopLine := 0;
  Fletrefresh := False;
  InternalLines.Clear;
  CurY := 0;
  CurX := 0;
  FSelStartX := 0;
  FSelEndX := 0;
  FSelStartY := 0;
  FSelEndY := 0;
  Fletrefresh := True;
  ClearLineStyles;
  InternalLines.Clear;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.ClearLineStyles;
var
  i: Integer;
begin
  for i := 0 to InternalLines.FLinesProp.Count - 1 do
  begin
    TLineProp(InternalLines.FlinesProp[i]).CachedStyle := False;
  end;
end;

procedure TTMSFMXMemo.ClearLineStylesFromTo(FromLine, ToLine: integer);
var
  Tlp: TlineProp;
  i: integer;
begin
  for I := FromLine to ToLine do
  begin
    Tlp := InternalLines.GetLineProp(I);
    if Tlp <> nil then
      Tlp.CachedStyle := false;
  end;
end;

procedure TTMSFMXMemo.ClearSelection;
var
  Changed: boolean;
begin
  Changed := not ((FSelStartX = FSelEndX) and (FSelStartY = FSelEndY));

  if Changed then
    BlockUpdate;

  FSelStartX := CurX;
  FSelStartY := CurY;
  FSelEndX := CurX;
  FSelEndY := CurY;
  FPrevSelX := CurX;
  FPrevSelY := CurY;
  if Changed then
  begin
    SelectionChanged;
    EndBlockUpdate;
  end;
end;

function TTMSFMXMemo.GetHorzScrollHeight: Integer;
var
  hs: TScrollBar;
begin
  Result := 0;
  hs := GetHScroll;
  if Assigned(hs) then
    Result := Round(hs.Height);
end;

function TTMSFMXMemo.GetHScroll: TScrollBar;
var
  tt: TFMXObject;
begin
  Result := nil;
  tt := FindStyleResource('hscroll');
  if Assigned(tt) and (tt is TScrollBar) then
  begin
    Result := tt as TScrollBar;
  end;
end;

function TTMSFMXMemo.GetGutter: TRectangle;
var
  tt: TFMXObject;
begin
  Result := nil;
  tt := FindStyleResource('gutter');
  if Assigned(tt) and (tt is TRectangle) then
  begin
    Result := tt as TRectangle;
  end;
end;

function TTMSFMXMemo.GetGutterWidth: Integer;
var
  g: TRectangle;
begin
  Result := 0;
  g := GetGutter;
  if Assigned(g) then
  begin
    if g.Visible then
      Result := Round(g.Width)
    else
      Result := 0;
  end;
end;

function TTMSFMXMemo.GetMemoRect: TRectangle;
var
  tt: TFMXObject;
begin
  Result := nil;
  tt := FindStyleResource('memo');
  if Assigned(tt) and (tt is TRectangle) then
  begin
    Result := tt as TRectangle;
  end;
end;

{$IFDEF DELPHIXE5_LVL}
function TTMSFMXMemo.GetReturnKeyType: TReturnKeyType;
begin
  {$IFDEF DELPHIXE6_LVL}
  Result := TReturnKeyType.Default;
  {$ELSE}
  Result := TReturnKeyType.rktDefault;
  {$ENDIF}
end;
{$ENDIF}

function TTMSFMXMemo.GetInternalStyles: TTMSFMXMemoCustomStyler;
begin
  Result := FSyntaxStyles;
end;

procedure TTMSFMXMemo.SetLineSpacing(const Value: integer);
begin
  FLineSpacing := Value;
  CalculateCellSize;
  Repaint;
end;

procedure TTMSFMXMemo.SetLineStyle(Index: Integer; const LineStyle: TStyle);
var
  Tlp: TlineProp;
begin
  if Index >= InternalLines.Count then
    Exit;
  Tlp := InternalLines.GetLineProp(index);
  if Tlp = nil then
    tlp := InternalLines.CreateProp(index);
  tlp.Style := LineStyle;
  tlp.CachedStyle := True;
  InternalLines.SetLineProp(index, tlp);
end;

procedure TTMSFMXMemo.SetMaxLength;
var
  i, len, mx: integer;
begin
  mx := 0;

  if InternalLines.FListLengths.Count <> InternalLines.Count then
    Exit;

  for i := 0 to InternalLines.FListLengths.Count - 1 do
  begin
    len := InternalLines.FListLengths.Items[i];
    if len > mx then
    begin
      mx := len;
    end;
  end;

  FmaxLength := mx;
end;

{$IFDEF DELPHIXE5_LVL}
procedure TTMSFMXMemo.SetReturnKeyType(Value: TReturnKeyType);
begin

end;
{$ENDIF}

procedure TTMSFMXMemo.SetRightMargin(const Value: integer);
begin
  if (FRightMargin <> Value) then
  begin
    FRightMargin := Value;
    Repaint;
  end;
end;

function TTMSFMXMemo.GetLineStyle(Index: Integer;
  var LineStyle: TStyle): Boolean;
var
  Tlp: TlineProp;
begin
  Tlp := InternalLines.GetLineProp(index);
  if (Tlp <> nil) and (tlp is TLineProp) then
  begin
    Result := tlp.CachedStyle;
    if tlp.CachedStyle then
      LineStyle := tlp.Style
  end
  else
    Result := False;
end;

function TTMSFMXMemo.GetUpStyle(stopat: Integer): TStyle;
var
  i, allb, ll, Iurl: integer;
  comment, bracket, linecomment: boolean;
  chBE, chBS: char;
  s: string;
  urls: TStringList;
  start: Integer;
  AStyle: TStyle;
  lit: char;
  bracketbefore,commentbefore: boolean;
  chBEBefore, chBSBefore: char;
  lc: char;
  lrf: Boolean;
  cl: integer;
begin
  Result.isComment := False;
  Result.isBracket := False;
  Result.isNumber := False;
  Result.isKeyWord := False;
  Result.isDelimiter := False;
  Result.isHighlight := False;
  Result.isURL := False;
  Result.index := -1;
  Result.EndBracket := #0;
  Result.StartBracket := #0;

  chBE := #0;
  chBS := #0;
  FTempdelimiters := '';

  if (not Assigned(InternalStyles)) or (not UseStyler) then
    Exit;

  s := InternalStyles.Literal;

  if length(s) > 0 then
  begin
    {$IFDEF DELPHI_LLVM}
    lit := s[0]
    {$ELSE}
    lit := s[1]
    {$ENDIF}
  end
  else
    lit := #0;

  comment := False;
  bracket := False;

  for allb := 0 to InternalStyles.FAllStyles.Count - 1 do
  begin
    if InternalStyles.FAllStyles.Items[allb].FStyleType <> stSymbol then
      Continue;

    FTempdelimiters := FTempdelimiters + InternalStyles.FAllStyles.Items[allb].FSymbols;
  end;

  start := 0;

  if InternalLines.Count = 0 then
    Exit;

  i := stopat - 1;

  while (i >= 0) do
  begin
    if GetLineStyle(i, AStyle) then
    begin
      comment := AStyle.isComment;
      bracket := AStyle.isBracket;
      chBE := AStyle.EndBracket;
      chBS := AStyle.StartBracket;
      start := i;
      Result := AStyle;
      Break;
    end
    else
      dec(i);
  end;

  urls := TStringList.Create;

  BeginUpdate;
  InternalLines.BeginUpdate;

  for i := start to stopat - 1 do
  begin
    if (i >= InternalLines.Count) then
      Break;

    s := InternalLines.Strings[i];

    if (s = '') then
      Continue;

    urls.Clear;

    bracketbefore := bracket;
    commentbefore := comment;
    chBEBefore := chBE;
    chBSBefore := chBS;

    ExtractURL(s, urls);
    linecomment := False;

    for Iurl := 0 to urls.Count - 1 do
    begin
      s := urls[iurl];

      if FUrlAware then
        Result.isURL := WordIsURL(s)
      else
        Result.isURL := False;

      if (not linecomment) and (not Result.isURL) and InternalStyles.HasMultiComment and (s <> '') then
      begin
        lc := #0;
        while (s <> '') do
        begin
          if (not comment) and (not bracket) then
          begin
            ll := InternalStyles.PosCommentLeft(s,cl);
            if ll = 1 then
            begin
              comment := True;
              Delete(s, 1, cl);
            end;
          end;

          if s = '' then
            Break;

          if (comment) and (not bracket) then
          begin
            ll := InternalStyles.PosCommentRight(s,cl);

            if ll = 1 then
            begin
              comment := False;
              Delete(s, 1, cl);
            end;
          end;

          if (s = '') then
            Break;

          if (not comment) and (not bracket) then
          begin
            ll := AnsiPos(InternalStyles.FLineComment, s);
            if ll = 1 then
            begin
              linecomment := True;
              Break;
            end;
          end;

          if not comment then
          begin
            for allb := 0 to InternalStyles.FAllStyles.Count - 1 do
              if (InternalStyles.FAllStyles.Items[allb].FStyleType = stBracket) and
                (InternalStyles.FAllStyles.Items[allb].BracketStart <> #0) and
                (InternalStyles.FAllStyles.Items[allb].BracketEnd <> #0) then
              begin
                if not bracket then
                begin
                  chBS := InternalStyles.FAllStyles.Items[allb].BracketStart;
                  chBE := InternalStyles.FAllStyles.Items[allb].BracketEnd;
                  Result.Index := allb;
                end;

                if (length(s) > 0) then
                begin
                  if bracket then
                  begin
                    {$IFDEF DELPHI_LLVM}
                    if (s[0] = lit) then
                    {$ELSE}
                    if (s[1] = lit) then
                    {$ENDIF}
                    begin
                      Delete(s, 1, 1);
                      if length(s) > 0 then
                        Delete(s, 1, 1);
                    end;
                  end;

                  {$IFDEF DELPHI_LLVM}
                  if (length(s) > 0) and ((s[0] = chBS) or (s[0] = chBE)) and not (lc = InternalStyles.EscapeChar) then
                  {$ELSE}
                  if (length(s) > 0) and ((s[1] = chBS) or (s[1] = chBE)) and not (lc = InternalStyles.EscapeChar) then
                  {$ENDIF}
                  begin
                    bracket := not bracket;
                    Break;
                  end;
                end;
              end;
          end;
          {$IFDEF DELPHI_LLVM}
          lc := s[0];
          {$ELSE}
          lc := s[1];
          {$ENDIF}
          Delete(s, 1, 1);
        end;
      end;
    end;

    if (i mod 20 = 0) then
    begin
      if not GetLineStyle(i, AStyle) then
      begin
        AStyle.isComment := commentbefore;
        AStyle.isBracket := bracketbefore;
        AStyle.EndBracket := chBEBefore;
        AStyle.StartBracket := chBSBefore;
        AStyle.isHighlight := False;
        AStyle.Index := Result.Index;
        SetLineStyle(i, AStyle);
      end;
    end;
  end;

  EndUpdate;
  InternalLines.EndUpdate;

  urls.Free;

  Result.isComment := comment;
  Result.isBracket := bracket;
  Result.isHighlight := False;
  Result.EndBracket := chBE;
  Result.StartBracket := chBS;
  lrf := FLetRefresh;
  FLetRefresh := False;
  SetLineStyle(stopat, Result);
  FLetRefresh := lrf;
end;

function TTMSFMXMemo.GetVersion: string;
var
  vn: Integer;
begin
  vn := GetVersionNr;
  Result := IntToStr(Hi(Hiword(vn)))+'.'+IntToStr(Lo(Hiword(vn)))+'.'+IntToStr(Hi(Loword(vn)))+'.'+IntToStr(Lo(Loword(vn)));
end;

function TTMSFMXMemo.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

function TTMSFMXMemo.GetVertScrollWidth: Integer;
var
  vs: TScrollBar;
begin
  Result := 0;
  vs := GetVScroll;
  if Assigned(vs) and vs.Visible then
    Result := Round(vs.Width);
end;

function TTMSFMXMemo.GetVScroll: TScrollBar;
var
  tt: TFMXObject;
begin
  Result := nil;
  tt := FindStyleResource('vscroll');
  if Assigned(tt) and (tt is TScrollBar) then
  begin
    Result := tt as TScrollBar;
  end;
end;

function TTMSFMXMemo.IsCompletionListActivationChar(Ch: Char): boolean;
var
  i: integer;
begin
  Result := false;
  {$IFDEF DELPHI_LLVM}
  for i := 0 to Length(AutoCompletion.StartToken) - 1 do
  {$ELSE}
  for i := 1 to Length(AutoCompletion.StartToken) do
  {$ENDIF}
  begin
    if (Ch = AutoCompletion.StartToken[i]) then
    begin
      Result := true;
      Break;
    end;
  end;
end;

function TTMSFMXMemo.IsDelimiter(value: Char): boolean;
var
  i: Integer;
begin
  result := False;

  if FCachedDelimiters <> '' then
    result := pos(value, FCachedDelimiters) <> 0
  else if (Assigned(InternalStyles)) and (UseStyler) then
  begin
    for i := 0 to InternalStyles.FAllStyles.Count - 1 do
    begin
      if InternalStyles.FAllStyles.Items[i].FStyleType = stSymbol then
      begin
        FCachedDelimiters := InternalStyles.FAllStyles.Items[i].Symbols;
        result := pos(value, InternalStyles.FAllStyles.Items[i].Symbols) <> 0;
        break;
      end;
    end;
  end
  else
    Result := value = ' ';
end;

{$IFDEF DELPHIXE6_LVL}
function TTMSFMXMemo.IsPassword: Boolean;
begin
  Result := False;
end;
{$ENDIF}

function TTMSFMXMemo.IsTokenBoundary(ch: char): boolean;
begin
  Result := (ch = #32) or (ch = '(') or (ch = ')') or (ch = '[') or (ch = ']') or (ch = ',') or (ch = '.')
    or (ch = ':') or (ch = ';') or (ch = '"') or (ch = '''') or (ch = '=');
end;

function TTMSFMXMemo.IsWordBoundary(ch: char): boolean;
begin
  Result := (ch = #32) or (ch = #39) or (ch = '"') or (ch = ',') or (ch = ';');
end;

procedure TTMSFMXMemo.InitScrollbars;
var
  s: TScrollbar;
  tw,i,ll: integer;
begin
  s := GetVScroll;
  if Assigned(s) then
  begin
    s.Min := 0;
    s.ViewportSize := (GetMemoRect.Height / FCellSize.H);
    s.Max := FLines.Count;
    s.Visible := s.ViewportSize < s.Max;
  end;

  if FSetMaxLength then
  begin
    tw := 0;
    for i := 0 to FLines.Count - 1 do
    begin
      ll := Length(FLines[i]);
      if ll > tw then
        tw := ll;
    end;
    FMaxLength := tw;
  end;

  s := GetHScroll;
  if Assigned(s) then
  begin
    s.Min := 0;
    s.ViewportSize := (GetMemoRect.Width / FCellSize.W);
    s.Max := FMaxLength;
    s.Visible := s.ViewportSize < s.Max;
  end;
end;

procedure TTMSFMXMemo.LinesChanged(Sender: TObject);
var
  v: TScrollBar;
begin
  if FLetRefresh then
  begin
    if Assigned(FVertScrollBar) then
      v := FVertScrollBar
    else
      v := GetVScroll;

    if Assigned(v) or Assigned(FHorzScrollBar) then
      InitScrollBars;
  end;
end;

procedure TTMSFMXMemo.ListBoxApplyStyleLookup(Sender: TObject);
begin
  if Assigned(OnAutoCompletionCustomizeList) then
    OnAutoCompletionCustomizeList(Self, FListBox);
end;

procedure TTMSFMXMemo.ListBoxItemApplyStyleLookup(Sender: TObject);
var
  lstit: TListBoxItem;
  ftlbl: TLabel;
  anim, animc: TFmxObject;
  I: Integer;
begin
  if Sender is TListBoxItem then
  begin
    ftlbl := GetAutoCompletionFont;
    if Assigned(ftlbl) then
    begin
      lstit := Sender as TListBoxItem;
      lstit.Font.Assign(ftlbl.Font);
      lstit.FontColor := ftlbl.FontColor;
      lstit.StyledSettings := [];
      if Assigned(OnAutoCompletionCustomizeItem) then
        OnAutoCompletionCustomizeItem(Self, lstit);
      anim := lstit.FindStyleResource('text');
      if Assigned(anim) and (anim is TText) then
      begin
        (anim as TText).Color := ftlbl.FontColor;
        for I := 0 to (anim as TText).ChildrenCount - 1 do
        begin
          animc := (anim as TText).Children[I];
          if Assigned(animc) and (animc is TColorAnimation) then
          begin
            (animc as TColorAnimation).StartValue := lstit.FontColor;
            (animc as TColorAnimation).StopValue := lstit.FontColor;
          end;
        end;
      end;
    end;
  end;
end;

procedure TTMSFMXMemo.ListBoxItemClick(Sender: TObject);
begin
  SelectPopupValue(False);
end;

procedure TTMSFMXMemo.ListBoxKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: System.WideChar; Shift: TShiftState);
begin
  KeyDown(Key, KeyChar, Shift);
end;

procedure TTMSFMXMemo.ListBoxKeyUp(Sender: TObject; var Key: Word;
  var KeyChar: System.WideChar; Shift: TShiftState);
begin
  if Key = vkReturn then
    SelectPopupValue(True);
end;

procedure TTMSFMXMemo.Loaded;
begin
  inherited;
end;

procedure TTMSFMXMemo.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FSyntaxStyles) then
    FSyntaxStyles := nil;
end;

procedure TTMSFMXMemo.PaintChildren;
begin
  inherited;
end;

function TTMSFMXMemo.FormatLineNumber(Line: integer): string;
var
  strCurLine: string;

begin
  strCurLine := IntToStr(Line);
  if FGutter.fShowLeadingZeros then
  begin
    while Length(strCurLine) < FGutter.FDigitCount do
      strCurLine := '0' + strCurLine;
  end;

  {
  Canvas.Font.Assign(Gutter.Font);
  cw := Canvas.TextWidth('W');


  Position := FGutter.GutterWidth - FGutter.GutterMargin;

  case Length(strCurLine) + 1 of
    2: Position := Position + 3 * cw;
    3: Position := Position + 2 * cw;
    4: Position := Position + cw;
  end;
  }
  Result := strCurLine;
end;

procedure TTMSFMXMemo.PaintGutter(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
var
  i,lnum,lat: integer;
  th,nl: double;
  r: TRectF;
  idx: Integer;
  bookmarkbmp, breakpointbmp, activelinebmp: TBitmap;
  rbmp, rbr: TRectF;
  tmpfmt: TFont;
  bmp: Boolean;
  lnumstr: string;

begin
  tmpfmt := TFont.Create;
  Canvas.SetMatrix(AbsoluteMatrix);
  Canvas.Font.Assign(FGutter.Font);

  th := FCellSize.H;

  nl := ARect.Height / th;

  Canvas.Fill.Color := TAlphaColorRec.Black;
  Canvas.Stroke.Color := TAlphaColorRec.Black;
  Canvas.StrokeThickness := 1;
  {$IFDEF DELPHIXE6_LVL}
  Canvas.Stroke.Kind := TBrushKind.Solid;
  {$ELSE}
  Canvas.Stroke.Kind := TBrushKind.bkSolid;
  {$ENDIF}

  bookmarkbmp := GetBookMarkBitmap;
  breakpointbmp := GetBreakPointBitmap;
  activelinebmp := GetActiveLineBitmap;

  for i := 0 to round(nl) - 1 do
  begin
    r := RectF(ARect.Left + 2, ARect.Top + i * th, ARect.Right, ARect.Top + (i + 1) *  th);

    lnum := FTopLine + i + FGutter.LineNumberStart;

    bmp := False;

    if FActiveLineSettings.ShowActiveLineIndicator then
    begin
      if (FActiveLineSettings.ActiveLineAtCursor and (lnum - 1 = CurY)) or
         (not FActiveLineSettings.ActiveLineAtCursor and (lnum - 1= ActiveLine)) then
      begin
        if Assigned(activelinebmp) then
        begin
          rbmp := RectF(r.Right - bookmarkbmp.Width - 3, r.Top + (r.Height - bookmarkbmp.Height) / 2, r.Right - 3, r.Top + (r.Height - bookmarkbmp.Height) / 2 + bookmarkbmp.Height);
          Canvas.DrawBitmap(activelinebmp,RectF(0,0,activelinebmp.Width,activelinebmp.Height), rbmp, 1);
          bmp := true;
        end;
      end;
    end;

    if FGutter.ShowLineNumbers then
    begin
      lnumstr := FormatLineNumber(lnum) + FGutter.NumberSuffix;

      lat := FGutter.LineNumberAt;
      if lat <= 1 then
        lat := 1;

      if (lnum mod lat = 0) or (lnum = CurY) then
        {$IFDEF DELPHIXE6_LVL}
        Canvas.FillText(r, lnumstr, false, 255, [], TTextAlign.Leading, TTextAlign.Center)
        {$ELSE}
        Canvas.FillText(r, lnumstr, false, 255, [], TTextAlign.taLeading, TTextAlign.taCenter)
        {$ENDIF}
      else
      begin
        Canvas.StrokeThickness := 0.5;
        Canvas.DrawLine(PointF(r.Left + 2,r.Top + th/2),PointF(r.Left + 5, r.Top+ th/2),255);
        bmp := true;
      end;

    end;

    if Assigned(bookmarkbmp) then
    begin
      if GetBookmarkInfo(FTopLine + i,idx) then
      begin
        bmp := True;
        rbmp := RectF(r.Right - bookmarkbmp.Width - 3, r.Top + (r.Height - bookmarkbmp.Height) / 2, r.Right - 3, r.Top + (r.Height - bookmarkbmp.Height) / 2 + bookmarkbmp.Height);
        Canvas.DrawBitmap(bookmarkbmp, RectF(0, 0, bookmarkbmp.Width, bookmarkbmp.Height), rbmp, 1);

        if idx <> -1 then
        begin
          tmpfmt.Assign(Canvas.Font);
          Canvas.Font.Family := 'Terminal';
          Canvas.Font.Size := 8;
          Canvas.Fill.Color := TAlphaColorRec.White;
          {$IFDEF DELPHIXE6_LVL}
          Canvas.FillText(rbmp, inttostr(idx), False, 1, [], TTextAlign.Center, TTextAlign.Center);
          {$ELSE}
          Canvas.FillText(rbmp, inttostr(idx), False, 1, [], TTextAlign.taCenter, TTextAlign.taCenter);
          {$ENDIF}
          Canvas.Font.Assign(tmpfmt);
          Canvas.Fill.Color := TAlphaColorRec.Black;
        end;
      end;
    end;

    if Assigned(breakpointbmp) then
    begin
      if BreakPoint[FTopLine + i] then
      begin
        if bmp and Assigned(bookmarkbmp) then
          rbr := RectF(r.Right - breakpointbmp.Width - bookmarkbmp.Width - 6, r.Top + (r.Height - breakpointbmp.Height) / 2, r.Right - bookmarkbmp.Width - 6, r.Top + (r.Height - breakpointbmp.Height) / 2 + breakpointbmp.Height)
        else
          rbr := RectF(r.Right - breakpointbmp.Width - 3, r.Top + (r.Height - breakpointbmp.Height) / 2, r.Right - 3, r.Top + (r.Height - breakpointbmp.Height) / 2 + breakpointbmp.Height);

        Canvas.DrawBitmap(breakpointbmp, RectF(0, 0, breakpointbmp.Width, breakpointbmp.Height), rbr, 1);
      end;
    end;
  end;

  tmpfmt.Free;
end;

procedure Swap(var I1, I2: integer);
var
  temp: integer;
begin
  temp := I1;
  I1 := I2;
  I2 := temp;
end;

procedure OrderPos(var StartX, StartY, EndX, EndY: integer);
begin
  if (EndY < StartY) or
    ((EndY = StartY) and (EndX < StartX)) then
  begin
    Swap(StartX, EndX);
    Swap(StartY, EndY);
  end;
end;

function Egalstyle(stl1, stl2: TStyle): Boolean;
begin
  Result :=
    (stl1.isComment = stl2.isComment) and
    (stl1.isBracket = stl2.isBracket) and
    (stl1.isnumber = stl2.isnumber) and
    (stl1.iskeyWord = stl2.iskeyWord) and
    (stl1.isdelimiter = stl2.isdelimiter) and
    (stl1.isHighlight = stl2.isHighlight) and
    (stl1.isURL = stl2.isURL) and
    (stl1.EndBracket = stl2.EndBracket) and
    (stl1.StartBracket = stl2.StartBracket) and
    (stl1.index = stl2.index);
end;

procedure ResetPartStyle(var buf: TStyle);
begin
  buf.isnumber := False;
  buf.iskeyWord := False;
  buf.isdelimiter := False;
  buf.isURL := False;
end;

procedure TTMSFMXMemo.PaintHTML(Part: string; var Drawstyle: Tstyle; lineno: integer);
var
  bc, c, bkcolor: TAlphaColor;
  sl: TFontstyles;
  localhtmlfont, shtml: string;

  procedure loadfromitemstyle;
  begin
    with Canvas do
    begin
      try
        C := InternalStyles.FAllStyles.Items[DrawStyle.index].FontColor;
        Sl := InternalStyles.FAllStyles.Items[DrawStyle.index].Font.Style;
        BC := InternalStyles.FAllStyles.Items[DrawStyle.index].FBGColor;
      except
        on Exception do
        begin
          C := Self.FontColor;
          Sl := FFont.Style;
          BC := GetBkgColor;
        end;
      end;
    end;
  end;

  function tagstrl(stc: Tfontstyles): string;
  var
    rz: string;
  begin
    rz := '';
    if TFontStyle.fsbold in stc then
      rz := rz + 'bold';

    if TFontStyle.fsItalic in stc then
    begin
      if rz = '' then
        rz := rz + 'italic'
      else
        rz := rz + ',italic'
    end;
    if rz <> '' then
      rz := 'font-style:' + rz + ';';

    if TFontStyle.fsUnderline in stc then
      rz := rz + 'text-decoration:'#39 + 'underline' + #39';';

    Result := rz;
  end;

begin
  if FHtml.Count <= 0 then
    Exit;

  bkcolor := GetBkgColor;

  C := FFontColor;
  Sl := FFont.Style;
  BC := bkcolor;

  begin
    if (DrawStyle.isComment) and (not DrawStyle.isURL) then
    begin
      C := InternalStyles.CommentStyle.FTextColor;
      SL := InternalStyles.CommentStyle.FStyle;
      if InternalStyles.CommentStyle.FBkColor <> TAlphaColorRec.Null then
        BC := InternalStyles.CommentStyle.FBkColor
      else
        BC := bkcolor;
    end
    else
    begin
      if (DrawStyle.isBracket) and (not DrawStyle.isURL) then
        loadfromitemstyle
      else
      begin
        if DrawStyle.isnumber then
        begin
          C := InternalStyles.FNumberStyle.FTextColor;
          SL := InternalStyles.FNumberStyle.Style;
          if InternalStyles.FNumberStyle.FBkColor <> TAlphaColorRec.Null then
            BC := InternalStyles.FNumberStyle.FBkColor
          else
            BC := bkcolor;
        end;
        if DrawStyle.isdelimiter then loadfromitemstyle;
        if DrawStyle.iskeyWord then loadfromitemstyle;

        if DrawStyle.isURL then
        begin
          C := InternalStyles.URLStyle.FTextColor;
          SL := InternalStyles.URLStyle.Style;
          if InternalStyles.URLStyle.FBkColor <> TAlphaColorRec.Null then
            BC := InternalStyles.URLStyle.FBkColor
          else
            BC := bkcolor;
        end;

      end;
    end;
  end;

  if part <> '' then
  begin
    // Needed only for HTTP comment rest of symbols solved by tag <PRE>
    part := StringReplace(part, '<', '&lt', [rfReplaceAll]);
    part := StringReplace(part, '>', '&gt', [rfReplaceAll]);

    if Drawstyle.isURL then
      part := '<a href="' + part + '">' + part + '</a>';
    if (bc = TAlphaColorRec.null) and (c = TAlphaColorRec.Null) then
      localhtmlfont := '<FONT style="' + tagstrl(sl) + '">'
    else if bc = TAlphaColorRec.null then
      localhtmlfont := '<FONT style="color:' + HTMLClr(C) + ';' + tagstrl(sl) + '">'
    else if c = TAlphaColorRec.null then
      localhtmlfont := '<FONT style="background-color:' + HTMLClr(C) + ';' + tagstrl(sl) + '">'
    else
      localhtmlfont := '<FONT style="background-color: ' + HTMLClr(bc) + ';color:' + HTMLClr(C) + ';' + tagstrl(sl) + '">';

    if localhtmlfont = FHtmlfont then
      shtml := FHtml.Strings[FHtml.Count - 1] + part
    else
    begin
      if FHtmlfont <> '' then
        shtml := FHtml.Strings[FHtml.Count - 1] + '</FONT>' + localhtmlfont + part
      else
        shtml := FHtml.Strings[FHtml.Count - 1] + localhtmlfont + part;
    end;
    Fhtmlfont := localhtmlfont;
    FHtml.Strings[FHtml.Count - 1] := shtml;
  end;

end;

procedure TTMSFMXMemo.PaintRTF(Part: string; var Drawstyle: Tstyle; lineno: integer);
var
  bc, c: TAlphaColor;
  sl: TFontStyles;

  procedure LoadFromItemStyle;
  begin
    with Canvas do
    begin
      try
        C := InternalStyles.FAllStyles.Items[DrawStyle.index].FontColor;
        Sl := InternalStyles.FAllStyles.Items[DrawStyle.index].Font.Style;
        BC := InternalStyles.FAllStyles.Items[DrawStyle.index].FBGColor;
      except
        on Exception do
        begin
          C := FFontColor;
          Sl := FFont.Style;
          BC := GetBkgColor;
        end;
      end;
    end;
  end;

  procedure tagstrl(stc: TFontStyles);
  begin
    if (TFontStyle.fsbold in stc) then
    begin
      if not FRTFEngine.Bold then
        FRTFEngine.AddBold(True);
    end
    else if FRTFEngine.Bold then
      FRTFEngine.AddBold(False);

    if TFontStyle.fsItalic in stc then
    begin
      if not FRTFEngine.Italic then
        FRTFEngine.AddItalic(True);
    end
    else if FRTFEngine.Italic then
      FRTFEngine.AddItalic(False);

    //if rz <> '' then rz := 'font:' + rz + ';';
    if TFontStyle.fsUnderline in stc then
    begin
      //rz := rz + 'text-decoration:'#39 + 'underline' + #39';';
      if not FRTFEngine.UnderLine then
        FRTFEngine.AddUnderLine(True);
    end
    else if FRTFEngine.UnderLine then
      FRTFEngine.AddUnderLine(False);
  end;

begin
  if not Assigned(FRTFEngine) then
    Exit;

  C := FFontColor;
  Sl := FFont.Style;
  BC := GetBkgColor;

  begin
    if (DrawStyle.isComment) and (not DrawStyle.isURL) then
    begin
      C := InternalStyles.CommentStyle.FTextColor;
      SL := InternalStyles.CommentStyle.FStyle;
      if InternalStyles.CommentStyle.FBkColor <> TAlphaColorRec.Null then
        BC := InternalStyles.CommentStyle.FBkColor
      else
        BC := GetBkgColor;
    end
    else
    begin
      if (DrawStyle.isBracket) and (not DrawStyle.isURL) then
        loadfromitemstyle
      else
      begin
        if DrawStyle.isnumber then
        begin
          C := InternalStyles.FNumberStyle.FTextColor;
          SL := InternalStyles.FNumberStyle.Style;
          if InternalStyles.FNumberStyle.FBkColor <> TAlphaColorRec.Null then
            BC := InternalStyles.FNumberStyle.FBkColor
          else
            BC := GetBkgColor;
        end;
        if DrawStyle.isdelimiter then loadfromitemstyle;
        if DrawStyle.iskeyWord then loadfromitemstyle;

        if DrawStyle.isURL then
        begin
          C := InternalStyles.URLStyle.FTextColor;
          SL := InternalStyles.URLStyle.Style;
          if InternalStyles.URLStyle.FBkColor <> TAlphaColorRec.Null then
            BC := InternalStyles.URLStyle.FBkColor
          else
            BC := GetBkgColor;
        end;

      end;
    end;
  end;

  if part <> '' then
  begin
    // Needed only for HTTP comment rest of symbols solved by tag <PRE>
    //part := StringReplace(part, '<', '&lt', [rfReplaceAll]);
    //part := StringReplace(part, '>', '&gt', [rfReplaceAll]);

    (*
    if Drawstyle.isURL then
    begin
      //part := '<a href="' + part + '">' + part + '</a>';
      AFont := TFont.Create;
      AFont.Assign(Font);
      AFont.Style := UrlStyle.Style;
      OldHClr := FRTFEngine.HighLightColor;
      if (UrlStyle.FBkColor = clNone) then
      begin
        if (OldHClr <> clNone) then
          FRTFEngine.AddHighLightColor(clNone);
      end
      else
      begin
        FRTFEngine.AddHighLightColor(UrlStyle.FBkColor);
      end;
      AFont.Color := UrlStyle.FTextColor;
      FRTFEngine.AddHyperLink(part, part, AFont);
      if OldHClr <> clNone then
        FRTFEngine.AddHighLightColor(OldHClr);
      AFont.Free;
      Exit;
    end;
    *)

    //localhtmlfont := '<FONT style="background-color: ' + HTMLClr(bc) + ';color:' + HTMLClr(C) + ';' + tagstrl(sl) + '">';
    if bc <> TAlphaColorRec.null then
      FRTFEngine.AddHighLightColor(bc);

    if c <> TAlphaColorRec.null then
      FRTFEngine.AddForeColor(C);

    tagstrl(sl);
    FRTFEngine.AddText(part);

   { if localhtmlfont = htmlfont then
      shtml := html.Strings[html.Count - 1] + part
    else
    begin
      if htmlfont <> '' then
        shtml := html.Strings[html.Count - 1] + '</FONT>' + localhtmlfont + part
      else
        shtml := html.Strings[html.Count - 1] + localhtmlfont + part;
    end;
    htmlfont := localhtmlfont;
    html.Strings[html.Count - 1] := shtml;
    }
  end;

end;



procedure TTMSFMXMemo.PaintLine(ACanvas: TCanvas; LineNo: Integer; var style: TStyle; DM: TDrawMode; PR: TRectF; VisLineNo: Integer = -1);
const
  tab = #9;
var
  rct0, rct1, rct, lineRct: TRectF;
  LineSelStart, LineSelEnd, posln, i, hvp: integer;
  dx: single;
  S, S1, S2, S3, hs: string;
  xSelStartX, xSelStartY, xSelEndX, xSelEndY: integer;
  isinlinecomment: boolean;
  backupstyle: TStyle;
  backupstring: string;
  lit: char;
  linecolor, linetextcolor: TAlphaColor;
  urls: TStringList;

  function Equal(s1, s2: string): boolean;
  begin
    if FCaseSensitive then
      Result := s1 = s2
    else
      Result := AnsiLowerCase(s1) = AnsiLowerCase(s2);
  end;

  procedure FindLineSelection(Selpart: string);
  var
    len: integer;
  begin
    s1 := '';
    s2 := '';
    s3 := '';

    if HideSelection then
    begin
      if not IsFocused then
      begin
        s1 := Selpart;
        Exit;
      end;
    end;

    if (lineno < xSelStartY) or (lineno > xSelEndY) then
    begin
      s1 := Selpart;
      Exit;
    end;

    if (xSelStartY < LineNo) and (LineNo < xSelEndY) then
    begin
      s2 := Selpart;
      Exit;
    end;

    len := length(Selpart);
    LineSelStart := 0;
    LineSelEnd := 0;

    if (xSelStartY = xSelEndY) then
    begin
      if xSelStartX = xSelEndX then
      begin
        s1 := Selpart;
        Exit;
      end;
      if xSelStartX >= posln + len then
      begin
        s1 := Selpart;
        Exit;
      end;
      if xSelEndX <= posln then
      begin
        s3 := Selpart;
        Exit;
      end;
      LineSelStart := xSelStartX - posln;
      LineSelEnd := xSelEndX - posln;
    end
    else
    begin
      if (xSelStartY = LineNo) then
      begin
        LineSelStart := xSelStartX - posln;
        LineSelEnd := len;
      end;
      if (xSelEndY = LineNo) then
      begin
        LineSelEnd := xSelEndX - posln; ;
      end;
    end;

    if LineSelEnd > len then LineSelEnd := len;
    if LineSelEnd < 0 then LineSelEnd := 0;
    if LineSelStart < 0 then LineSelStart := 0;
    if LineSelStart > len then LineSelStart := len;

    S1 := Copy(Selpart, 1, LineSelStart);
    S2 := Copy(Selpart, LineSelStart + 1, LineSelEnd - LineSelStart);
    S3 := Copy(Selpart, LineSelEnd + 1, len - LineSelEnd);
  end;

  procedure DrawPart(Part: string; var Drawstyle: TStyle);
  var
    len: integer;
    BrushColor,FontColor: TAlphaColor;
    rct3: TRectF;

    function CutText(var str: string; numc: integer): string;
    var
      i: integer;
    begin
      for i := 1 to Min(numc,length(str)) do
      {$IFDEF DELPHI_LLVM}
        str[i - 1] := ' ';
      {$ELSE}
        str[i] := ' ';
      {$ENDIF}
    end;

    procedure LoadFromItemstyle;
    begin
      with ACanvas do
      begin
        try
          FontColor := FSyntaxStyles.FAllStyles.Items[DrawStyle.index].FontColor;
          Font.Style := FSyntaxStyles.FAllStyles.Items[DrawStyle.index].Font.Style;
          BrushColor := FSyntaxStyles.FAllStyles.Items[DrawStyle.index].FBGColor;
        except
          on Exception do
          begin
            FontColor := FFontColor;
            Font.Style := [];
            BrushColor := GetBkgColor;
          end;
        end;
      end;
    end;

  begin
    len := Length(Part);

    if len > 0 then
    begin
      if DM = dmHTML then
      begin
        PaintHTML(part, Drawstyle, lineno);
        inc(posln, length(Part));
        Exit;
      end;

      if DM = dmRTF then
      begin
        PaintRTF(part, Drawstyle, lineno);
        inc(posln, length(Part));
        Exit;
      end;

      with ACanvas do
      begin
        ACanvas.Stroke.Color := FFontColor;
        ACanvas.Fill.Color := ACanvas.Stroke.Color;
        Font.Style := [];

        FontColor := FFontColor;
        BrushColor := TAlphaColorRec.Null;

        begin
          if (DrawStyle.isHighlight) and Assigned(FSyntaxStyles) then
          begin
            FontColor := FSyntaxStyles.HighlightStyle.FTextColor;
            Font.Style := FSyntaxStyles.HighlightStyle.FStyle;
            BrushColor := FSyntaxStyles.HighlightStyle.FBkColor;
          end
          else

          if (DrawStyle.isComment) and (not DrawStyle.isURL) then
          begin
            FontColor := FSyntaxStyles.CommentStyle.FTextColor;
            Font.Style := FSyntaxStyles.CommentStyle.FStyle;
            BrushColor := FSyntaxStyles.CommentStyle.FBkColor;
          end
          else
          begin
            if (DrawStyle.isBracket) and (not DrawStyle.isURL) then
              LoadFromItemStyle
            else
            begin
              if DrawStyle.isNumber and Assigned(FSyntaxStyles) then
              begin
                FontColor := FSyntaxStyles.FNumberStyle.FTextColor;
                Font.Style := FSyntaxStyles.FNumberStyle.Style;
                BrushColor := FSyntaxStyles.FNumberStyle.FBkColor;
              end;

              if DrawStyle.isDelimiter or DrawStyle.iskeyWord then
                LoadFromItemStyle;

              if DrawStyle.isURL and Assigned(FSyntaxStyles) then
              begin
                FontColor := FSyntaxStyles.URLStyle.TextColor;
                BrushColor := FSyntaxStyles.URLStyle.BkColor;
                Font.Style := FSyntaxStyles.FURLStyle.Style;
              end;
            end;
          end;
        end;

        if (LineColor <> TAlphaColorRec.Null) then
          BrushColor := LineColor;

        if (LineTextColor <> TAlphaColorRec.Null) then
          FontColor := LineTextColor;

        if part <> '' then
        begin
          FindLineSelection(part);

          if s1 <> '' then
          begin
            if BreakPoint[LineNo] then
            begin
              ACanvas.Fill.Color := FBreakPointBackGroundColor;
              ACanvas.Stroke.Color := FBreakPointBackGroundColor;
              {$IFDEF DELPHIXE6_LVL}
              ACanvas.Stroke.Kind := TBrushKind.None;
              ACanvas.Fill.Kind := TBrushKind.Solid;
              {$ELSE}
              ACanvas.Stroke.Kind := TBrushKind.bkNone;
              ACanvas.Fill.Kind := TBrushKind.bkSolid;
              {$ENDIF}

              rct3 := rct1;
              if (rct3.Left < rct1.Right) and (rct3.Right > 0) then
                ACanvas.FillRect(rct3, 1, 1, AllCorners, AbsoluteOpacity);

              ACanvas.Fill.Color := BreakPointTextColor;
            end
            else
              ACanvas.Fill.Color := FontColor;

            ACanvas.Stroke.Color := ACanvas.Fill.Color;
            {$IFDEF DELPHIXE6_LVL}
            ACanvas.Stroke.Kind := TBrushKind.Solid;
            ACanvas.Fill.Kind := TBrushKind.Solid;
            {$ELSE}
            ACanvas.Stroke.Kind := TBrushKind.bkSolid;
            ACanvas.Fill.Kind := TBrushKind.bkSolid;
            {$ENDIF}

            if (rct1.Left < 0) then
              CutText(s1,round(-rct1.Left / fcellsize.W));

            dx := FCellSize.W * Length(s1);

            if (BrushColor <> TAlphaColorRec.Null) then
            begin
              ACanvas.Fill.Color := BrushColor;
              ACanvas.FillRect(Rectf(rct1.Left, rct1.Top, rct1.Left + dx, rct1.Bottom),1,1,AllCorners,AbsoluteOpacity);
              ACanvas.Fill.Color := FontColor;
            end;

            {$IFDEF DELPHIXE6_LVL}
            ACanvas.FillText(rct1, s1, false, AbsoluteOpacity, [], TTextAlign.Leading, TTextAlign.Center);
            {$ELSE}
            ACanvas.FillText(rct1, s1, false, AbsoluteOpacity, [], TTextAlign.taLeading, TTextAlign.taCenter);
            {$ENDIF}

            rct1.Left := rct1.Left + dx;
          end;

          if s2 <> '' then
          begin
            if BreakPoint[LineNo] then
            begin
              ACanvas.Fill.Color := FBreakPointBackGroundColor;
              ACanvas.Stroke.Color := FBreakPointBackGroundColor;
              {$IFDEF DELPHIXE6_LVL}
              ACanvas.Stroke.Kind := TBrushKind.None;
              ACanvas.Fill.Kind := TBrushKind.Solid;
              {$ELSE}
              ACanvas.Stroke.Kind := TBrushKind.bkNone;
              ACanvas.Fill.Kind := TBrushKind.bkSolid;
              {$ENDIF}

              rct3 := rct1;
              if (rct3.Left < rct1.Right) and (rct3.Right > 0) then
                ACanvas.FillRect(rct3, 1, 1, AllCorners, AbsoluteOpacity);
            end;

            ACanvas.Fill.Color := FSelectionBackGroundColor;
            ACanvas.Stroke.Color := FSelectionBackGroundColor;
            {$IFDEF DELPHIXE6_LVL}
            ACanvas.Stroke.Kind := TBrushKind.None;
            ACanvas.Fill.Kind := TBrushKind.Solid;
            {$ELSE}
            ACanvas.Stroke.Kind := TBrushKind.bkNone;
            ACanvas.Fill.Kind := TBrushKind.bkSolid;
            {$ENDIF}

            rct3 := RectF(rct1.Left-1, rct1.Top, rct1.Left + FCellSize.W * Length(s2) + 1, rct1.Top + FCellSize.H);
            if (rct3.Right > rct1.Right) then
              rct3.Right := rct1.Right;

            if (rct3.Left < 0) then
              rct3.Left := 0;
            if (rct3.Left < rct1.Right) and (rct3.Right > 0) then
              ACanvas.FillRect(rct3, 1, 1, AllCorners, AbsoluteOpacity);

            ACanvas.Fill.Color := FSelectionTextColor;
            ACanvas.Stroke.Color := FSelectionBackGroundColor;
            {$IFDEF DELPHIXE6_LVL}
            ACanvas.Stroke.Kind := TBrushKind.None;
            ACanvas.Fill.Kind := TBrushKind.Solid;
            {$ELSE}
            ACanvas.Stroke.Kind := TBrushKind.bkNone;
            ACanvas.Fill.Kind := TBrushKind.bkSolid;
            {$ENDIF}

            if rct1.Left < 0 then
              CutText(s2,round(-rct1.Left / fcellsize.W));

            dx := FCellSize.W * Length(s2);

            if (BrushColor <> TAlphaColorRec.Null) then
            begin
              ACanvas.Fill.Color := BrushColor;
              ACanvas.FillRect(Rectf(rct1.Left, rct1.Top, rct1.Left + dx, rct1.Bottom),1,1,AllCorners,AbsoluteOpacity);
              ACanvas.Fill.Color := FontColor;
            end;

            {$IFDEF DELPHIXE6_LVL}
            ACanvas.FillText(rct1, s2, false, AbsoluteOpacity, [], TTextAlign.Leading, TTextAlign.Center);
            {$ELSE}
            ACanvas.FillText(rct1, s2, false, AbsoluteOpacity, [], TTextAlign.taLeading, TTextAlign.taCenter);
            {$ENDIF}

            rct1.Left := rct1.Left + dx;
          end;

          if s3 <> '' then
          begin
            if BreakPoint[LineNo] then
            begin
              ACanvas.Fill.Color := FBreakPointBackGroundColor;
              ACanvas.Stroke.Color := FBreakPointBackGroundColor;
              {$IFDEF DELPHIXE6_LVL}
              ACanvas.Stroke.Kind := TBrushKind.None;
              ACanvas.Fill.Kind := TBrushKind.Solid;
              {$ELSE}
              ACanvas.Stroke.Kind := TBrushKind.bkNone;
              ACanvas.Fill.Kind := TBrushKind.bkSolid;
              {$ENDIF}

              rct3 := rct1;
              if (rct3.Left < rct1.Right) and (rct3.Right > 0) then
                ACanvas.FillRect(rct3, 1, 1, AllCorners, AbsoluteOpacity);

              ACanvas.Fill.Color := BreakPointTextColor;
            end
            else
              ACanvas.Fill.Color := FontColor;

            ACanvas.Stroke.Color := ACanvas.Fill.Color;
            {$IFDEF DELPHIXE6_LVL}
            ACanvas.Stroke.Kind := TBrushKind.Solid;
            ACanvas.Fill.Kind := TBrushKind.Solid;
            {$ELSE}
            ACanvas.Stroke.Kind := TBrushKind.bkSolid;
            ACanvas.Fill.Kind := TBrushKind.bkSolid;
            {$ENDIF}

            if rct1.Left < 0 then
              CutText(s3,round(-rct1.Left / fcellsize.W));

            dx := FCellSize.W * Length(s3);

            if (BrushColor <> TAlphaColorRec.Null) then
            begin
              ACanvas.Fill.Color := BrushColor;
              ACanvas.FillRect(Rectf(rct1.Left, rct1.Top, rct1.Left + dx, rct1.Bottom),1,1,AllCorners,AbsoluteOpacity);
              ACanvas.Fill.Color := FontColor;
            end;

            {$IFDEF DELPHIXE6_LVL}
            ACanvas.FillText(rct1, s3, false, AbsoluteOpacity, [], TTextAlign.Leading, TTextAlign.Center);
            {$ELSE}
            ACanvas.FillText(rct1, s3, false, AbsoluteOpacity, [], TTextAlign.taLeading, TTextAlign.taCenter);
            {$ENDIF}

            rct1.Left := rct1.Left + dx;
          end;
          Inc(posln, length(Part));
        end;
      end;
    end;
  end;

  procedure BufferingDraw(part: string; var bufstyle: TStyle);
  begin
    if EgalStyle(bufstyle, backupstyle) then
    begin
      backupstring := backupstring + part;
    end
    else
    begin
      DrawPart(backupstring, backupstyle);
      backupstyle := bufstyle;
      backupstring := part;
    end;
    ResetPartStyle(bufstyle);
  end;

  procedure DrawSegments(S: string; var rct: TRectF; var SegmentStyle: Tstyle);
  var
    i, len, toStart, toEnd, Innr, lc, rc: integer;
    done, WasPoint: boolean;
    validno: boolean;
    part, partc, cs: string;
    numsallowed: string;
    mcrl, mcll: Integer;
  begin
    if (not Assigned(FSyntaxStyles)) or (not UseStyler) then
    begin
      BufferingDraw(s, SegmentStyle);
      Exit;
    end;

    if (SegmentStyle.isURL) and (FUrlAware) then
    begin
      BufferingDraw(s, SegmentStyle);
      Exit;
    end;

    if (SegmentStyle.isHighlight) then
    begin
      BufferingDraw(s, SegmentStyle);
      Exit;
    end;

    toStart := 1;
    validno := True;
    done := false;

    while S <> '' do
    begin
      Len := Length(S);
      if (len = 0) or (tostart > len) then
        Exit;

      if not done then
      begin
        {$IFDEF DELPHI_LLVM}
        validno := (toStart = 1) or (s[toStart - 1] = #32) or (FTempDelimiters = '') or
          ((AnsiPos(S[toStart - 1], FTempDelimiters) > 0));
        {$ELSE}
        validno := (toStart = 1) or (s[toStart] = #32) or (FTempDelimiters = '') or
          ((AnsiPos(S[toStart], FTempDelimiters) > 0));
        {$ENDIF}
      end;

      done := False;

      if (not SegmentStyle.isBracket) then
        if InternalStyles.HasMultiComment then
        begin
          if SegmentStyle.isComment then
          begin
            rc := InternalStyles.PosCommentRight(s,mcrl);

            if (rc > 0) then
            begin
              BufferingDraw(copy(s, 1, rc + mcrl - 1), SegmentStyle);
              Delete(s, 1, rc + mcrl - 1);
              SegmentStyle.isComment := False;
              len := length(s);
              if len = 0 then
                Exit;
            end
            else
            begin
              BufferingDraw(s, SegmentStyle);
              Exit;
            end;
          end
          else
          begin
            lc := InternalStyles.PosCommentLeft(s, mcll);
            if (lc = tostart) then
            begin
              cs := copy(s,lc,mcll);
              Delete(s, 1, (lc - 1) + mcll);
              tostart := 1;
              SegmentStyle.isComment := True;
              BufferingDraw(cs, SegmentStyle);

              len := length(s);
              if len = 0 then
                Exit;
              done := True;
            end
          end;
        end;

      if not done then
      begin
        if (not SegmentStyle.isComment) then
        begin
          if (not SegmentStyle.isBracket) and (AnsiPos(FSyntaxStyles.LineComment, s) = tostart) then
          begin
            part := copy(s, tostart, len - tostart + 1);
            SegmentStyle.isComment := True;
            BufferingDraw(part, SegmentStyle);
            isinlinecomment := True;
            Exit;
          end;

          if (SegmentStyle.isBracket) and (SegmentStyle.StartBracket <> #0) then
          begin
            {$IFDEF DELPHI_LLVM}
            if s[tostart - 1] = lit then
            {$ELSE}
            if s[tostart] = lit then
            {$ENDIF}
            begin
              {$IFDEF DELPHI_LLVM}
              BufferingDraw(s[tostart - 1], SegmentStyle);
              {$ELSE}
              BufferingDraw(s[tostart], SegmentStyle);
              {$ENDIF}
              Delete(s, tostart, 1);
              len := length(s);
              if tostart > len then
                Exit;

              {$IFDEF DELPHI_LLVM}
              BufferingDraw(s[tostart - 1], SegmentStyle);
              {$ELSE}
              BufferingDraw(s[tostart], SegmentStyle);
              {$ENDIF}
              Delete(s, tostart, 1);
              len := length(s);
              if tostart > len then
                Exit;
              done := True;
              Continue;
            end;

            {$IFDEF DELPHI_LLVM}
            if s[tostart - 1] = SegmentStyle.EndBracket then
            begin
              BufferingDraw(s[tostart - 1], SegmentStyle);
            {$ELSE}
            if s[tostart] = SegmentStyle.EndBracket then
            begin
              BufferingDraw(s[tostart], SegmentStyle);
            {$ENDIF}
              Delete(s, tostart, 1);
              SegmentStyle.isBracket := False;
              validno := False;
              done := True;
              Continue;
            end
            else
            begin
              {$IFDEF DELPHI_LLVM}
              BufferingDraw(s[tostart - 1], SegmentStyle);
              {$ELSE}
              BufferingDraw(s[tostart], SegmentStyle);
              {$ENDIF}
              inc(tostart);
              len := length(s);
              if (tostart > len) then
              begin
                if SegmentStyle.EndBracket = #32 then
                begin
                  SegmentStyle.isBracket := false;
                  SegmentStyle.StartBracket := #0;
                  SegmentStyle.EndBracket := #0;
                end;
                Exit;
              end;
              done := True;
            end;
          end
          else
          begin
            SegmentStyle.EndBracket := #0;
            SegmentStyle.StartBracket := #0;

            for lc := 0 to FSyntaxStyles.FAllStyles.Count - 1 do
            begin
              if FSyntaxStyles.FAllStyles.Items[lc].FStyleType <> stBracket then
                Continue;

              SegmentStyle.EndBracket := FSyntaxStyles.FAllStyles.Items[lc].BracketEnd;
              SegmentStyle.StartBracket := FSyntaxStyles.FAllStyles.Items[lc].BracketStart;

              SegmentStyle.index := lc;
              {$IFDEF DELPHI_LLVM}
              if (SegmentStyle.EndBracket <> #0) and (s[toStart - 1] = SegmentStyle.StartBracket) then
              {$ELSE}
              if (SegmentStyle.EndBracket <> #0) and (s[toStart] = SegmentStyle.StartBracket) then
              {$ENDIF}
              begin
                SegmentStyle.isBracket := True;
                SegmentStyle.EndBracket := FSyntaxStyles.FAllStyles.Items[lc].BracketEnd;
                SegmentStyle.StartBracket := FSyntaxStyles.FAllStyles.Items[lc].BracketStart;
                Break;
              end;
            end;

            if SegmentStyle.isBracket then
            begin
              {$IFDEF DELPHI_LLVM}
              BufferingDraw(s[toStart - 1], SegmentStyle);
              {$ELSE}
              BufferingDraw(s[toStart], SegmentStyle);
              {$ENDIF}
              Delete(s, toStart, 1);
              Continue;
            end;
          end;
        end;
      end;

      len := length(s);
      if (Len = 0) or (toStart > len) then
        Exit;

      if not done then
        for i := 0 to FSyntaxStyles.FAllStyles.Count - 1 do
        begin
          if FSyntaxStyles.FAllStyles.Items[i].FStyleType <> stSymbol then
            Continue;

          {$IFDEF DELPHI_LLVM}
          if (toStart <= len) and (AnsiPos(S[toStart - 1], FSyntaxStyles.FAllStyles.Items[i].FSymbols) > 0) then
          {$ELSE}
          if (toStart <= len) and (AnsiPos(S[toStart], FSyntaxStyles.FAllStyles.Items[i].FSymbols) > 0) then
          {$ENDIF}
          begin
            SegmentStyle.isDelimiter := True;
            SegmentStyle.index := i;
            {$IFDEF DELPHI_LLVM}
            BufferingDraw(s[toStart - 1], SegmentStyle);
            {$ELSE}
            BufferingDraw(s[toStart], SegmentStyle);
            {$ENDIF}
            Delete(s, toStart, 1);
            validno := True;
            Len := Length(S);
            done := True;
            Break;
          end;
        end;

      if done then
        Continue;

      toEnd := tostart;
      if (len = 0) or (tostart > Len) then
        Exit;

      if validno then
      begin
        {$IFDEF DELPHI_LLVM}
        if (AnsiPos(UpCase(S[tostart - 1]), FtmpNoStart) > 0) then
        {$ELSE}
        if (AnsiPos(UpCase(S[tostart]), FtmpNoStart) > 0) then
        {$ENDIF}
        begin
          if Pos(FtmpNoHex, Uppercase(s)) = toStart then
          begin
            numsallowed := FtmpNo + 'ABCDEF';
            toEnd := toEnd + length(ftmpnohex);
          end
          else
            numsallowed := FtmpNo;

          WasPoint := False;
          Innr := toStart;

          {$IFDEF DELPHI_LLVM}
          while ((toEnd <= Len) and (AnsiPos(UpCase(S[toEnd - 1]), numsallowed) > 0)) do
          {$ELSE}
          while ((toEnd <= Len) and (AnsiPos(UpCase(S[toEnd]), numsallowed) > 0)) do
          {$ENDIF}
          begin
            if UpperCase(copy(s, tostart, toend)) = FtmpNoHex then
              numsallowed := FtmpNo + 'ABCDEF';

            {$IFDEF DELPHI_LLVM}
            if S[toEnd - 1] = '.' then
            {$ELSE}
            if S[toEnd] = '.' then
            {$ENDIF}
            begin
              if WasPoint then
              begin
                toEnd := Innr;
                Break;
              end;
              WasPoint := True;
              Innr := toEnd;
            end;
            Inc(toEnd);
          end;

          Dec(toEnd);

          if (tostart <= toend) then
          begin
            SegmentStyle.isDelimiter := False;
            SegmentStyle.isNumber := True;
            part := copy(s, tostart, toend - tostart + 1);
            Delete(s, tostart, toend - tostart + 1);
            BufferingDraw(part, SegmentStyle);
            validno := False;
            done := True;
          end;
        end;
      end;

      if done then
        Continue;

      Len := Length(S);

      if (len = 0) or (tostart > Len) then
        Exit;

      toend := tostart + 1;

      {$IFDEF DELPHI_LLVM}
      while (toend <= Len) and (S[toend - 1] <> #32) and (not ( (s[toend - 1] = ';') or (s[toend - 1] = ':')) )
        and (AnsiPos(S[toend - 1], FTempdelimiters) = 0) do
          Inc(toend);
      {$ELSE}
      while (toend <= Len) and (S[toend] <> #32) and (not ( (s[toend] = ';') or (s[toend] = ':')) )
        and (AnsiPos(S[toend], FTempdelimiters) = 0) do
          Inc(toend);
      {$ENDIF}

      part := Copy(S, toStart, toEnd - toStart);

      if (length(part) >= 1) and (validno) then
      begin
        if not CaseSensitive then
          partc := AnsiLowerCase(part)
        else
          partc := part;

        for i := 0 to FSyntaxStyles.FAllStyles.Count - 1 do
        begin
          if FSyntaxStyles.FAllStyles.Items[i].FStyleType = stKeyword then
          begin
            FSyntaxStyles.FAllStyles.Items[i].FKeywords.CaseSensitive := CaseSensitive;

            if FSyntaxStyles.FAllStyles.Items[i].FKeyWords.IndexOf(partc) <> - 1 then
            begin
              SegmentStyle.iskeyWord := True;
              SegmentStyle.index := i;

              if FSyntaxStyles.CustomDraw then
              begin
                BufferingDraw(backupstring, SegmentStyle);
                backupstring := '';
              end
              else
              begin
                BufferingDraw(part, SegmentStyle);
              end;
              Delete(s, toStart, toend - tostart);
              done := True;
              Break;
            end;
          end;
        end;
      end;


      if done then
        Continue;

      if not done then
      begin
      {$IFDEF DELPHI_LLVM}
        BufferingDraw(s[toStart - 1], SegmentStyle);
      {$ELSE}
        BufferingDraw(s[toStart], SegmentStyle);
      {$ENDIF}
      end;

      inc(toStart);
    end;
  end;

begin
  if not (DM = dmHTML) and not (DM = dmRTF) then
  begin
    rct0 := pr;
    lineRct := rct0;
  end;

  LineColor := TAlphaColorRec.Null;
  LineTextColor := TAlphaColorRec.Null;

  if ShowBands then
  begin
    if odd(LineNo) then
      LineColor := GetBandColor;
  end;

  if ActiveLineSettings.ShowActiveLine then
  begin
    if (ActiveLineSettings.ActiveLineAtCursor and (LineNo = CurY)) or
       (not ActiveLineSettings.ActiveLineAtCursor and (LineNo = ActiveLine)) then
    begin
      LineColor := GetActiveLineColor;
      LineTextColor := GetActiveLineTextColor;
    end;
  end;

  if (LineColor <> TalphaColorRec.Null) and not (DM in [dmHTML, dmRTF]) then
  begin
    {$IFDEF DELPHIXE6_LVL}
    Canvas.Fill.Kind := TBrushKind.Solid;
    {$ELSE}
    Canvas.Fill.Kind := TBrushKind.bkSolid;
    {$ENDIF}
    Canvas.Fill.Color := LineColor;
    Canvas.FillRect(linerct,0,0,AllCorners,255);
  end;

  s := '';
  if (Assigned(FSyntaxStyles)) and (UseStyler) then
    s := FSyntaxStyles.Literal;

  if length(s) > 0 then
    {$IFDEF DELPHI_LLVM}
    lit := s[0]
    {$ELSE}
    lit := s[1]
    {$ENDIF}
  else
    lit := #0;

  if LineNo < Lines.Count then
  begin
    rct := rct0;

    S := Lines[LineNo];

    if not (DM = dmHTML) and not (DM = dmRTF) then
    begin
      xSelStartX := FSelStartX;
      xSelEndX := FSelEndX;
      xSelStartY := FSelStartY;
      xSelEndY := FSelEndY;
      OrderPos(xSelStartX, xSelStartY, xSelEndX, xSelEndY);
      rct1 := rct;
      rct1.Left := rct1.Left - (FLeftCol * FCellSize.W);
    end;

    if (DM = dmPrinter) or (DM = dmPrintPreview) then
      rct1 := PR;

    posln := 0;
    backupstyle := style;
    isinlinecomment := False;


    while (HighlightText <> '') and (VarPos(HighlightText, s, hvp) > 0) do
    begin
      hs := copy(s, 1, hvp - 1);

      if URLAware then
      begin
        urls := TStringList.Create;

        ExtractURL(hs, urls);

        for i := 0 to urls.Count - 1 do
        begin
          style.isURL := WordIsUrl(urls.Strings[i]);
          DrawSegments(urls.Strings[i], rct1, style);
          style.isURL := false;
        end;
        urls.Free;
      end
      else
        DrawSegments(hs, rct1, style);

      hs := Copy(s, hvp, length(HighLightText));
      style.isHighlight := true;
      DrawSegments(hs, rct1, style);
      style.isHighlight := false;
      s := copy(s, hvp + length(highlighttext),length(s));
    end;


    if URLAware then
    begin
      urls := TStringList.Create;

      try
        ExtractURL(s, urls);

        for i := 0 to urls.Count - 1 do
        begin
          style.isURL := WordIsUrl(urls.Strings[i]);
          DrawSegments(urls.Strings[i], rct1, style);
          style.isURL := false;
        end;
      finally
        urls.Free;
      end;
    end
    else
      DrawSegments(s, rct1, style);

    DrawPart(BackupString, BackupStyle);

    if isinlinecomment then
      Style.isComment := False;
  end;
end;

procedure TTMSFMXMemo.PaintMemo(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
var
  i, gw: integer;
  nl: double;
  r, mr: TRectF;
  LineStyle: TStyle;
  dx: integer;
begin
  Canvas.SetMatrix(AbsoluteMatrix);

  nl := ARect.Height / FCellSize.H;
  Canvas.Font.Assign(FFont);

  // paint right margin
  if FShowRightMargin then
  begin
    dx := Round(FCellSize.W * (RightMargin - FLeftCol));

    if (dx < Width) and (dx > 0) then
    begin
      Canvas.Stroke.Color := TAlphaColorRec.Gray;
      Canvas.StrokeThickness := 0.5;
      {$IFDEF DELPHIXE6_LVL}
      Canvas.Stroke.Kind := TBrushKind.Solid;
      {$ELSE}
      Canvas.Stroke.Kind := TBrushKind.bkSolid;
      {$ENDIF}
      Canvas.DrawLine(PointF(dx, 0), PointF(dx, Height),255);
    end;
  end;

  FSelectionBackGroundColor := GetSelectionColor;
  FSelectionTextColor := GetSelectionTextColor;
  FBreakPointBackGroundColor := GetBreakLineColor;
  FBreakPointTextColor := GetBreakLineTextColor;

  Canvas.Fill.Color := TAlphaColorRec.Black;
  Canvas.Stroke.Color := TAlphaColorRec.Black;
  Canvas.StrokeThickness := 1;
  {$IFDEF DELPHIXE6_LVL}
  Canvas.Stroke.Kind := TBrushKind.Solid;
  {$ELSE}
  Canvas.Stroke.Kind := TBrushKind.bkSolid;
  {$ENDIF}

  gw := GetGutterWidth;

  LineStyle := GetUpStyle(TopLine);
  mr := RectF(gw, 0, Width - gw, Height);

  for i := 0 to round(nl) - 1 do
  begin
    r := RectF(ARect.Left + 1 + gw, ARect.Top + i * FCellSize.H, ARect.Right + gw, ARect.Top + (i + 1) *  FCellSize.H);

    if FTopLine + i < FLines.Count then
    begin
      if (FClipLine >= 0) then
      begin
        if (FTopLine + i = FClipLine) then
        begin
          PaintLine(Canvas, FTopLine + i, LineStyle, dmScreen, r);
          FClipLine := -1;
          Break;
        end;
      end
      else
        PaintLine(Canvas, FTopLine + i, LineStyle, dmScreen, r);
    end;
  end;
end;

procedure TTMSFMXMemo.OnFontChanged(Sender: TObject);
begin
  CalculateCellSize;
  UpdateMemo;
end;


procedure TTMSFMXMemo.OutputHTML(FixedFonts: Boolean; fromline, toline: integer;
  fragment: boolean);
var
  i: Integer;
  hstl: TStyle;
  pRect: TRectF;
  fl,tl: integer;
begin
  InternalLines.OnChange := nil;
  BeginUpdate;

  FHtml.Clear;

  if not fragment then
  begin
    {$IFDEF FREEWARE}
    FHtml.Add('<!-- saved from TAdvMemo -->');
    {$ENDIF}
    FHtml.Add('<HTML>');
    //FHtml.Add('<BODY bgColor=' + HTMLClr(FBkColor) + ' LINK=' + HTMLClr(UrlStyle.FTextColor) + ' VLINK=' + HTMLClr(UrlStyle.FTextColor) + ' ALINK=' + HTMLClr(UrlStyle.FTextColor) + '>');
    FHtml.Add('<BODY bgColor='+HTMLClr(GetBkgColor) + '>');
  end;

  FHtml.Add('<PRE>');

  if FixedFonts then
    FHtml.Add('<FONT style="font-family:' + FFont.Family + '; font-size:' + IntToStr(Round(FFont.Size)) + '">')
  else
    FHtml.Add('<FONT style="font-family:' + FFont.Family + ';">');

  hstl.isComment := False;
  hstl.isBracket := False;
  hstl.isnumber := False;
  hstl.iskeyWord := False;
  hstl.isdelimiter := False;
  hstl.isHighlight := False;
  hstl.isURL := False;
  hstl.EndBracket := #0;
  hstl.index := 0;
  FHtmlfont := '';

  if fromline = -1 then
  begin
    fl := 0;
    tl := InternalLines.Count - 1;
  end
  else
  begin
    if (fromline < toline) then
    begin
      fl := fromline;
      tl := toline;
    end
    else
    begin
      fl := toline;
      tl := fromline;
    end;
  end;

  for i := fl to tl do
  begin
    FHtml.Add('');
    PaintLine(Canvas, i, hstl, dmHTML, pRect);
  end;

  if FHtmlfont <> '' then
    FHtml.Add('</FONT>');

  FHtml.Add('</FONT>');
  FHtml.Add('</PRE>');

  if not fragment then
  begin
    FHtml.Add('</BODY>');
    FHtml.Add('</HTML>');
  end;

  InternalLines.OnChange := LinesChanged;

  EndUpdate;
end;

procedure TTMSFMXMemo.OutputRTF(FixedFonts: Boolean; fromline, toline: integer);
var
  i: Integer;
  hstl: TStyle;
  pRect: TRectF;
  fl,tl: integer;
begin
  if not Assigned(FRTFEngine) then
    Exit;

  if FixedFonts then
  begin
    FRTFEngine.AddFontName(FFont.Family);
    FRTFEngine.AddFontSize(Round(FFont.Size));
    FRTFEngine.AddForeColor(FFontColor);
  end
  else
  begin
    FRTFEngine.AddFontName(FFont.Family);
    FRTFEngine.AddFontSize(Round(FFont.Size));
    FRTFEngine.AddForeColor(FFontColor);
  end;

  hstl.isComment := False;
  hstl.isBracket := False;
  hstl.isnumber := False;
  hstl.iskeyWord := False;
  hstl.isdelimiter := False;
  hstl.isHighlight := False;
  hstl.isURL := False;
  hstl.EndBracket := #0;
  hstl.index := 0;
  fhtmlfont := '';

  if fromline = -1 then
  begin
    fl := 0;
    tl := InternalLines.Count - 1;
  end
  else
  begin
    if (fromline < toline) then
    begin
      fl := fromline;
      tl := toline;
    end
    else
    begin
      fl := toline;
      tl := fromline;
    end;
  end;

  for i := fl to tl do
  begin
    PaintLine(Canvas, i, hstl, dmRTF, pRect);
    FRTFEngine.AddNewLine;
  end;
end;

procedure TTMSFMXMemo.GutterChanged(Sender: TObject);
begin
  UpdateMemo;
end;

function TTMSFMXMemo.GetVisibleN(const Index: Integer): Integer;
var
  Coord: TFullPos;
  Cell: TCellPos;
  eRect: TRectF;
begin
  eRect := EditorRect;

  Coord := CharFromPos(eRect.Right - 1, eRect.Bottom - 1);
  Cell := CellFromPos(eRect.Right - 1, eRect.Bottom - 1);
  case Index of
    0: Result := Round(Cell.X);
    1: Result := Round(Cell.Y);
    2: Result := Round(Coord.Pos - 1);
    3: Result := Round(Coord.LineNo - 1);
  else
    Result := 0;
  end;
end;

procedure TTMSFMXMemo.ResizeScrollBars;
begin
  InitScrollbars;
end;

procedure TTMSFMXMemo.SetCurX(Value: Integer);
var
  len: integer;
  WasVisible: boolean;
  lf: boolean;
  chng: boolean;
begin
  if (Value = FCurX) and AutoExpand then
    Exit;

  if (InternalLines.Count = 0) and not AutoExpand then
    Exit;

  if Value < 0 then
    if CurY = 0 then Value := 0
    else
    begin
      CurY := CurY - 1;
      if (CurY >= 0) and (CurY < InternalLines.Count) then
        Value := Length(InternalLines[CurY]);
    end;

  chng := FCurX <> Value;

  FCurX := Value;

  if (CurY >= 0) and (CurY < InternalLines.Count) then
  begin
    len := Length(InternalLines[CurY]);

    lf := FLetRefresh;
    FLetRefresh := False;

    if Value <= FMaxLength then
      SetMaxLength;

    if Value > len then
    begin
     if AutoExpand then
      begin
        if InternalLines.Count > 0 then
          InternalLines[CurY] := InternalLines[CurY] + StringOfChar(' ', Value - len)
        else
          InternalLines.Add(StringOfChar(' ', Value - len));
      end
      else
      begin
        if InternalLines.Count = 0 then
        begin
          InternalLines.Add('');
          Value := 0;
        end
        else
          Value := len;
      end;
    end;
    FLetRefresh := lf;
  end;

  if chng or (FCurX <> Value) then
  begin
    FCurX := Value;
    DoCursorChange;
  end;

  WasVisible := CaretVisible;

  MakeVisible;
  ResizeScrollBars;
  if WasVisible then
    ShowCaret(True);
end;

procedure TTMSFMXMemo.SetCurY(Value: Integer);
var
  WasVisible: boolean;

begin
  if InternalLines.Count = 0 then
    InternalLines.Add('');

  WasVisible := CaretVisible;

  if Value >= InternalLines.Count then
    Value := InternalLines.Count - 1;

  if Value < 0 then
    Value := 0;

  if Value = FCurY then
    Exit;

  if (FCurY <> Value) then
  begin
    FCurY := Value;
    DoCursorChange;
  end;

  if AutoExpand then
  begin
    MakeVisible;
    ResizeScrollBars;
    if WasVisible then
      ShowCaret(True);
  end
  else
    CurX := CurX;
end;

procedure TTMSFMXMemo.SetFont(const Value: TFont);
begin
  FFont.Assign(Value);
  CalculateCellSize;
  UpdateMemo;
end;

procedure TTMSFMXMemo.SetFontColor(const Value: TAlphaColor);
begin
  if (FFontColor <> Value) then
  begin
    FFontColor := Value;
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.SetGutter(const Value: TTMSFMXGutter);
begin
  FGutter.Assign(Value);
end;

procedure TTMSFMXMemo.SetHighlightText(const Value: string);
begin
  if (FHighlightText <> Value) then
  begin
    FHighlightText := Value;
    Repaint;
  end;
end;

procedure TTMSFMXMemo.SetInternalLines(const Value: TFMXMemoStrings);
begin
  FLines.Assign(Value);
end;

function TTMSFMXMemo.GetInternalLines: TFMXMemoStrings;
begin
  Result := FLines;
end;

procedure TTMSFMXMemo.SetLeftCol(const Value: integer);
begin
  if (FLeftCol <> Value) then
  begin
    FLeftCol := Value;
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.SetLines(const Value: TFMXMemoStrings);
var
  I: Integer;
begin
  if Value <> nil then
  begin
    BlockUpdate;
    InternalLines.Clear;
    InternalLines.AddStrings(Value);

    if TrimTrailingSpaces then
    begin
      for I := 0 to InternalLines.Count - 1 do
        InternalLines[i] := RemoveTrailingSpaces(InternalLines[i]);
    end;

    if not (csDesigning in ComponentState) then
      SelectionChanged;

    EndBlockUpdate;
  end;
end;

procedure TTMSFMXMemo.ResizeEditor;
var
  otl ,lib: Integer;
  tp, x, y: Integer;
  oldss,oldsl: Integer;
begin
  if (csReading in ComponentState) or (csLoading in ComponentState) then
    Exit;

  oldss := SelStart;
  oldsl := SelLength;

  otl := TopLine;

  TextFromPos(CurX, CurY, tp);

  lib := Lines.Count;

  PosFromText(tp, x, y);

  SetCursor(x, y);

  SelStart := oldss;
  SelLength := oldsl;

  MakeVisible;

  if (lib = Lines.Count) then
    FTopLine := otl;
end;

procedure TTMSFMXMemo.SetCursor(ACurX, ACurY: integer);
begin
  ClearSelection;
  FCurX := 0;
  CurY := ACurY;
  CurX := ACurX;
end;

procedure TTMSFMXMemo.SelectAll;
begin
  BlockUpdate;
  FSelStartY := 0;
  FSelStartX := 0;

  if (InternalLines.Count > 0) then
  begin
    FSelEndY := InternalLines.Count - 1;
    FSelEndX := Length(InternalLines[InternalLines.Count - 1]);
  end;

  SelectionChanged;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.SelectionChanged;
begin
  if Assigned(FOnSelectionChange) then
    FOnSelectionChange(Self);
end;

procedure TTMSFMXMemo.SelectPopupValue(AutoParenthesis: Boolean);
var
  sEntry: string;
  flg: Boolean;
  ae, ab, vp: integer;
  s, AToken: string;
  i: integer;
begin
  if FListBox.ItemIndex > -1 then
  begin
    sEntry := FListBox.Items[FListBox.ItemIndex];

    AToken := TokenAtXY(CurX, CurY);

    DoInsertAutoCompletionEntry(sEntry);

    flg := false;

    if sEntry = FListBox.Items[FListBox.ItemIndex] then
    begin
      vp := pos(' ', sEntry);
      if vp > 0 then
        Delete(sEntry, 1, vp);

      s := '';
      {$IFDEF DELPHI_LLVM}
      for i := 0 to Length(sEntry) - 1 do
      {$ELSE}
      for i := 1 to Length(sEntry) do
      {$ENDIF}
      begin
        if not ((sEntry[i] = '(') or (sEntry[i] = ';') or (sEntry[i] = ' ') or (sEntry[i] = ':')) then
          s := s + sEntry[i]
        else
          break;
      end;

      if (pos('(', sEntry) > 0) and AutoParenthesis then
      begin
        sEntry := s + '()';
        flg := true;
      end
      else
        sEntry := s;
    end;

    FSelStartY := CurY;
    FSelEndY := CurY;

    if FAutoCompleteDot and (Pos('.', sEntry) = 0) then
    begin
      FSelStartX := FDotPoint.X - Length(AToken);
      FSelEndX := CurX;
    end
    else
    begin
      s := InternalLines[cury];
      ae := length(s);
      ab := 1;
      if ae > 0 then
      begin
        {$IFDEF DELPHI_LLVM}
        for vp := CurX to length(s) - 1 do
        {$ELSE}
        for vp := CurX + 1 to length(s) do
        {$ENDIF}
        begin
          if (s[vp] = #32) or (s[vp] = '(') or (s[vp] = ')') or (s[vp] = '[') or (s[vp] = ']') then
          begin
            ae := vp - 1;
            Break;
          end;
        end;
        {$IFDEF DELPHI_LLVM}
        for vp := CurX - 1 downto 0 do
        {$ELSE}
        for vp := CurX  downto 1 do
        {$ENDIF}
        begin
          if (s[vp] = #32) or (s[vp] = '(') or (s[vp] = ')') or (s[vp] = '[') or (s[vp] = ']')  then
          begin
            ab := vp + 1;
            break;
          end;
        end;
      end;

      FSelStartX := ab - 1;
      FSelEndX := ae;
    end;

    SetSelText(sEntry);

    if flg then
      CurX := CurX - 1;

    if Popup.IsOpen then
      DoPopup;

    ShowCaret(True);

    if Assigned(FOnAutoCompletion) then
      FOnAutoCompletion(Self);
  end;
end;

procedure TTMSFMXMemo.SelectWordAtCursor;
var
  s: string;
  i: integer;
  fb, fe: integer;
begin
  if InternalLines.Count = 0 then
    Exit;

  BlockUpdate;

  s := InternalLines.Strings[CurY];

  fe := 0;
  fb := 0;

  {$IFDEF DELPHI_LLVM}
  for i := CurX - 1 to Length(s) - 1 do
  {$ELSE}
  for i := CurX to Length(s) do
  {$ENDIF}
  begin
    if not IsWordBoundary(s[i]) then
      fe := i
    else
      Break;
  end;

  {$IFDEF DELPHI_LLVM}
  for i := CurX - 1 downto 0 do
  {$ELSE}
  for i := CurX downto 1 do
  {$ENDIF}
  begin
    if not IsWordBoundary(s[i]) then
      fb := i - 1
    else
      Break;
  end;

  ClearSelection;

  if (fb < Fe) then
  begin
    FSelStartX := fb;
    FSelEndX := fe;
    FSelStartY := curY;
    FSelEndY := curY;
  end;
  SelectionChanged;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.DeleteSelection;
begin
  BlockUpdate;
  DeleteSelectionInt;
  EndBlockUpdate;
end;

function TTMSFMXMemo.DeleteSelectionInt: boolean;
var
  xSelStartX, xSelStartY, xSelEndX, xSelEndY: integer;
  i: integer;
  OldX, OldY: integer;
  S1, S2, S: string;
  Undo: TDeleteBufUndo;
  oldperf: boolean;
  extralf: boolean;

begin
  BlockUpdate;
  Result := false;

  if (FSelStartY = FSelEndY) and (FSelStartX = FSelEndX) then
  begin
    EndBlockUpdate;
    Exit;
  end;

  extralf := (FSelStartY <> FSelEndY) and (FSelEndX = 0);

  if SelectionMode <> smBlock then
  begin
    if (Max(FSelStartY, FSelEndY) >= InternalLines.Count) or (Min(FSelStartY, FSelEndY) < 0) or
      (InternalLines.Count = 0) then
    begin
      FSelStartY := 0;
      FSelEndY := 0;
      FSelStartX := 0;
      FSelEndX := 0;
      EndBlockUpdate;
      Exit;
    end;

    OldX := CurX;
    OldY := CurY;
    xSelStartX := FSelStartX;
    xSelStartY := FSelStartY;
    xSelEndX := FSelEndX;
    xSelEndY := FSelEndY;

    OrderPos(xSelStartX, xSelStartY, xSelEndX, xSelEndY);

    if xSelStartY = xSelEndY then
    begin
      S1 := Copy(InternalLines[xSelStartY], xSelStartX + 1, xSelEndX - xSelStartX);
      S2 := '';
    end
    else
    begin
      S1 := Copy(InternalLines[xSelStartY], xSelStartX + 1, Length(InternalLines[xSelStartY]));
      S2 := Copy(InternalLines[xSelEndY], 1, xSelEndX);
    end;

    Fletrefresh := False;

    InternalLines[xSelStartY] := Copy(InternalLines[xSelStartY], 1, xSelStartX) +
      Copy(InternalLines[xSelEndY], xSelEndX + 1, Length(InternalLines[xSelEndY]));

    if TrimTrailingSpaces
      then RemoveTrailingSpaces(InternalLines[xSelStartY]);

    CurY := xSelStartY;
    CurX := xSelStartX;

    ClearSelection;

    oldperf := InternalUndoList.IsPerforming;
    InternalUndoList.IsPerforming := true;

    BeginUpdate;

    if xSelStartY <> xSelEndY then
      InternalLines.Delete(xSelEndY);


    S := S1 + S;

    for i := xSelStartY + 1 to xSelEndY - 1 do
    begin
      S := S + #13#10;
      S := S + InternalLines[i];
    end;

    for i := xSelEndY - 1 downto xSelStartY + 1 do
      InternalLines.Delete(i);

    EndUpdate;

    InternalUndoList.IsPerforming := oldperf;

    if S2 <> '' then
      S := S + #13#10 + S2;

    SelectionChanged;

    Fletrefresh := True;

    if extralf then
      s:= s + #13#10;

    Undo := TDeleteBufUndo.Create(OldX, OldY, CurX, CurY, S);
    Undo.UndoSelStartX := xSelStartX;
    Undo.UndoSelStartY := xSelStartY;
    Undo.UndoSelEndX := xSelEndX;
    Undo.UndoSelEndY := xSelEndY;

    InternalUndoList.Add(Undo);
  end;

  Result := true;

  ClearSelection;
  EndBlockUpdate;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXMemo.PosFromText(TextPos: integer; var X, Y: Integer);
var
  i, j, l: integer;
begin
  X := 0;
  Y := 0;

  j := 0;
  i := 0;

  l := TextPos;

  while (j <= TextPos) and (i < InternalLines.Count) do
  begin
    j := j + Length(InternalLines[i]) + 2;

    if (TextPos < j) then
    begin
      X := l;
      Y := i;
      Break;
    end;

    l := l - (Length(InternalLines[i]) + 2);
    inc(i);
  end;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXMemo.SetSelectionColor(const Value: TAlphaColor);
var
  obj: TFMXObject;
begin
  FSelectionBackgroundColor := Value;

  obj := FindStyleResource('selectioncolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    (obj as TRectangle).Fill.Color := Value;
  end;
end;

procedure TTMSFMXMemo.SetSelectionMode(const Value: TSelectionMode);
begin
  if (FSelectionMode <> Value) then
  begin
    FSelStartX := 0;
    FSelStartY := 0;
    FSelEndX := 0;
    FSelEndY := 0;
    FSelectionMode := Value;
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.SetSelectionTextColor(const Value: TAlphaColor);
var
  obj: TFMXObject;
begin
  FSelectionTextColor := Value;

  obj := FindStyleResource('selectiontextcolor');
  if Assigned(obj) and (obj is TLabel) then
  begin
    (obj as TLabel).FontColor := Value;
  end;
end;

//------------------------------------------------------------------------------

procedure TTMSFMXMemo.SetSelLength(const Value: Integer);
begin
  BlockUpdate;
  PosFromText(SelStart + Value, FSelEndX, FSelEndY);
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.SetSelStart(const Value: Integer);
var
  len: integer;
begin
  BlockUpdate;
  len := GetSelLength;
  PosFromText(Value, FSelStartX, FSelStartY);
  PosFromText(Value + len, FSelEndX, FSelEndY);
  EndBlockUpdate;
end;

function TTMSFMXMemo.GetSelLength: Integer;
begin
  Result := Length(GetSelText);
end;

function TTMSFMXMemo.GetSelStart: Integer;
var
  i: integer;
  xSelStartX, xSelStartY, xSelEndX, xSelEndY: integer;
begin
  Result := 0;

  if InternalLines.Count = 0 then
    Exit;

  if (FSelStartY = FSelEndY) and (FSelStartX = FSelEndX) then
  begin
    for i := 1 to FSelStartY do
      Result := Result + Length(InternalLines[i - 1]) + 2;

    Result := Result + FSelStartX;
    Exit;
  end;

  xSelStartX := FSelStartX;
  xSelStartY := FSelStartY;
  xSelEndX := FSelEndX;
  xSelEndY := FSelEndY;

  OrderPos(xSelStartX, xSelStartY, xSelEndX, xSelEndY);

  for i := 1 to xSelStartY do
    Result := Result + Length(InternalLines[i - 1]) + 2;

  Result := Result + xSelStartX;
end;

//------------------------------------------------------------------------------

function TTMSFMXMemo.GetSelText: string;
var
  xSelStartX, xSelStartY, xSelEndX, xSelEndY: integer;
  i: integer;
begin
  Result := '';

  if InternalLines.Count = 0 then
    Exit;
  if (FSelStartY = FSelEndY) and (FSelStartX = FSelEndX) then
    Exit;

  xSelStartX := FSelStartX;
  xSelStartY := FSelStartY;
  xSelEndX := FSelEndX;
  xSelEndY := FSelEndY;

  OrderPos(xSelStartX, xSelStartY, xSelEndX, xSelEndY);

  if xSelStartY = xSelEndY then
    Result := Copy(InternalLines[xSelStartY], xSelStartX + 1, xSelEndX - xSelStartX)
  else
  begin
    Result := Copy(InternalLines[xSelStartY], xSelStartX + 1, Length(InternalLines[xSelStartY]));
    for i := xSelStartY + 1 to xSelEndY - 1 do
    begin
      Result := Result + #13#10;
      Result := Result + InternalLines[i];
    end;

    Result := Result + #13#10;
    Result := Result + Copy(InternalLines[xSelEndY], 1, xSelEndX);
  end;
end;

{$IFDEF ANDROID}
function TTMSFMXMemo.GetTargetClausePointF: TPointF;
begin

end;

function TTMSFMXMemo.GetTextService: TTextService;
begin
  Result := FTextService;
end;
{$ENDIF}

procedure TTMSFMXMemo.SetSelText(const AValue: string);
var
  i, k, j, vp: integer;
  xSelStartX, xSelStartY, xSelEndX, xSelEndY: integer;
  Buff, S, NL: string;
  OldX, OldY, from, sl,lines: integer;
  tz: Tstringlist;
  delsel: boolean;
begin
  BlockUpdate;
  Buff := AValue;
  xSelStartX := FSelStartX;
  xSelStartY := FSelStartY;
  xSelEndX := FSelEndX;
  xSelEndY := FSelEndY;

  OrderPos(xSelStartX, xSelStartY, xSelEndX, xSelEndY);

  delsel := DeleteSelectionInt;

  OldX := CurX;
  OldY := CurY;
  i := Pos(#13#10, Buff);

  if InternalLines.Count > 0 then
    S := InternalLines[xSelStartY]
  else
    InternalLines.Add('');

  sl := Length(AValue);

  if SelectionMode = smBlock then
  begin
    while i > 0 do
    begin
      S := Copy(Buff,1,i);
      Delete(Buff,1,i + 1);
      NL := InternalLines[oldY];
      k := length(NL);
      if (k < CurX) then
        NL := NL + DupeString(' ',CurX - k);

      Insert(S, NL, CurX + 1);

      InternalLines[oldY] := NL;

      inc(oldY);

      i := Pos(#13#10, Buff);
    end;
  end
  else
  begin
    if i = 0 then
    begin
      InternalLines[xSelStartY] := Copy(S, 1, xSelStartX) + Buff + Copy(S, xSelStartX + 1, Length(S));

      CurX := xSelStartX;

      if Buff <> '' then
        CurX := CurX + Length(Buff);

      InternalLines.OnChange := LinesChanged;
      InternalUndoList.Add(TPasteUndo.Create(OldX, OldY, CurX, CurY, AValue, delsel));
    end
    else
    begin
      k := xSelStartY;
      InternalLines[k] := Copy(S, 1, xSelStartX) + Copy(Buff, 1, i - 1); // first line
      TFMXMemoStrings(InternalLines).DoInsert(k + 1, Copy(S, xSelStartX + 1, Length(S)));

      InternalLines.OnChange := nil;
      tz := TStringList.Create;

      from := 1;
      lines := 0;

      while varposl(#13#10, buff, sl, from, vp) > 0 do
      begin
        NL := copy(buff, from, vp - from);
        tz.Add(NL);
        from := vp + 2;
        inc(lines);
      end;

      if from <= sl then
      begin
        NL := copy(buff, from, sl);
        tz.Add(NL);
      end
      else
        tz.Add('');

      j := 0;

      for k := 1 to tz.Count - 1 do
      begin
        if xSelStartY + k >= InternalLines.Count then
          Continue;

        if k < tz.Count - 1 then
        begin
          TFMXMemoStrings(InternalLines).DoInsert(xSelStartY + k, tz.Strings[k]);
        end
        else
        begin
            InternalLines[xSelStartY + k] := tz.Strings[k] + InternalLines[xSelStartY + k];

          j := length(tz.Strings[k]);
        end;
      end;

      k := xSelStartY + lines;
      tz.free;

      InternalLines.OnChange := LinesChanged;

      CurY := k;
      CurX := j;

      MakeVisible;

      InternalUndoList.Add(TPasteUndo.Create(OldX, OldY, CurX, CurY, AValue, delsel));
    end;
  end;

  ClearSelection;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.SetShowBands(const Value: boolean);
begin
  if (FShowBands <> Value) then
  begin
    FShowBands := Value;
    Repaint;
  end;
end;

procedure TTMSFMXMemo.SetShowRightMargin(const Value: boolean);
begin
  if (FShowRightMargin <> Value) then
  begin
    FShowRightMargin := Value;
    Repaint;
  end;
end;

procedure TTMSFMXMemo.ExpandSelection;
begin
  BlockUpdate;
  FSelEndX := CurX;
  FSelEndY := CurY;
  FPrevSelX := CurX;
  FPrevSelY := CurY;

  SelectionChanged;

  FSelChange := true;
  FSelChange := false;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.SetSyntaxStyles(const Value: TTMSFMXMemoCustomStyler);
begin
  FSyntaxStyles := Value;
  FCachedDelimiters := '';
end;

procedure TTMSFMXMemo.SetTopLine(const Value: integer);
var
  nv, LinesCount: Integer;
  newval: Boolean;
  WasVisible: boolean;
  VS: TScrollBar;
begin
  newval := FTopLine <> Value;

  if not newval then
    Exit;

  LinesCount := InternalLines.Count;

  if (VisibleLineCount > LinesCount) then
  begin
    CurY := 0;
    CurX := 0;
    Exit;
  end;

  VS := GetVScroll;
  if (Value >= 0) and (Value + VisibleLineCount < LinesCount) then
  begin
    FTopLine := Value;
    if Assigned(VS) then
      VS.Value := FTopLine;
  end
  else
  begin
    if (Value >= 0) then
    begin
      nv := LinesCount - (VisibleLineCount div 2);
      if nv < 0 then
        nv := 0;
      FTopLine := nv;
      if Assigned(VS) then
        VS.Value := FTopLine;
    end;
  end;

  WasVisible := FCaretVisible;

  if (CurY >= TopLine) and (CurY <= TopLine - VisibleLineCount) then
    MakeVisible;

  if WasVisible then
    ShowCaret(True);
end;

procedure TTMSFMXMemo.SetTrimTrailingSpaces(const Value: boolean);
begin
  if (FTrimTrailingSpaces <> Value) then
  begin
    FTrimTrailingSpaces := Value;
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.SetUrlAware(const Value: boolean);
begin
  if FUrlAware <> Value then
  begin
    FUrlAware := Value;
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.SetUseStyler(const Value: boolean);
begin
  FUseStyler := Value;
end;

procedure TTMSFMXMemo.SetVersion(const Value: String);
begin

end;

{$IFDEF ANDROID}
procedure TTMSFMXMemo.UpdateCaretPoint;
var
  str: String;
  I, l: Integer;
  s: TShiftState;
  k: Word;
begin
  str := FTextService.CombinedText;
  if Length(str) < Length(FImeText) then
  begin
    l := Length(FImeText) - Length(str);
    for I := 0 to l - 1 do
      BackSpace;
    FImeText := str;
  end
  else
  begin
    str := Copy(str, Length(FImeText) + 1, Length(str) - Length(FImeText));

    for I := 0 to Length(str) - 1 do
    begin
      s := [];
      if UpperCase(str[I]) = str[I] then
        s := [ssShift];

      k := 0;//Ord(str[I]);
      KeyDown(k, str[I], s);
    end;
  end;
end;
{$ENDIF}

procedure TTMSFMXMemo.UpdateCompletionList(AToken: String);
var
  i, j, vp, tp: Integer;
  method: string;

begin
  GetAutoCompletionList(AToken, FAutoCompleteList);

  j := -1;

  for i := 0 to FAutoCompleteList.Count - 1 do
  begin
    if (AToken <> '') then
    begin
      method := uppercase(FAutoCompleteList.Strings[i]);

      vp := pos(' ', method);
      if vp > 0 then
        Delete(method, 1, vp);

      vp := pos('.', method);
      if vp > 0 then
        Delete(method, 1, vp);

      tp := pos(AToken, method);
      if ((tp = 1) and FAutoCompletion.FromFirstChar) or ((tp > 0) and not FAutoCompletion.FromFirstChar) then
      begin
        if (FListBox.Items.IndexOf(FAutoCompleteList.Strings[i]) = -1) then
        begin
          FListBox.Items.AddObject(FAutoCompleteList.Strings[i], FAutoCompleteList.Objects[i]);
          if j = -1 then
            j := FListBox.Items.Count - 1;
        end;
      end;
    end
    else
    begin
      if (FListBox.Items.IndexOf(FAutoCompleteList.Strings[i]) = -1) then
        FListBox.Items.AddObject(FAutoCompleteList.Strings[i], FAutoCompleteList.Objects[i]);
    end;
  end;

  if FListBox.Items.Count > 0 then
  begin
    if j > -1 then
      FListBox.ItemIndex := j
    else
      FListBox.ItemIndex := 0;
  end;
end;

procedure TTMSFMXMemo.UpdateMemo;
var
  bkg: TRectangle;
begin
  Repaint;
  bkg := GetMemoRect;
  if Assigned(bkg) then
    bkg.Repaint;
end;

function TTMSFMXMemo.GetKeyboardType: TVirtualKeyboardType;
begin
  Result := FKeyboardType;
end;

procedure TTMSFMXMemo.SetKeyboardType(Value: TVirtualKeyboardType);
begin
  FKeyboardType := Value;
end;

function TTMSFMXMemo.EditorRect: TRectF;
var
  g: TRectangle;
  vs, hs: TScrollBar;
begin
  g := GetGutter;
  if Assigned(g) and g.Visible then
    Result := RectF(g.Width + 1, 0, Width - g.Width, Height)
  else
    Result := RectF(0, 0, Width, Height);

  vs := GetVScroll;
  if Assigned(vs) then
    Result.Right := Result.Right - vs.Width;

  hs := GetHScroll;
  if Assigned(hs) then
    Result.Bottom := Result.Bottom - hs.Height;
end;

procedure TTMSFMXMemo.EndBlockUpdate;
begin
  Dec(FUpdateCount);
  if FUpdateCount = 0 then
    UpdateMemo;
end;

{$IFDEF ANDROID}
procedure TTMSFMXMemo.EndIMEInput;
begin
  FImeText := '';
end;
{$ENDIF}

function TTMSFMXMemo.LineRect(ARow: integer): TRectF;
var
  rct: TRectF;
begin
  rct := EditorRect;
  ARow := ARow - FTopLine;
  with FCellSize do
    Result := RectF(rct.Left, rct.Top + H * ARow, rct.Right, rct.Top + H * (ARow + 1));
end;

function TTMSFMXMemo.CellRect(ACol, ARow: integer): TRectF;
var
  rct: TRectF;
begin
  rct := EditorRect;
  with FCellSize do
    Result := RectF(rct.Left + W * ACol, rct.Top + H * ARow + 1,
      rct.Left + W * (ACol + 1), rct.Top + H * (ARow + 1) + 1);
end;

function TTMSFMXMemo.TrimRightWW(LineNo: Integer): string;
begin
  Result := TrimRight(InternalLines[LineNo]);
end;

function TTMSFMXMemo.CharFromPos(X, Y: Single): TFullPos;
var
  rct: TRectF;
begin
  rct := EditorRect;
  rct.Left := 0;

  if (FCellSize.H = 0) and Assigned(FFont) then SetFont(FFont);
  if (FCellSize.W <> 0) and (FCellSize.H <> 0) then
  begin
    Result.Pos := (X - rct.Left) / FCellSize.W + FLeftCol;
    Result.LineNo := (Y - rct.Top) / FCellSize.H + FTopLine;
  end
  else
  begin
    Result.Pos := 1;
    Result.LineNo := 1;
  end;
end;

procedure TTMSFMXMemo.CheckCodeInsightChar(Key: Char);
begin
  if IsCompletionListActivationChar(Key) then
    PerformAutoComplete;
end;

function TTMSFMXMemo.CellFromPos(X, Y: Single): TCellPos;
var
  rct: TRectF;
begin
  rct := EditorRect;
  rct.Left := 0;
  if (FCellSize.H = 0) and Assigned(FFont) then
    SetFont(FFont);
  if (FCellSize.W <> 0) and (FCellSize.H <> 0) then
  begin
    Result.X := Round((X - rct.Left + (FCellSize.W / 2)) / FCellSize.W);
    Result.Y := Round((Y - rct.Top) / FCellSize.H);
  end
  else
  begin
    Result.X := 0;
    Result.Y := 0;
  end;
end;

procedure TTMSFMXMemo.TabLine(AShift: TShiftState);
var
  S1, S2, sIndent: string;
  strOrigLine: string;
  OldX, OldY: integer;
  first_nonblank: integer;

  function GetTabIndent: string;
  var
    // use for smarttabs
    MinLen, iLine: integer;
    PrevLine, Spaces: string;
    stFound: boolean;
    nDistanceToTab: Integer;

  begin //get the number of spaces to indent the
    MinLen := CurX + 1;
    first_nonblank := MinLen;
    iLine := CurY - 1;

    stFound := false;
    if (iLine >= 0) and (iLine < InternalLines.Count) then
    begin
      repeat
        //locate a prev line to get tab
        PrevLine := InternalLines[iLine] + #13#10;

        if (Length(PrevLine) >= MinLen) then
        begin
          first_nonblank := MinLen;
          //first go until we find a blank
          while first_nonblank < Length(PrevLine) do
          begin
            if (PrevLine[first_nonblank] = #32) or (PrevLine[first_nonblank] = #9) or
               (PrevLine[first_nonblank] = #13) or (PrevLine[first_nonblank] = #10) then
            begin
              break;
            end
            else
              inc(first_nonblank);
          end;

          //now go till we find a non blank
          while first_nonblank <= Length(PrevLine) do
          begin
            if not ( (PrevLine[first_nonblank] = #32) or (PrevLine[first_nonblank] = #9) ) then
            begin
              stFound := true;
              break;
            end
            else
              inc(first_nonblank);
          end;

          if stFound then
            break; //found the point
        end;

        Dec(iLine);
      until
        iLine < 0;
    end;

    //  now actually use the tab
    dec(first_nonblank);

    nDistanceToTab := first_nonblank - CurX;

    if nDistanceToTab > 0 then
      Spaces := Spaces + StringOfChar(#32, nDistanceToTab)
    else
    begin
      //no smart tab found so do normal tab
      Spaces := '';
    end;

    Result := Spaces;
  end;

begin
  if FSmartTabs and (InternalLines.Count > 0) then
  begin
    FLetRefresh := False;

    OldX := CurX;
    OldY := CurY;

    strOrigLine := InternalLines[CurY];
    sIndent := GetTabIndent; //figure out how many spaces to indent over

    if Length(sIndent) = 0 then
    begin
      InsertChar(#9);
      Repaint;
    end
    else
    begin
      S1 := Copy(strOrigLine, 1, CurX);
      S2 := TrimRight(Copy(strOrigLine, CurX + 1, Length(strOrigLine)));

      InternalLines[CurY] := S1 + sIndent + S2;
      CurX := first_nonblank;
    end;

    InternalUndoList.Add(TInsertCharUndo.Create(OldX, OldY, CurX, CurY, #13 + sIndent));
    FLetRefresh := True;
    LinesChanged(Self);
  end
  else
  begin //no smarttabs
    if not (ssCtrl in AShift) then
    begin
      if ssShift in AShift then
      begin
        if CurX > TabSize
          then CurX := CurX - TabSize
        else CurX := 0;
      end
      else
      begin
        InsertChar(#9);
        Repaint;
      end;
    end;
  end;
end;

procedure TTMSFMXMemo.TextFromPos(X, Y: Integer; var TextPos: Integer);
var
  i, l: Integer;
begin
  l := 0;

  for i := 0 to Y - 1 do
  begin
    if i < InternalLines.Count then
      l := l + Length(InternalLines[i]) + 2;
  end;
  if Y < InternalLines.Count then
  begin
    if X < Length(InternalLines[Y]) then
      l := l + X
    else
      l := l + Length(InternalLines[Y]);
  end;

  TextPos := l;
end;

function TTMSFMXMemo.TokenAtXY(X, Y: Integer): String;
var
  i: integer;
  s: string;
  fe, fb: integer;
begin
  Result := '';

  if (Y >= InternalLines.Count) or (Y < 0) then
    Exit;

  s := InternalLines.Strings[Y];

  if (X > Length(s)) then
    Exit;

  fe := X;
  fb := X + 1;

  {$IFDEF DELPHI_LLVM}
  for i := X to Length(s)-1 do
  {$ELSE}
  for i := X + 1 to Length(s) do
  {$ENDIF}
  begin
    if (i > 0) and not IsTokenBoundary(s[i]) then
      fe := i
    else
      Break;
  end;

  {$IFDEF DELPHI_LLVM}
  for i := X - 1 downto 0 do
  {$ELSE}
  for i := X downto 1 do
  {$ENDIF}
  begin
    if not IsTokenBoundary(s[i]) then
      fb := i
    else
      Break;
  end;

  Result := Copy(s, fb, fe - fb + 1);
end;

procedure TTMSFMXMemo.GoEnd(Shift: TShiftState);
var
  Selecting: boolean;
begin
  if ssCtrl in Shift then
  begin
    CurY := InternalLines.Count - 1;
    CurX := Length(InternalLines[InternalLines.Count - 1]);
  end;

  Selecting := (ssShift in Shift) and (CurX = FPrevSelX) and (CurY = FPrevSelY);

  CurX := Length(TrimRightWW(CurY));

  if Selecting then
    ExpandSelection
  else
    ClearSelection;
end;

procedure TTMSFMXMemo.GoHome(Shift: TShiftState);
var
  Selecting: boolean;
  S: string;
  first_nonblank: integer;
begin
  if (ssCtrl in Shift) then
  begin
    CurX := 0;
    CurY := 0;
  end;

  Selecting := (ssShift in Shift) and (CurX = FPrevSelX) and (CurY = FPrevSelY);


  if FEnhancedHomeKey then
  begin //using enhanced home key
    s := InternalLines[CurY];
    first_nonblank := CurX - 1;
    if first_nonblank > length(s) then
      first_nonblank := length(s);
    while (first_nonblank > 0) do
    begin
      if (s[first_nonblank] = #32) then
      begin //found a space
        if s[first_nonblank + 1] = #32 then
        begin //next char is a space
          dec(first_nonblank);
        end
        else
        begin //next char is NOT a space
          break;
        end;
      end
      else
        dec(first_nonblank);
    end;
    CurX := first_nonblank;
    if CurX < FLeftCol then
      FLeftCol := CurX;
  end
  else
  begin
    CurX := 0;
    FLeftCol := 0;
  end;

  if Selecting then
    ExpandSelection
  else
    ClearSelection;
end;

procedure TTMSFMXMemo.InsertChar(C: char);
var
  S, S1: string;
  NewPlace: integer;
  ovw: boolean;
  CurX0, CurY0: integer;
begin
  BlockUpdate;
  {$IFDEF ANDROID}
  FImeText := FImeText + C;
  {$ENDIF}
  if (curY >= InternalLines.Count) then
    curY := curY;

  if InternalLines.Count = 0 then
    InternalLines.Add('');

  CurX0 := CurX;
  CurY0 := CurY;
  S := InternalLines[CurY];
  S1 := '';

  if C = #9 then
  begin
    if FOverwrite then
    begin
      if CurX + TabSize < Length(InternalLines[CurY]) then
        CurX := CurX + TabSize
      else
      begin
        S1 := StringOfChar(' ', TabSize - (Length(InternalLines[CurY]) - CurX));
        CurX := Length(InternalLines[CurY]);
      end;
      C := #0;
    end
    else
      S1 := StringOfChar(' ', TabSize)
  end
  else
  begin
    if CurX > Length(s) then
      S := S + StringOfChar(' ', CurX);
    S1 := C;
  end;

  NewPlace := CurX + length(S1);
  ovw := false;

  {$IFDEF DELPHI_LLVM}
  if FOverwrite then
  begin
    if (length(S) >= CurX + 1) and (C <> #0) then
    begin
      InternalUndoList.Add(TOverwriteCharUndo.Create(CurX0, CurY0, CurX, CurY, S[CurX]));
      S[CurX] := C;
      ovw := true;
    end
    else
      Insert(S1, S, CurX + 1);
  end
  else
    Insert(S1, S, CurX + 1);

  {$ELSE}
  if FOverwrite then
  begin
    if (length(S) >= CurX + 1) and (C <> #0) then
    begin
      InternalUndoList.Add(TOverwriteCharUndo.Create(CurX0, CurY0, CurX, CurY, S[CurX + 1]));
      S[CurX + 1] := C;
      ovw := true;
    end
    else
      Insert(S1, S, CurX + 1);
  end
  else
    Insert(S1, S, CurX + 1);
  {$ENDIF}

  FLetRefresh := False;

  InternalLines[CurY] := S;

  FLetRefresh := True;

  FCurX := NewPlace;
  MakeVisible;
  ResizeScrollBars;
  ShowCaret(True);

  ClearSelection;
  if not ovw then
    InternalUndoList.Add(TInsertCharUndo.Create(CurX0, CurY0, CurX, CurY, S1));

  EndBlockUpdate;
end;

procedure TTMSFMXMemo.BackSpace;
var
  OldX, OldY: integer;

  function IsFirstNonBlank: boolean;
  var
    iCur: integer;
    CurLine: string;
  begin
    iCur := 0;
    CurLine := InternalLines[CurY];
    if Length(CurLine) = 0 then
    begin
      Result := false;
      Exit;
    end;

    repeat
      if ((CurLine[iCur] = #32) or (CurLine[iCur] = #0)) then
        inc(iCur)
      else
        break;
    until iCur = CurX;
    Result := (iCur = CurX);
  end;

  function GetTabIndent: integer;
  var
    MinLen, iLine: integer;
    PrevLine: string;
    first_nonblank: integer;
    stFound: boolean;
  begin
    MinLen := CurX - 1;
    first_nonblank := MinLen;
    iLine := CurY - 1;
    stFound := false;
    if (iLine > 0) and (iLine < InternalLines.Count) then
    begin
      repeat
        PrevLine := InternalLines[iLine];
        first_nonblank := MinLen;
        if first_nonblank > Length(PrevLine) then
          first_nonblank := Length(PrevLine) + 1;

        while (first_nonblank < Length(PrevLine)) and (first_nonblank > 0) do
        begin
          if not ( (PrevLine[first_nonblank] = #32) or (PrevLine[first_nonblank] = #9)) then
          begin
            stFound := true;
            break;
          end
          else
            dec(first_nonblank);
        end;

        while (first_nonblank < Length(PrevLine)) and (first_nonblank > 0) do
        begin
          if (PrevLine[first_nonblank] = #32) or (PrevLine[first_nonblank] =  #9) then
          begin
            break;
          end
          else
            dec(first_nonblank);
        end;

        if stFound then break;

        Dec(iLine);
      until iLine < 0;
    end;
    Result := first_nonblank;
  end;

begin
  OldX := CurX;
  OldY := CurY;
  MoveCaret(-1, 0, []);

  if (OldX = CurX) and (OldY = CurY) then
    Exit;

  DeleteChar(OldX, OldY);

  if CurY >= InternalLines.Count then
  begin
    CurY := InternalLines.Count -1;
    CurX := Length(InternalLines[CurY]);
  end;

  FSelStartX := CurX;
  FSelStartY := CurY;
  FSelEndX := CurX;
  FSelEndY := CurY;
end;

function TTMSFMXMemo.IndentCurrLine: string;
var
  Len, Count,i: integer;
  CurS: string;
begin
  Result := '';

  if not AutoIndent then
    Exit;

  CurS := InternalLines[CurY];
  Len := Length(CurS);

  Count := 0;

  {$IFDEF DELPHI_LLVM}
  i := 0;
  {$ELSE}
  i := 1;
  {$ENDIF}

  if length(Curs) > 0 then
  begin
    while Curs[i] = ' ' do
    begin
      inc(Count);
      inc(i);
      if i >= Length(Curs) then
        break;
    end;
  end;


  while (Count < CurX) and (Count < Len) do
  begin
    {$IFDEF DELPHI_LLVM}
    if CurS[Count] <> ' ' then break;
    {$ELSE}
    if CurS[Count + 1] <> ' ' then break;
    {$ENDIF}
      Inc(Count);

  end;
  Result := StringOfChar(' ', Count);
end;

procedure TTMSFMXMemo.NewLine;
var
  S, sIndent: string;
  OldX, OldY: integer;
begin
  BlockUpdate;
  FImeText := '';
  FTextService.Text := '';

  FLetRefresh := False;
  if (curY >= InternalLines.Count) then
  begin
    curY := curY;
  end;

  OldX := CurX;
  OldY := CurY;

  if InternalLines.Count = 0 then
    InternalLines.Add('');

  S := InternalLines[CurY];

  sIndent := IndentCurrLine;
  InternalLines[CurY] := Copy(S, 1, CurX);

  S := TrimRight(Copy(S, CurX + 1, Length(S)));

  if AutoIndent then
  begin
    {$IFDEF DELPHI_LLVM}
    while (Length(S) > 0) and (S[0] = ' ') do
    {$ELSE}
    while (Length(S) > 0) and (S[1] = ' ') do
    {$ENDIF}
      Delete(S, 1, 1);
  end;

  TFMXMemoStrings(InternalLines).DoInsert(CurY + 1, sIndent + S);

  MoveCaret(0, 1, []);
  CurX := Length(sIndent);
  ClearSelection;

  if FUndoLineByLine then
    InternalUndoList.Add(TNewLineUndo.Create(OldX, OldY, CurX, CurY, #13 + sIndent))
  else
    InternalUndoList.Add(TInsertCharUndo.Create(OldX, OldY, CurX, CurY, #13 + sIndent));

  Fletrefresh := True;
  EndBlockUpdate;
end;

function TTMSFMXMemo.NextToken(const s: string; StartFrom: integer;
  var Offset: integer): string;
var
  i,ls: integer;
  bnd: boolean;
begin
  Result := '';
  Offset := 0;
  ls := Length(s);

  if StartFrom > ls then
    Exit;

  for i := StartFrom to ls do
  begin
    bnd := IsTokenBoundary(s[i]);

    if (Result = '') and bnd then
    begin
      inc(Offset);
      Continue;
    end
    else
      if not bnd then
      begin
        Result := Result + s[i];
        inc(Offset);
      end
      else
        if bnd and (Result <> '') then
          break;

  end;
end;

procedure TTMSFMXMemo.DblClick;
begin
  inherited;
  FDblClicked := True;
  SelectWordAtCursor;
end;

procedure TTMSFMXMemo.DeleteChar(OldX, OldY: integer);
var
  S: string;
  C: char;
  Undo: TDeleteCharUndo;
  IsBackspace: boolean;
  CurLnIdx: Integer;
  oldperf: boolean;
begin
  BlockUpdate;

  Fletrefresh := False;

  if OldX < 0 then
  begin
    OldX := CurX;
    OldY := CurY;
    IsBackspace := False;
  end
  else
    IsBackspace := True;

  ClearSelection;

  CurLnIdx := CurY;

  if (CurX < Length(InternalLines[CurLnIdx])) then
  begin
    S := InternalLines[CurLnIdx];
    {$IFDEF DELPHI_LLVM}
    C := S[CurX];
    {$ELSE}
    C := S[CurX + 1];
    {$ENDIF}
    Delete(S, CurX + 1, 1);
    InternalLines[CurLnIdx] := S;

    Undo := TDeleteCharUndo.Create(OldX, OldY, CurX, CurLnIdx, C);
    Undo.IsBackSpace := IsBackSpace;
    InternalUndoList.Add(Undo);
  end
  else if CurY < InternalLines.Count - 1 then
  begin
    S := InternalLines[CurY] + InternalLines[CurY + 1];

    InternalLines[CurY] := S;
    InternalLines[CurY + 1] := '';

    oldperf := InternalUndoList.IsPerforming;
    InternalUndoList.IsPerforming := true;

    FInternalDelete := True;
    InternalLines.Delete(CurY + 1);
    FInternalDelete := False;

    InternalUndoList.IsPerforming := oldperf;

    Undo := TDeleteCharUndo.Create(OldX, OldY, CurX, CurY, #13);
    Undo.IsBackSpace := IsBackSpace;
    InternalUndoList.Add(Undo);
  end;

  Fselstartx := CurX;
  Fselstarty := CurY;
  FSelEndX := CurX;
  FSelEndY := CurY;

  Fletrefresh := True;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.MovePage(dP: integer; Shift: TShiftState);
var
  eRect: TRectF;
  LinesPerPage: integer;
  Selecting: boolean;
  tr: integer;

begin
  if FCellSize.H = 0 then
    Exit;

  tr := TopLine;

  Selecting := (ssShift in Shift) and (CurX = FPrevSelX) and (CurY = FPrevSelY);

  eRect := EditorRect;
  LinesPerPage := Round(eRect.Bottom - eRect.Top) div Round(FCellSize.H);

  if InternalLines.Count > LinesPerPage then
  begin
   CurY := CurY + dP * LinesPerPage;
   TopLine := tr + dP * LinesPerPage;
  end
  else
  begin
   if dP > 0 then
     CurY := InternalLines.Count - 1
   else
     CurY := 0;
  end;

  if TopLine > tr then
    ClearLineStylesFromTo(tr, TopLine)
  else
    ClearLineStylesFromTo(TopLine, tr);


  if ssCtrl in Shift then
    if dP > 0 then
    begin
      CurY := InternalLines.Count - 1;
      CurX := Length(InternalLines[InternalLines.Count - 1]);
    end
    else
    begin
      CurY := 0;
      CurX := 0;
    end;

  if Selecting then
    ExpandSelection
  else
    ClearSelection;
end;

procedure TTMSFMXMemo.DoCommand(cmd: TCommand; const AShift: TShiftState);
var
  Allow, SkipIns: Boolean;
  OldCurY, OldCurX: Integer;
  ae: boolean;
begin
  BlockUpdate;
  case cmd of
    cmCopy: DoCopyToClipboard;
    cmInsert:
      begin
        if (ssCtrl in AShift) then
          DoCopyToClipboard;
      end;
    cmHome:
      begin
        if InternalLines.Count = 0 then
        begin
          CurX := 0;
          Exit;
        end;

        OldCurY := FSelStartY;
        OldCurX := FSelStartX;

        GoHome(AShift);
        if (ssCtrl in AShift) and (ssShift in AShift) then
        begin
          FSelStartY := OldCurY;
          FSelStartX := OldCurX;
          UpdateMemo;
        end;
      end;
    cmEnd:
      begin
        if InternalLines.Count = 0 then
        begin
          CurX := 0;
          Exit;
        end;

        OldCurY := FSelStartY;
        OldCurX := FSelStartX;

        GoEnd(AShift);
        if (ssCtrl in AShift) and (ssShift in AShift) then
        begin
          FSelStartY := OldCurY;
          FSelStartX := OldCurX;
          UpdateMemo;
        end;
      end;
    cmPageDown: MovePage(1, AShift);
    cmPageUp: MovePage(-1, AShift);
  end;

  case cmd of
    cmDelete:
      begin
        if InternalLines.Count = 0 then
          Exit;

        if ssShift in AShift then
          CutToClipboard
        else
          if FDelErase and
             (not ((FSelStartX = FSelEndX) and (FSelStartY = FSelEndY))) then
          begin
            ae := AutoExpand;
            AutoExpand := true;
            DeleteSelectionInt;
            AutoExpand := ae;
          end
          else
            DeleteChar(-1, -1);

         FPrevSelX := CurX;
         FPrevSelY := CurY;
      end;
    cmBackSpace:
      begin
        if InternalLines.Count = 0 then
          Exit;

        if ssCtrl in AShift then
        begin
          BackWord;
        end
        else
        begin
          if FDelErase and
            (not ((FSelStartX = FSelEndX) and (FSelStartY = FSelEndY))) then
            DeleteSelectionInt
          else
            BackSpace;
        end;
      end;
    cmNewLine:
      begin
        DeleteSelectionInt;
        NewLine;
      end;
    cmSelectAll: SelectAll;
    cmCut: CutToClipboard;
    cmPaste: PasteFromClipboard;
    cmInsert:
      begin
        if ssShift in AShift then
          PasteFromClipboard
        else
          if not (ssCtrl in AShift) then
          begin
            Allow := True;
            DoOverwriteToggle(Allow);
            if Allow then
            begin
              if InternalUndoList.Count > 0 then
              begin
                SkipIns := false;
                if  InternalUndoList.Count > 1 then
                begin
                  SkipIns := (InternalUndoList.Items[InternalUndoList.Count - 1] is TInsertOverWriteUndo) and
                     (InternalUndoList.Items[InternalUndoList.Count - 2] is TInsertOverWriteUndo);
                end;
                if not SkipIns then
                  InternalUndoList.Add(TInsertOverwriteUndo.Create(FOverwrite));
              end;

              FOverwrite := not FOverwrite;
            end;
          end;
      end;
    cmTab:
      begin
        TabLine(AShift);
        //LineModifiedInt[CurY] := lmModified;
      end;
  end;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.DoContextMenuItemClick(Menu: TPopupMenu; Item: TMenuItem);
begin
  if Assigned(OnContextMenuItemClick) then
    OnContextMenuItemClick(Self, FMemoContext, Item);
end;

procedure TTMSFMXMemo.BackWord;
var
  OldX, OldY: Integer;
  s: string;
  ch: char;
begin
  s := InternalLines[CurY];
  repeat
    OldX := CurX;
    OldY := CurY;

    MoveCaret(-1, 0, []);

    if (OldX = CurX) and (OldY = CurY) then
      Exit;

    DeleteChar(OldX, OldY);

    if (CurX <= length(s)) and (CurX > 0) then
    {$IFDEF DELPHI_LLVM}
      ch := s[CurX - 1]
    {$ELSE}
      ch := s[CurX]
    {$ENDIF}
    else
      ch := ' ';

  until (OldX = CurX) or (ch = ' ') or (CurY <> OldY);
end;

function TTMSFMXMemo.BeforeAutoCompletion(AToken: String): boolean;
begin
  Result := true;
  if Assigned(OnBeforeAutoCompletion) then
    OnBeforeAutoCompletion(Self, AToken, Result);
end;

procedure TTMSFMXMemo.BlockIndent(FromLine, ToLine, Indent: Integer;
  AllowUndo: boolean);
var
  i,j: Integer;
  s:string;
  bi: TIndentUndo;
begin
  if FromLine > ToLine then
    Swap(FromLine, ToLine);

  FLetRefresh := False;

  for i := FromLine to ToLine do
  begin
    if (i < Lines.Count) then
    begin
      s := Lines[i];

      if Indent > 0 then
      begin
        Lines[i] := StringOfChar(' ',Indent) + s;
      end
      else
      begin
        j := 0;
        while (j < -Indent) and (length(s) > 0) do
        begin
          if s[1] = ' ' then
            delete(s,1,1);
          inc(j);
        end;
        Lines[i] := s;
      end;
    end;
  end;

  if AllowUndo then
  begin
    bi := TIndentUndo.Create(FromLine,ToLine,Indent);
    UndoList.Add(bi);
  end;

  FLetRefresh := true;
  Repaint;
end;

procedure TTMSFMXMemo.BlockUpdate;
begin
  Inc(FUpdateCount);
end;

function TTMSFMXMemo.CaretVisible: Boolean;
begin
  Result := (Caret <> nil) and Caret.Visible;
end;

function TTMSFMXMemo.GetActiveLineBitmap: TBitmap;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('activelinebitmap');
  if Assigned(obj) and (obj is TImage) then
    Result := (obj as TImage).Bitmap;
end;

function TTMSFMXMemo.GetActiveLineColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('activelinecolor');
  if Assigned(obj) and (obj is TRectangle) then
  begin
    Result := (obj as TRectangle).Fill.Color;
  end;
end;

function TTMSFMXMemo.GetActiveLineTextColor: TAlphaColor;
var
  obj: TFMXObject;
begin
  Result := TAlphaColorRec.Null;

  obj := FindStyleResource('activelinetextcolor');
  if Assigned(obj) and (obj is TText) then
  begin
    Result := (obj as TText).Color;
  end;

end;

function TTMSFMXMemo.GetAutoCompletionFont: TLabel;
var
  obj: TFmxObject;
begin
  Result := nil;
  obj := FindStyleResource('autocompletionfont');
  if Assigned(obj) and (obj is TLabel) then
    Result := (obj as TLabel);
end;

procedure TTMSFMXMemo.GetAutoCompletionList(AToken: String; AList: TStringList);
var
  i, vp: integer;
  method: string;
begin
  if (Assigned(InternalStyles)) and (UseStyler) then
  begin
    for i := 1 to InternalStyles.FlistAuto.Count do
    begin
      method := UpperCase(InternalStyles.FlistAuto.Strings[i - 1]);
      vp := pos(' ', method);
      if (vp > 0) then
          Delete(method, 1, vp);

      if (pos(AToken, method) = 1) or (AToken = '') then
      begin
        AList.Add(InternalStyles.FlistAuto.Strings[i - 1]);
      end;
    end;
  end;


  if Assigned(FOnGetAutoCompletionList) then
    FOnGetAutoCompletionList(Self, AToken, AList);
end;

procedure TTMSFMXMemo.ShowCaretProc;
begin
  Caret.Visible := True;
end;

{$IFDEF ANDROID}
procedure TTMSFMXMemo.StartIMEInput;
begin
  FImeText := '';
end;
{$ENDIF}

procedure TTMSFMXMemo.HideCaret;
begin
  Caret.Visible := False;
end;

function TTMSFMXMemo.GetCaretX: Single;
begin
  Result := -1;
  if (Caret <> nil) then
  begin
    Result := Caret.Pos.X;
  end;
end;

procedure TTMSFMXMemo.SetCaretX(const Value: Single);
begin
  if (Caret <> nil) then
  begin
    Caret.Pos := PointF(Value, Caret.Pos.Y);
  end;
end;

procedure TTMSFMXMemo.SetCaretY(const Value: Single);
begin
  if (Caret <> nil) then
  begin
    Caret.Pos := PointF(Caret.Pos.X, Value);
  end;
end;

function TTMSFMXMemo.GetCaretY: Single;
begin
  Result := -1;
  if (Caret <> nil) then
  begin
    Result := Caret.Pos.Y;
  end;
end;

function TTMSFMXMemo.GetContextMenuItem(Command: integer): TMenuItem;
var
  i: integer;

  function FindMenuItem(Cmd: integer; MenuItem: TMenuItem): TMenuItem;
  var
    j: integer;
  begin
    Result := nil;
    if MenuItem.Tag = Cmd then
    begin
      Result := MenuItem;
      Exit;
    end;

    for j := 0 to MenuItem.ItemsCount - 1 do
    begin
      Result := FindMenuItem(Cmd, MenuItem.Items[j]);
      if Assigned(Result) then
        Exit;
    end;
  end;

begin
  Result := nil;

  for i := 0 to FMemoContext.ItemsCount - 1 do
  begin
    Result := FindMenuItem(Command, TMenuItem(FMemoContext.Items[i]));
    if Assigned(Result) then
      Break;
  end;
end;

procedure TTMSFMXMemo.ShowCaret(Value: boolean);
var
  rct: TRectF;
begin
  if not Value then
  begin
    if (Caret <> nil) and Caret.Visible then
      HideCaret;

    FCaretVisible := False;
  end
  else if IsFocused then
  begin
    rct := CellRect(CurX - FLeftCol, CurY - FTopLine);

    if (CaretX <> rct.Left) or (CaretY <> rct.Top + 1) then
    begin
      FTextService.CaretPosition := Point(Round(rct.Left), Round(rct.Top + 1));
      if Assigned(Caret) then
      begin
        Caret.Color := TAlphaColorRec.Black;
        Caret.Size := PointF(1, FCellSize.H);
        Caret.Pos := PointF(rct.Left, rct.Top + 1);
      end;
    end;

    if not Assigned(Caret) or (not Caret.Visible) then
      ShowCaretProc;

    FCaretVisible := True;
  end;
end;

procedure TTMSFMXMemo.DoPopup(AUpdate: Boolean = False);
var
  H: single;
  Num, Count, i: Integer;
  Item: TListBoxItem;
  m: TRectangle;
  p, pl: TPointF;
  s: String;
  lstit: TListBoxItem;

begin
  if not AutoCompletion.Active then
    Exit;

  if not FPopup.IsOpen or (AUpdate and FPopup.IsOpen) then
  begin
    FDroppedDown := True;
    m := GetMemoRect;
    if Assigned(m) then
      FPopup.PlacementTarget := m;

    FListBox.Items.Clear;

    if AUpdate then
    begin
      FDotPoint := Point(CurX, CurY);
      s := UpperCase(TokenAtXY(CurX, CurY));

      if not BeforeAutoCompletion(s) then
      begin
        FPopup.IsOpen := False;
        Exit;
      end;

      UpdateCompletionList(s);
    end
    else
    begin
      s := UpperCase(FullWordAtCursor);
      FAutoCompleteList.Clear;

      if not BeforeAutoCompletion(s) then
      begin
        FPopup.IsOpen := False;
        Exit;
      end;

      GetAutoCompletionList(s, FAutoCompleteList);

      FListBox.Items.Assign(FAutoCompleteList);
      FListBox.ItemIndex := 0;
    end;

    if (FListBox.Count = 0) or (FAutoCompleteList.Count = 0) then
    begin
      FPopup.IsOpen := False;
      Exit;
    end;

    for I := 0 to FListBox.Content.ChildrenCount - 1 do
    begin
      if FListBox.Content.Children[I] is TListBoxItem then
      begin
        lstit := (FListBox.Content.Children[I] as TListBoxItem);
        lstit.OnApplyStyleLookup := ListBoxItemApplyStyleLookup;
      end;
    end;

    pl := Self.Position.Point;
    p := TPointF(pl);
    p := LocalToScreen(p);

    FPopup.PlacementRectangle.Left := p.X + CaretX - pl.X + Caret.Size.Width;
    FPopup.PlacementRectangle.Top := p.Y + CaretY - pl.Y + Caret.Size.Height;

    Count := AutoCompletion.AutoCompletionCount;
    if Count = -1 then
      FPopup.Height := AutoCompletion.Height
    else
    begin
      if FListBox.Count < Count then
        Count := FListBox.Count;

      H := 0;
      Num := 0;
      for i := 0 to FListbox.Count - 1 do
      begin
        Item := FListbox.ListItems[i];
        if Item.Position.Y >= 0 then
        begin
          H := H + Item.Height;
          Num := Num + 1;
        end;
        if Num >= Count then Break;
      end;
      FPopup.Height := H + 4;
    end;

    if AutoCompletion.AutoWidth then
    begin
      FPopup.Width := FListBox.AutoAdaptWidth;
      if AutoCompletion.MaxWidth > 0 then
        FPopup.Width := Min(FListBox.AutoAdaptWidth, AutoCompletion.MaxWidth);
    end
    else
      FPopup.Width := AutoCompletion.Width;

    FPopup.IsOpen := True;
    if FPopup.IsOpen then
      FListBox.SetFocus;
  end
  else
  begin
    FPopup.IsOpen := False;
    SetFocus;
  end;
end;

procedure TTMSFMXMemo.DoShowContextMenu(Menu: TPopupMenu);
begin
  if Assigned(OnShowContextMenu) then
    OnShowContextMenu(Self, Menu);
end;

procedure TTMSFMXMemo.MoveCaret(dX, dY: integer; Shift: TShiftState);
var
  Selecting: Boolean;
  S: string;
  ll: integer;

  procedure MoveWordLeft;
  begin
    FLetRefresh := false;
    CurX := CurX - 1;

    S := TrimRightWW(CurY);
    while (CurX > 0) and (CurX <= Length(s)) do
    begin
      {$IFDEF DELPHI_LLVM}
      if IsDelimiter(S[CurX - 1]) and (S[CurX] <> ' ') then
      {$ELSE}
      if IsDelimiter(S[CurX]) and (S[CurX + 1] <> ' ') then
      {$ENDIF}
        Break;

      CurX := CurX - 1;
    end;
    if (CurX < 0) then
    begin
      if CurY > 0 then
      begin
        CurY := CurY - 1;
        CurX := Length(InternalLines[CurY]);
      end;
    end;
    FLetRefresh := true;
  end;

  procedure MoveWordRight;
  var
    Len: Integer;
  begin
    FLetRefresh := false;
    S := TrimRightWW(CurY);
    Len := Length(S);
    CurX := CurX + 1;
    while CurX < Len do
    begin
      {$IFDEF DELPHI_LLVM}
      if IsDelimiter(S[CurX - 1]) and (S[CurX] <> ' ') then
      {$ELSE}
      if IsDelimiter(S[CurX]) and (S[CurX + 1] <> ' ') then
      {$ENDIF}
        Break;
      CurX := CurX + 1;
    end;
    if CurX > Len then
    begin
      if CurY < InternalLines.Count - 1 then
      begin
        CurY := CurY + 1;
        CurX := 0;
      end;
    end;
    FLetRefresh := true;
  end;

begin
  BlockUpdate;
  Selecting := (ssShift in Shift) and (CurX = FPrevSelX) and (CurY = FPrevSelY);

  if ssCtrl in Shift then
  begin
    if dX > 0 then MoveWordRight;
    if dX < 0 then MoveWordLeft;
  end
  else
  begin
    CurY := CurY + dY;

    if (ssShift in Shift) and SelectSingleLine and not AutoExpand then
    begin
      CurX := Max(0, Min(Length(InternalLines[CurY]), CurX + dX));
      if FSelEndY <> FSelStartY then
        FSelStartY := FSelEndY;
    end
    else
    begin
      if not AutoExpand and (dx >= 0) and (dy = 0) then
      begin
        S := TrimRightWW(CurY);
        ll := Length(s);
        if ll = CurX then
        begin
          CurX := 0;
          CurY := CurY + 1;
        end
        else
          CurX := FCurX + dX;
      end
      else
      begin
        if (dx < 0) then
          CurX := CurX + dX
        else
        begin
          CurX := FCurX + dX;

          if (dX <> 0) or (FCurX = 0) then
          begin
            MakeVisible;
            ResizeScrollBars;
            ShowCaret(True);
          end;
        end;
      end;
    end;

    if (dy <> 0) and (dx = 0) and not AutoExpand then
    begin
      S := TrimRightWW(CurY);
      ll := Length(s);
      if ll < CurX then
      begin
        CurX := ll;
      end;
    end;
  end;

  if Selecting then
    ExpandSelection
  else
    ClearSelection;

  InitScrollbars;
  EndBlockUpdate;
end;

procedure TTMSFMXMemo.MakeVisible;
var
  Modified: boolean;
  i, LastVisLine: Integer;
  eRect, R: TRectF;
  Coord: TFullPos;
begin
  Modified := False;
  if CurX < FLeftCol then
  begin
    FLeftCol := CurX - 2;
    if FLeftCol < 0 then FLeftCol := 0;
    Modified := True;
  end;

  if CurX > LastVisiblePos then
  begin
    FLeftCol := FLeftCol + CurX - LastVisiblePos + 2;
    if FLeftCol < 0 then
      FLeftCol := 0;

    Modified := True;
  end;

  if CurY < FTopLine then
  begin
    FTopLine := CurY;
    if FTopLine < 0 then
      FTopLine := 0;
    Modified := True;
  end;

  eRect := EditorRect;
  Coord := CharFromPos(eRect.Right - 1, eRect.Bottom - 1);
  LastVisLine := Round(Coord.LineNo - 1);

  R := LineRect(Round(Coord.LineNo));

  if (R.Bottom > eRect.Bottom + GetHorzScrollHeight - 3) then
    i := 1
  else
    i := 0;

  if CurY > LastVisLine - i then
  begin
    FTopLine := FTopLine + CurY - LastVisLine + i;
    Modified := True;
  end;

  if Modified then
  begin
    UpdateMemo;
  end;
end;

procedure TTMSFMXMemo.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
var
  p: TPointF;
  newPos: TCellPos;
  s: string;
  mnu: TMenuItem;
begin
  inherited;

  if Button = TMouseButton.mbRight then
  begin
    if not Assigned(PopupMenu) then
    begin
      p := LocalToAbsolute(PointF(X, Y));
      p := Scene.LocalToScreen(p);

      mnu := GetContextMenuItem(menuCut);
      if Assigned(mnu) then mnu.Enabled := CanCut;
      mnu := GetContextMenuItem(menuCopy);
      if Assigned(mnu) then mnu.Enabled := CanCopy;
      mnu := GetContextMenuItem(menuUndo);
      if Assigned(mnu) then mnu.Enabled := CanUndo;
      mnu := GetContextMenuItem(menuRedo);
      if Assigned(mnu) then mnu.Enabled := CanRedo;

      DoShowContextMenu(FMemoContext);

      FMemoContext.Popup(p.X, p.Y);
    end;

    Exit;
  end;

  if not IsFocused then
    SetFocus;

  newPos := CellFromPos(X - GetGutterWidth, Y);

  if newPos.x < 0 then
    newPos.x := 0
  else
    newPos.x := newPos.x + FLeftCol;

  if newPos.y < 0 then
    newPos.y := 0
  else
    newPos.y := newPos.y + FTopLine;

  if (newPos.X >= 0) and (newPos.Y >= 0) then
  begin
    s := WordAtXY(newPos.X,newPos.Y);

    if URLAware and (s <> '') and WordIsURL(s) then
    begin
      DoAnchorClick(s,newPos.X,newPos.Y);
      Exit;
    end;
  end;

  Capture;

  SelClickUpdate(X - GetGutterWidth, Y, True, Shift, Button);
end;

procedure TTMSFMXMemo.MouseMove(Shift: TShiftState; X, Y: Single);
var
  newPos: TCellPos;
  oldSx, oldSy, oldEy, oldEx,newss: integer;
  s:string;
begin
  inherited;

  if FSelClick then
  begin
    FSelClick := false;
    Exit;
  end;

  newPos := CellFromPos(X - GetGutterWidth, Y);

  if newPos.x < 0 then
    newPos.x := 0
  else
    newPos.x := newPos.x + FLeftCol;

  if newPos.y < 0 then
    newPos.y := 0
  else
    newPos.y := newPos.y + FTopLine;


  if (newPos.X >= 0) and (newPos.Y >= 0) then
  begin
    s := WordAtXY(newPos.X,newPos.Y);

    if URLAware and (s <> '') and WordIsURL(s) then
      Cursor := crHandPoint
    else
      Cursor := crIBeam;
  end;


  if newPos.Y < 0 then newPos.y := 0
  else
    newPos.y := newPos.y + FTopLine;

  TextFromPos(Round(newpos.X), Round(newpos.Y), newss);

  oldSx := FSelStartX;
  oldSy := FSelStartY;
  oldEx := FSelEndX;
  oldEY := FSelEndY;

  if (ssLeft in Shift) and FLeftButtonDown and not FSelButtonDown and not FSelClick then
  begin
    newPos := CellFromPos(X - GetGutterWidth, Y);

    CurY := Round(newPos.Y) + FTopLine;
    if newPos.x < 0 then
    begin
      curx := 0;
      FLeftCol := 0;
    end
    else
    begin
      if cury <= (InternalLines.Count - 1) then
        CurX := min(Length(InternalLines[cury]), Round(newPos.X) + FLeftCol)
      else
        CurX := Round(newPos.X) + FLeftCol;
    end;

    ExpandSelection;

    FSelChange := true;

    if ((oldSx < FSelStartX + 2) or
      (oldSy < FSelStartY + 2) or
      (oldEx > FSelEndX + 2) or
      (oldEY > FSelEndY + 2)) then UpdateMemo;

    FSelChange := false;
  end;
end;

procedure TTMSFMXMemo.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  ReleaseCapture;
  if FDblClicked then
  begin
    FDblClicked := False;
    Exit;
  end;

  if Button = TMouseButton.mbLeft then
    ShowCaret(True);

  FLeftButtonDown := False;

  if FSelButtonDown then
  begin
    BlockUpdate;
    ClearSelection;
    FSelButtonDown := False;
    SelClickUpdate(X - GetGutterWidth, Y, false, Shift, Button);
    EndBlockUpdate;
  end;
end;

procedure TTMSFMXMemo.KeyDown(var Key: Word; var KeyChar: System.WideChar;
  Shift: TShiftState);
var
  wrd, s: string;
begin
  if FReadOnly then
    Exit;

//  inherited;

  {$IFDEF MACOS}
  if (Shift = [ssShift]) and (KeyChar = ' ') then
  begin
    FAutoCompleteDot := False;
    PerformAutoComplete;
    Exit;
  end;
  {$ENDIF}

  if Shift = [ssCtrl] then
  begin
    case Key of
      vkSpace:
        begin
          FAutoCompleteDot := False;
          PerformAutoComplete;
          Exit;
        end;
      Ord('A'):
        begin
          SelectAll;
          KeyChar := #0;
          Exit;
        end;
      Ord('C'):
        begin
          CopyToClipboard;
          KeyChar := #0;
          Exit;
        end;
      Ord('V'):
        begin
          PasteFromClipboard;
          KeyChar := #0;
          Exit;
        end;
      Ord('X'):
        begin
          CutToClipboard;
          KeyChar := #0;
          Exit;
        end;
      Ord('Z'):
        begin
          if (ssctrl in Shift) and not (ssAlt in Shift) then
            Undo;

          KeyChar := #0;
          Exit;
        end;
      ord('Y'):
        begin
          if (ssctrl in Shift) then
          begin
            Redo;
          end;
          KeyChar := #0;
          Exit;
        end;
      end;
    end;

  inherited;

  case Key of
    vkLeft: MoveCaret(-1, 0, Shift);
    vkRight: MoveCaret(1, 0, Shift);
    vkUp:
    begin
      if Popup.IsOpen then
      {$IFNDEF GENERALOSX}
        FListBox.KeyDown(Key, KeyChar, Shift)
      {$ENDIF}
      else
        MoveCaret(0, -1, Shift);
    end;
    vkDown:
    begin
      if Popup.IsOpen then
      {$IFNDEF GENERALOSX}
        FListBox.KeyDown(Key, KeyChar, Shift)
      {$ENDIF}
      else
        MoveCaret(0, 1, Shift);
    end;
    vkEscape:
    begin
      if Popup.IsOpen then
      begin
        DoPopup;
        if Assigned(OnCancelAutoCompletion) then
          OnCancelAutoCompletion(Self);
      end
      else
        DoCommand(Key, Shift);
    end;
    vkTab, vkInsert, vkHome,vkEnd, vkDelete, vkPrior, vkNext:
    begin
       if Popup.IsOpen then
      {$IFNDEF GENERALOSX}
        FListBox.KeyDown(Key, KeyChar, Shift)
      {$ENDIF}
      else
        DoCommand(Key, Shift);
    end;
    vkBack:
    begin
      if ssAlt in Shift then
      begin
        if ssShift in Shift then
          Redo
        else
          Undo;
      end
      else
        DoCommand(Key, Shift);

      if FPopup.IsOpen then
      begin
        if FAutoCompleteDot then
          s := UpperCase(WordAtXY(FDotPoint.X, FDotPoint.Y))
        else
          s := Uppercase(FullWordAtCursor);

        if FAutoCompleteDot and (pos('.', FullWordAtCursor) = 0) and (pos(')', FullWordAtCursor) = 0) then // it needs to close again when backspace deletes to '.' otherwise it just sticks there floating
          FPopup.IsOpen := false
        else
          DoPopup(True);
      end;
    end;
    vkReturn:
    begin
      if Popup.IsOpen then
        SelectPopupValue(True)
      else
        DoCommand(Key, Shift);
    end;
    else
    begin
      if (Ord(KeyChar) in [32..126, 128..255])
      or (Ord(KeyChar) > 255) then
      begin
        if FDelErase and (not ((FSelStartX = FSelEndX) and (FSelStartY = FSelEndY))) then
          DeleteSelectionInt;

        wrd := KeyChar;

        case CharCase of
        ecUpperCase: wrd := AnsiUpperCase(wrd);
        ecLowerCase: wrd := AnsiLowerCase(wrd);
        end;

        {$IFDEF DELPHI_LLVM}
        InsertChar(wrd[0]);
        {$ELSE}
        InsertChar(wrd[1]);
        {$ENDIF}
        if Popup.IsOpen then
          DoPopup(True);
      end
      else
        if not (Ord(Key) in [3, 8, 9]) then
          DoCommand(Ord(Key), []);
    end;
  end;

  CheckCodeInsightChar(KeyChar);
end;

function TTMSFMXMemo.SaveToHTML(FileName: string; Fixedfonts: Boolean): Boolean;
begin
  Result := False;
  OutputHTML(FixedFonts);

  try
    FHtml.SaveToFile(FileName);
    Result := True;
  except
    on Exception do ;
  end;

end;

function TTMSFMXMemo.SaveToHTMLStream(AStream: TMemoryStream;
  Fixedfonts: Boolean): Boolean;
begin
  Result := False;
  OutputHTML(FixedFonts);
  try
    FHtml.SaveToStream(AStream);
    Result := True;
  except
    on Exception do ;
  end;
end;

function TTMSFMXMemo.SaveToRTF(FileName: string; Fixedfonts: Boolean): Boolean;
begin
  Result := False;
  FRTFEngine := TRTFEngine.Create;
  try
    OutputRTF(FixedFonts);
    try
      FRTFEngine.SaveToFile(FileName);
      Result := True;
    except
      on Exception do ;
    end;
  finally
    FRTFEngine.Free;
  end;
end;

function TTMSFMXMemo.SaveToRTFStream(AStream: TMemoryStream;
  Fixedfonts: Boolean): Boolean;
begin
  Result := False;
  FRTFEngine := TRTFEngine.Create;
  try
    OutputRTF(FixedFonts);
    try
      AStream.Position := 0;
      FRTFEngine.SaveToStream(AStream);
      AStream.Position := 0;
      Result := True;
    except
      on Exception do;
    end;
  finally
    FRTFEngine.Free;
  end;
end;

procedure TTMSFMXMemo.SelClickUpdate(X, Y: Single; Down: Boolean;
  Shift: TShiftState; Button: TMouseButton);
var
  newPos: TCellPos;
  lc: Boolean;
  tp: integer;
  er: TRectF;
begin
  BlockUpdate;
  er := EditorRect;
  er.Left := 0;
  if PointInRect(PointF(X, Y), er) then
  begin
    newPos := CellFromPos(X, Y);

    TextFromPos(Round(newPos.X + LeftCol), Round(newPos.Y + TopLine), tp);
    if (SelStart < tp) and (SelStart + SelLength > tp) and Down then
    begin
      FSelButtonDown := True;
      EndBlockUpdate;
      Exit;
    end;

    ShowCaret(False);

    if newPos.x < 0 then
      newPos.x := 0;
    if newPos.y < 0 then
      newPos.y := 0;

    FCurY := Round(newPos.Y + FTopLine);
    if FCury >= InternalLines.Count then
      FCury := InternalLines.Count - 1;

    if FCury < 0 then
      FCury := 0;

    CurX := Round(newPos.X + FLeftCol);
    CurY := FCury;

    MakeVisible;

    lc := FLetRefresh;
    FLetRefresh := False;

    FLetRefresh := lc;

    if (Button = TMouseButton.mbLeft) and Down then
    begin
      if (ssShift in Shift) then
        ExpandSelection
      else
        ClearSelection;

      FLeftButtonDown := True;
    end;

    ShowCaret(True);
  end;

  EndBlockUpdate;
end;

procedure TTMSFMXMemo.MouseWheel(Shift: TShiftState;
  WheelDelta: Integer; var Handled: Boolean);
var
  vs: TScrollBar;
  n, wd: Integer;
begin
  inherited;

  wd := WheelDelta;

  {$IFDEF MACOS}
  wd := -wd;
  {$ENDIF}

  if Assigned(FVertScrollBar) then
    vs := FVertScrollBar
  else
    vs := GetVScroll;
  if Assigned(vs) and (wd <> 0) and vs.Visible then
  begin
    n := Round(LocalRect.Height / FCellSize.H) div 2;
    if wd > 0 then
      n := -n;
    vs.Value := FTopLine + n;
  end;
end;

procedure TTMSFMXMemo.Undo;
begin
  InternalUndoList.Undo;
end;

procedure TTMSFMXMemo.Redo;
begin
  InternalUndoList.Redo;
end;

procedure TTMSFMXMemo.AddUndoStep(AUndoStep: TUndo);
begin
  InternalUndoList.Add(AUndoStep);
end;

procedure TTMSFMXMemo.SetUndoLimit(Value: integer);
begin
  if (FUndoLimit <> Value) then
  begin
    if Value <= 0 then Value := 1;
    if Value > 100 then Value := 100;
    FUndoLimit := Value;
    FUndoList.Limit := Value;
  end;
end;

procedure TTMSFMXMemo.UndoChange;
begin
  if Assigned(FOnUndoChange) then
    FOnUndoChange(Self, InternalUndoList.Pos < InternalUndoList.Count, InternalUndoList.Pos > 0);
end;

function TTMSFMXMemo.CanUndo: boolean;
begin
  Result := (InternalUndoList.FPos < InternalUndoList.Count);
end;

procedure TTMSFMXMemo.CalculateCellSize;
var
  rctout: TRectF;
begin
  if not Assigned(Canvas) then
    Exit;
  if not Assigned(Canvas.Font) then
    Exit;

  Canvas.Font.Assign(FFont);
  rctout := RectF(0, 0, 10000, 10000);
  {$IFDEF DELPHIXE6_LVL}
  Canvas.MeasureText(rctout, 'W', False, [], TTextAlign.Leading, TTextAlign.Leading);
  {$ELSE}
  Canvas.MeasureText(rctout, 'W', False, [], TTextAlign.taLeading, TTextAlign.taLeading);
  {$ENDIF}
  FCellSize.W := rctout.Width;
  rctout := RectF(0, 0, 10000, 10000);
  {$IFDEF DELPHIXE6_LVL}
  Canvas.MeasureText(rctout, 'gh', False, [], TTextAlign.Leading, TTextAlign.Leading);
  {$ELSE}
  Canvas.MeasureText(rctout, 'gh', False, [], TTextAlign.taLeading, TTextAlign.taLeading);
  {$ENDIF}
  FCellSize.H := rctout.Height + LineSpacing;
end;

function TTMSFMXMemo.CanCopy: boolean;
begin
  Result := Selection <> '';
end;

function TTMSFMXMemo.CanCut: boolean;
begin
  Result := (Selection <> '') and not ReadOnly;
end;

function TTMSFMXMemo.CanRedo: boolean;
begin
  Result := InternalUndoList.FPos > 0;
end;

procedure TTMSFMXMemo.ClearUndoList;
begin
  InternalUndoList.Clear;
end;

procedure TTMSFMXMemo.ClearUndoRedo;
begin
  ClearUndoList;
end;

function TTMSFMXMemo.InternalUndoList: TAdvUndoList;
begin
  Result := FUndoList;
end;

procedure TTMSFMXMemo.DoEnter;
begin
  inherited;
  ShowCaretProc;
end;

procedure TTMSFMXMemo.DoExit;
begin
  inherited;
  HideCaret;

  if Observers.IsObserving(TObserverMapping.EditLinkID) then
    TLinkObservers.EditLinkUpdate(Observers);
end;

procedure TTMSFMXMemo.DoGutterMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
var
  newpos: TCellPos;
begin
  newPos := CellFromPos(0, Y);
  FGutterLine := newPos.Y;
  if Assigned(OnGutterClick) then
    OnGutterClick(Sender, FGutterLine);
end;

procedure TTMSFMXMemo.DoGutterDblClick(Sender: TObject);
begin
  if Assigned(OnGutterDblClick) then
    OnGutterDblClick(Sender,FGutterLine);
end;

procedure TTMSFMXMemo.PasteFromClipboard;
var
  Buff: String;
  ss, len: Integer;
  ClipService: IFMXClipboardService;
begin
  BlockUpdate;
  if Observers.IsObserving(TObserverMapping.EditLinkID) then
    if not TLinkObservers.EditLinkEdit(Observers) then
    begin
      TLinkObservers.EditLinkReset(Observers);
      EndBlockUpdate;
      Exit;
    end
    else
      TLinkObservers.EditLinkModified(Observers);
  try
    if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
    begin
      if not VarIsStr(ClipService.GetClipboard.AsVariant) then
      begin
        EndBlockUpdate;
        Exit;
      end;
    end
    else
    begin
      EndBlockUpdate;
      Exit;
    end;

    if AutoExpand and (CurY < InternalLines.Count) then
    begin
      len := Length(InternalLines[CurY]);
      if (CurX > len) then
      begin
        InternalLines[CurY] := InternalLines[CurY] + StringOfChar(' ', curx - len);
      end;
    end;

    Buff := ClipService.GetClipboard.AsString;
    AdjustLineBreaks(Buff);

    SetSelText(Buff);

    TextFromPos(CurX,CurY,ss);

    SelLength := 0;
    SelStart := ss;

    ResizeScrollBars;

  finally
    EndBlockUpdate;
  end;
end;

procedure TTMSFMXMemo.PerformAutoComplete;
var
  sWord: String;
begin
  if AutoCompletion.Active and not Popup.IsOpen then
  begin
    sWord := FullWordAtCursor;
    if (sWord <> '') then

//    if ((sWord <> '.') or (sWord <> '(')) and (sWord <> '') then
    begin
      FAutoCompleteDot := True;
      FDotPoint := Point(CurX, CurY);
      DoPopup;
    end
    else
    begin
      FAutoCompleteDot := False;
      DoPopup;
    end;
  end;
end;

procedure TTMSFMXMemo.ContextMenuItemClick(Sender: TObject);
begin
  if (Sender is TMenuItem) then
  begin
    case (Sender as TMenuItem).Tag of
    menuRedo: Redo;
    menuUndo: Undo;
    menuCut: DoCutToClipboard;
    menuCopy: DoCopyToClipboard;
    menuPaste: DoPasteFromClipboard;
    menuSelWord: SelectWordAtCursor;
    menuSelAll: SelectAll;
    menuDelSel: DeleteSelection;
    menuDelLine: DeleteLine;
    else
      DoContextMenuItemClick(FMemoContext, Sender as TMenuItem);
    end;
  end;
end;

procedure TTMSFMXMemo.CopyToClipboard;
var
  ClipService: IFMXClipboardService;
begin
  if Selection <> '' then
  begin
    if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
      ClipService.SetClipboard(Selection);
  end;
end;

procedure TTMSFMXMemo.CutToClipboard;
begin
  if Observers.IsObserving(TObserverMapping.EditLinkID) then
  begin
    if not TLinkObservers.EditLinkEdit(Observers) then
    begin
      TLinkObservers.EditLinkReset(Observers);
      Exit;
    end
    else
      TLinkObservers.EditLinkModified(Observers);
  end;

  CopyToClipboard;
  DeleteSelectionInt;
end;

procedure TTMSFMXMemo.DoCopyToClipboard;
var
  Allow: boolean;
begin
  Allow := true;

  DoClipboardAction(caCopy, Allow);

  if Allow then
    CopyToClipboard;
end;

procedure TTMSFMXMemo.DoCursorChange;
begin
  if Assigned(FOnCursorChange) then
    FOnCursorChange(Self);
end;

procedure TTMSFMXMemo.DoCutToClipboard;
var
  Allow: boolean;
begin
  Allow := true;

  DoClipboardAction(caCut, Allow);

  if Allow then
    CutToClipboard;
end;

procedure TTMSFMXMemo.DoPasteFromClipboard;
var
  Allow: boolean;
begin
  Allow := true;

  DoClipboardAction(caPaste, Allow);

  if Allow then
    PasteFromClipboard;
end;

procedure TTMSFMXMemo.DoAnchorClick(var Anchor: string; X, Y: integer);
var
  Handled: boolean;
begin
  Handled := false;
  if Assigned(OnAnchorClick) then
    OnAnchorClick(Self, Anchor, X,Y,Handled);
  if not Handled then
    XOpenURL(Anchor);
end;

procedure TTMSFMXMemo.DoClipboardAction(ClipboardAction: TMemoClipboardAction; var Allow: Boolean);
begin
  if Assigned(OnClipboardAction) then
    OnClipboardAction(Self, ClipboardAction, Allow);
end;

procedure TTMSFMXMemo.DoClosePopup(Sender: TObject);
begin
  FDroppedDown := False;
end;

function TTMSFMXMemo.WordAtXY(X, Y: Integer): String;
var
  i: integer;
  s: string;
  fe, fb: integer;
begin
  Result := '';

  if Y >= InternalLines.Count then
    Exit;

  s := InternalLines.Strings[Y];

  if (X > Length(s)) then
    Exit;

  fe := X;
  fb := X + 1;

  for i := X + 1 to Length(s) do
  begin
    {$IFDEF DELPHI_LLVM}
    if not IsWordBoundary(s[i-1]) then
    {$ELSE}
    if not IsWordBoundary(s[i]) then
    {$ENDIF}
      fe := i
    else
      Break;
  end;

  for i := X downto 1 do
  begin
    {$IFDEF DELPHI_LLVM}
    if not IsWordBoundary(s[i-1]) then
    {$ELSE}
    if not IsWordBoundary(s[i]) then
    {$ENDIF}
      fb := i
    else
      Break;
  end;

  Result := Copy(s, fb, fe - fb + 1);

end;

function TTMSFMXMemo.WordIsURL(s: string): Boolean;
begin
  s := LowerCase(s);

  Result := (AnsiPos('http://', s) = 1) or
            (AnsiPos('mailto:', s) = 1) or
            (AnsiPos('www.', s) = 1) or
            (AnsiPos('https://', s) = 1) or
            (AnsiPos('ftp://', s) = 1) or
            (AnsiPos('nntp://', s) = 1) or
            (AnsiPos('file://', s) = 1);
end;


function ClosingParenthesis(s1: string): integer;
var
  i,j,k,r: integer;
begin
  r := 0;
  j := 0;
  k := 0;
  i := 1;

  while (i <= length(s1)) do
  begin
    if (s1[i] = ')') then
      inc(k);

    if (s1[i] = '(') then
      inc(j);

    if (s1[i] = ')') and (j = k) then
    begin
      r := i;
      break;
    end;


    inc(i);
  end;

  Result := r;
end;


function StripLogicSpaces(s: string): string;
var
  i: integer;
  q: integer;
begin

  q := 0;
  i := 1;
  Result := '';

  while (i <= length(s)) do
  begin
    if s[i] = '"' then
      inc(q);

    if (s[i] = ' ') then
    begin
      if odd(q) then
        result := result + s[i];
    end
    else
      Result := Result + s[i];

    inc(i);
  end;
end;

function StripThousandSep(ps: pchar):string;
var
  i: Integer;
  s: string;
begin
  Result := '';
  s := strpas(ps);
  for i := 1 to Length(s) do
  begin
    if s[i] = FormatSettings.DecimalSeparator then
      Result := Result + '.' else
      if s[i] <> FormatSettings.ThousandSeparator then
        Result := Result + s[i];
  end;
end;

function IsDate(s:string;var dt:TDateTime):boolean;
var
  su, ts: string;
  da,mo,ye,ho,mi,se: word;
  err: Integer;
  dp,mp,yp,vp: Integer;
begin
  Result := False;

  ts := '';

  su := UpperCase(FormatSettings.ShortDateFormat);
  dp := pos('D',su);
  mp := pos('M',su);
  yp := pos('Y',su);

  da := 0;
  mo := 0;
  ye := 0;
  ho := 0;
  mi := 0;
  se := 0;

  if VarPos(FormatSettings.DateSeparator,s,vp)>0 then
  begin
    su := Copy(s,1,vp - 1);

    if (dp<mp) and
       (dp<yp) then
       val(su,da,err)
    else
    if (mp<dp) and
       (mp<yp) then
       val(su,mo,err)
    else
    if (yp<mp) and
       (yp<dp) then
       val(su,ye,err);

    if err<>0 then Exit;
    Delete(s,1,vp);

    if VarPos(FormatSettings.DateSeparator,s,vp)>0 then
    begin
      su := Copy(s,1,vp - 1);

      if ((dp>mp) and (dp<yp)) or
         ((dp>yp) and (dp<mp)) then
         val(su,da,err)
      else
      if ((mp>dp) and (mp<yp)) or
         ((mp>yp) and (mp<dp)) then
         val(su,mo,err)
      else
      if ((yp>mp) and (yp<dp)) or
         ((yp>dp) and (yp<mp)) then
         val(su,ye,err);

      if err<>0 then Exit;
      Delete(s,1,vp);

      s := Trim(s);

      if VarPos(' ',s, vp) > 0 then  // there is space to separate date & time
      begin
        ts := copy(s, vp, length(s));
        s := copy(s, 1, vp - 1);
      end;

      if (dp>mp) and
         (dp>yp) then
         val(s,da,err)
      else
      if (mp>dp) and
         (mp>yp) then
         val(s,mo,err)
      else
      if (yp>mp) and
         (yp>dp) then
         val(s,ye,err);

      if err<>0 then Exit;
      if (da>31) then Exit;
      if (mo>12) then Exit;

      if (ts <> '') then  // there is a time part
      begin
        if VarPos(FormatSettings.TimeSeparator,ts,vp)>0 then
        begin
          su := Copy(ts,1,vp - 1); // hour part
          val(su,ho,err);

          if (err <> 0) then Exit;
          if (ho > 23) then Exit;

          Delete(ts,1,vp);

          if VarPos(FormatSettings.TimeSeparator,ts,vp)>0 then // there is a second part
          begin
            su := Copy(ts,1,vp - 1); // minute part
            val(su,mi,err);

            if err <> 0 then Exit;
            Delete(ts,1,vp);

            val(ts,se,err);  // second part
            if (err <> 0) then Exit;
            if (se > 60) then Exit;
          end
          else
          begin
            val(su,mi,err); // minute part
            if (err <> 0) then Exit;
          end;

          if (mi > 59) then Exit;

          Result := true;
        end;
      end
      else
        Result := True;

      try
        dt := EncodeDate(ye,mo,da) + EncodeTime(ho,mi,se,0);
      except
        Result := False;
      end;
    end;
  end;
end;



function Matches(s0a,s1a: PChar): Boolean;
const
  larger = '>';
  smaller = '<';
  logand  = '&';
  logor   = '^';
  asterix = '*';
  qmark = '?';
  negation = '!';
  null = #0;

var
  matching:boolean;
  done:boolean;
  len:longint;
  lastchar:char;
  s0,s1,s2,s3:pchar;
  oksmaller,oklarger,negflag:boolean;
  compstr:array[0..255] of char;
  flag1,flag2,flag3:boolean;
  equal:boolean;
  n1,n2:double;
  code1,code2:Integer;
  dt1,dt2:TDateTime;
  q: integer;
begin
  oksmaller := True;
  oklarger := True;
  flag1 := False;
  flag2 := False;
  flag3 := False;
  negflag := False;
  equal := False;

  { [<>] string [&|] [<>] string }

  // do larger than or larger than or equal
  s2 := StrPos(s0a,larger);
  if s2 <> nil then
  begin
    inc(s2);
    if (s2^ = '=') then
    begin
      Equal := True;
      inc(s2);
    end;

    while (s2^ = ' ') do
      inc(s2);

    s3 := s2;
    len := 0;

    lastchar := #0;

    q := 0;

    while (s2^ <> ' ') and (s2^ <> NULL) and (odd(q) or ((s2^ <> '&') and (s2^ <> '|')))  do
    begin
      if (s2^= '"') then
        inc(q);

      if (len = 0) and (s2^ = '"') then
        inc(s3)
      else
        inc(len);

      lastchar := s2^;
      inc(s2);

      if (s2^= ' ') and odd(q) then // skip space if between quotes
      begin
        lastchar := s2^;
        inc(s2);
      end;
    end;

    if (len > 0) and (lastchar = '"') then
      dec(len);

    StrLCopy(compstr,s3,len);

    Val(StripThousandSep(s1a),n1,code1);
    Val(StripThousandSep(compstr),n2,code2);

    if IsDate(compstr,dt2) then code2 := 1;
    if IsDate(s1a,dt1) then code1 := 1;

    if (code1 = 0) and (code2 = 0) then {both are numeric types}
    begin
      if equal then
        oklarger := n1 >= n2
      else
        oklarger := n1 > n2;
    end
    else
    begin
      if IsDate(StrPas(compstr),dt2) and IsDate(StrPas(s1a),dt1) then
      begin
        if equal then
         oklarger := dt1 >= dt2
        else
         oklarger := dt1 > dt2;
      end
      else
      begin
        if equal then
         oklarger := (strlcomp(compstr,s1a,255)<=0)
        else
         oklarger := (strlcomp(compstr,s1a,255)<0);
      end;
    end;
    flag1 := True;
  end;

  equal := False;

  // do smaller than or smaller than or equal
  s2 := strpos(s0a,smaller);
  if (s2 <> nil) then
  begin
    inc(s2);
    if (s2^ = '=') then
      begin
       equal := True;
       inc(s2);
      end;

    lastchar := #0;

    while (s2^=' ') do inc(s2);
    s3 := s2;
    len := 0;
    q := 0;

    while (s2^ <> ' ') and (s2^ <> NULL) and (odd(q) or ((s2^ <> '&') and (s2^ <> '|'))) do
    begin
      if s2^ = '"' then
        inc(q);

      if (len = 0) and (s2^ = '"') then
        inc(s3)
      else
        inc(len);

      lastchar := s2^;
      inc(s2);
    end;

    if (len > 0) and (lastchar = '"') then
      dec(len);

    strlcopy(compstr,s3,len);

    Val(StripThousandSep(s1a),n1,code1);
    Val(StripThousandSep(compstr),n2,code2);
    if IsDate(compstr,dt2) then code2 := 1;
    if IsDate(s1a,dt1) then code1 := 1;

    if (code1 = 0) and (code2 = 0) then // both are numeric types
     begin
      if equal then
       oksmaller := n1 <= n2
      else
       oksmaller := n1 < n2;
     end
    else
     begin
      // check for dates here ?
      if IsDate(strpas(compstr),dt2) and IsDate(strpas(s1a),dt1) then
       begin
        if equal then
         oksmaller := dt1 <= dt2
        else
         oksmaller := dt1 < dt2;
       end
      else
       begin
        if equal then
          oksmaller := (strlcomp(compstr,s1a,255)>=0)
        else
          oksmaller := (strlcomp(compstr,s1a,255)>0);
       end;
     end;

    flag2 := True;
  end;

  s2 := strpos(s0a,negation);

  if (s2 <> nil) then
  begin
    inc(s2);
    while (s2^=' ') do
      inc(s2);
    s3 := s2;
    len := 0;

    lastchar := #0;
    q := 0;

    while (s2^ <> ' ') and (s2^ <> NULL) and (odd(q) or ((s2^ <> '&') and (s2^ <> '|'))) do
    begin
      if (s2^ = '"') then
        inc(q);

      if (len = 0) and (s2^ = '"') then
        inc(s3)
      else
        inc(len);

      lastchar := s2^;
      inc(s2);
    end;

    if (len > 0) and (lastchar = '"') then
      dec(len);

    strlcopy(compstr,s3,len);
    flag3 := True;
  end;

  if (flag3) then
  begin
    if strpos(s0a,larger) = nil then
      flag1 := flag3;
    if strpos(s0a,smaller) = nil then
      flag2 := flag3;
  end;

  if (strpos(s0a,logor) <> nil) then
    if flag1 or flag2 then
    begin
      matches := oksmaller or oklarger;
      Exit;
    end;

  if (strpos(s0a,logand)<>nil) then
    if flag1 and flag2 then
    begin
      matches := oksmaller and oklarger;
      Exit;
    end;

  if ((strpos(s0a,larger) <> nil) and (oklarger)) or
     ((strpos(s0a,smaller) <> nil) and (oksmaller)) then
  begin
    matches := True;
    Exit;
  end;

  s0 := s0a;
  s1 := s1a;

  matching := True;

  done := (s0^ = NULL) and (s1^ = NULL);

  while not done and matching do
  begin
    case s0^ of
    qmark:
      begin
        matching := s1^ <> NULL;
        if matching then
        begin
          inc(s0);
          inc(s1);
        end;
      end;
    negation:
      begin
        negflag:=True;
        inc(s0);
      end;
    '"':
      begin
        inc(s0);
      end;
    (*
    '\':
      begin
        inc(s0);
        matching := s0^ = s1^;

        if matching then
        begin
          inc(s0);
          inc(s1);
        end;
      end;
    *)
    asterix:
      begin
        repeat
          inc(s0)
        until (s0^ <> asterix);
        len := strlen(s1);
        inc(s1,len);
        matching := matches(s0,s1);
        while (len >= 0) and not matching do
        begin
         dec(s1);
         dec(len);
         matching := Matches(s0,s1);
       end;
       if matching then
       begin
         s0 := strend(s0);
         s1 := strend(s1);
       end;
     end;
   else
     begin
       matching := s0^ = s1^;

       if matching then
       begin
         inc(s0);
         inc(s1);
       end;
     end;
   end;

   Done := (s0^ = NULL) and (s1^ = NULL);
  end;

  if negflag then
    Matches := not matching
  else
    Matches := matching;
end;

function FirstChar(Charset:TCharSet;s:string;var spos: integer):char;
var
  i:Integer;
  q: Integer;

  function InArray(ch: char): boolean;
  var
    j: integer;
  begin
    result := false;
    for j := 0 to High(CharSet) - 1 do
    begin
      if ch = CharSet[j] then
      begin
        result := true;
        break;
      end;
    end;
  end;


begin
  i := 1;
  q := 0;
  spos := -1;
  Result := #0;

  while i <= Length(s) do
  begin
    if s[i] = '"' then
      inc(q);

    if (InArray(s[i])) and not odd(q) then
    begin
      spos := i;
      Result := s[i];
      Break;
    end;
    Inc(i);
  end;
end;





function MatchStr(s1,s2:string;DoCase:Boolean):Boolean;
begin
  if DoCase then
    MatchStr := Matches(PChar(s1),PChar(s2))
  else
    MatchStr := Matches(PChar(AnsiUpperCase(s1)),PChar(AnsiUpperCase(s2)));
end;


function MatchStrEx(s1,s2:string;DoCase:Boolean): Boolean;
var
  ch,lastop: Char;
  sep,cp: Integer;
  res,newres: Boolean;
  CharArray: TCharSet;

begin
  // remove leading & trailing spaces
  s1 := Trim(s1);

  // remove spaces between multiple filter conditions
  s1 := StripLogicSpaces(s1);

  if VarPos('=',s1,sep) = 1 then
    Delete(s1,sep,1);

  LastOp := #0;
  Res := True;

  SetLength(CharArray,5);
  CharArray[0] := '(';
  CharArray[1] := ';';
  CharArray[2] := '^';
  CharArray[3] := '&';
  CharArray[4] := '|';

  repeat
    ch := FirstChar(CharArray,s1, sep);

    // extract first part of filter
    if ch <> #0 then
    begin
      //VarPos(ch,s1,sep);

      if (length(s1) > 0) and (s1[1] = '(') and (pos('(',s1) > 0) then
      begin // found start of parenthesis
        cp := ClosingParenthesis(s1);
        NewRes := MatchStrEx(copy(s1,2,cp - 2),s2,DoCase);
        delete(s1,1,cp);
      end
      else
      begin
        NewRes := MatchStr(Copy(s1,1,sep - 1),s2,DoCase);
        Delete(s1,1,sep);
      end;

      if LastOp = #0 then
        Res := NewRes
      else
        case LastOp of
        ';','^','|':Res := Res or NewRes;
        '&':Res := Res and NewRes;
        end;

      LastOp := ch;
     end;

  until (ch = #0);

  NewRes := MatchStr(s1,s2,DoCase);

  if LastOp = #0 then
    Res := NewRes
  else
    case LastOp of
    ';','^','|':Res := Res or NewRes;
    '&':Res := Res and NewRes;
    end;

  Result := Res;
end;


function AnsiRPos(Substr, str: string): integer;
var
  i, last: integer;
begin
  Result := 0;
  if length(Substr) > length(str) then exit;
  if Substr = str then
  begin
    Result := 1;
    exit;
  end;
  last := 0;
  repeat
    i := ansipos(Substr, str);
    if i > 0 then
    begin
      last := last + i;
      Delete(str, 1, i);
    end;
  until i = 0;
  Result := last;
end;



function TTMSFMXMemo.FindAndReplace(SearchStr, NewStr: string;
  Options: TTMSFMXFindDialogOptions): Integer;
var
  rz,oldrz,count: integer;
  sex,sey,ssx,ssy,dx: integer;
begin
  oldrz := -1;
  count := 0;

  if FSelEndY > FSelStartY then
  begin
    sex := FSelEndX;
    sey := FSelEndY;
    ssx := FSelStartX;
    ssy := FSelStartY;
  end
  else
  begin
    sex := FSelStartX;
    sey := FSelStartY;
    ssx := FSelEndX;
    ssy := FSelEndY;
  end;

  dx := 0;

  repeat
    rz := FindText(SearchStr, Options);
    if rz = oldrz then
      Break;
    oldrz := rz;
    if (rz > -1) then
    begin
      dx := Length(NewStr) - Length(Selection);
      Selection := NewStr;
      inc(count);
    end;

    FSelEndX := sex;
    FSelEndY := sey;

  until (rz = -1);

  FSelStartX := ssx;
  FSelStartY := ssy;
  FSelEndX := sex + dx;
  FSelEndY := sey;

  Result := Count;
end;

function TTMSFMXMemo.FindText(SearchStr: string;
  Options: TTMSFMXFindDialogOptions): Integer;
begin
  Result := FindTextInt(SearchStr, Options, false);
end;

function TTMSFMXMemo.FindTextCount(SearchStr: string;
  Options: TTMSFMXFindDialogOptions): Integer;
begin
  Result := FindTextInt(SearchStr, Options, true);
end;


function TTMSFMXMemo.FindTextInt(SearchStr: string; Options: TTMSFMXFindDialogOptions; Count: boolean): integer;
var
  i, j, cit, ll, fl: integer;
  s: string;
  FoundStr: string;

  function CompareLineText: integer;
  var
    position,sf,tl: integer;
    worddelim: set of _AnsiChar;
    v: string;
    found: boolean;

  begin
    if (fdoExpression in Options) then
    begin
      sf := 1;
      Position := 0;
      FoundStr := '';
      found := false;
      repeat
        v := NextToken(s,sf,tl);

        if MatchStr(searchstr,v,(fdoCaseSensitive in Options)) then
        begin
          FoundStr := v;
          Position := sf + tl - length(v);
          found := true;
        end;

        sf := sf + tl;

      until (v = '') or (found);

      Result := Position;

      Exit;
    end;

    worddelim := [ord('A')..ord('Z'),ord('a')..ord('z'),ord('0')..ord('9')];

    if (fdoCaseSensitive in Options) then
    begin
      if fdoDown in Options then
        Position := AnsiPos(SearchStr, s)
      else
        Position := AnsiRPos(SearchStr, s);
    end
    else
    begin
      if fdoDown in Options then
        position := AnsiPos(LowerCase(SearchStr), LowerCase(s))
      else
        Position := AnsiRPos(LowerCase(SearchStr), LowerCase(s));
    end;

    Result := Position;

    if (fdoWholeWordOnly in Options) and (Position > 0) then
    begin
      if Length(s) = position + length(SearchStr) - 1 then
      begin
        if position = 1 then
          Exit;

        if not (ord(s[position - 1]) in worddelim) then
          Exit;
      end;

      if (position = 1) and not (ord(s[length(SearchStr) + 1]) in worddelim) then
        Exit;

      if not (ord(s[position - 1]) in worddelim) and not (ord(s[length(SearchStr) + position]) in worddelim) then
        Exit;
      Result := 0;
    end;
  end;

  procedure NextLine;
  begin
    if (fdoDown in Options) or (fdoSelection in Options) then inc(i)
    else
      dec(i);
  end;

begin
  Result := -1;
  if SearchStr = '' then
    Exit;

  if Count then
  begin
    //if not Assigned(FOnTextFound) then
    //  Exit;
    Result := 0;
  end;

  if (fdoSelection in Options) then
  begin
    i := Min(FSelStartY, FSelEndY);
    ll := Max(FSelStartY, FSelEndY);
    fl := i;
  end
  else
  begin
    i := CurY;
    ll := InternalLines.Count - 1;
    if (fdoDown in Options) then
      fl := i
    else
      fl := 0;
  end;

  cit := 0;

  while (i <= ll) and (i >= fl) do
  begin
    s := InternalLines[i];

    if (i = FSelStartY) and (FSelStartY = FSelEndY) then
    begin
      if (fdoDown in Options) {or (freSelection in Options)} then
      begin
        if FSelEndX >= Length(s) then
        begin
          NextLine;
          Continue;
        end;
        Delete(s, 1, FSelEndX);
        cit := FSelEndX;
      end
      else
      begin
        if (FSelStartX <> FSelEndX) and ((FSelStartX > 0) or (FSelEndX > 0)) then
        begin
          Delete(s, FSelStartX + 1, (length(s) - FSelStartX));
          cit := -(length(s) - FSelStartX);
        end
        else
        begin
          Delete(s, CurX + 1, length(s) - CurX);
          cit := -(length(s) - CurX);
        end;
      end;
    end
    else
    begin
      if (i = fl) and (FSelStartX > 0) then
      begin
        Delete(s, 1, FSelStartX);
        cit := FSelStartX;
      end
      else
        cit := 0;
    end;

    j := CompareLineText;

    // some text found, so update the selection
    if j > 0 then
    begin
      if Count then
      begin
        //FOnTextFound(Self, j + cit, i + 1);
        inc(Result);
      end
      else
      begin
        CurY := i;
        Curx := (j - 1) + cit;

        FSelStartX := Curx;
        if fdoExpression in Options then
          FSelEndX := Curx + Length(FoundStr)
        else
          FSelEndX := Curx + Length(SearchStr);
        FSelStartY := CurY;
        FSelEndY := CurY;

        Result := GetSelStart;
        Repaint;
        Exit;
      end;
    end;
    NextLine;
  end;

  FSelStartX := 0;
  FSelStartY := 0;
  FSelEndX := 0;
  FSelEndY := 0;
  Repaint;
end;


{ TTMSFMXMemoCustomStyler }

procedure TTMSFMXMemoCustomStyler.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXMemoCustomStyler) then
  begin
    FDescription := (Source as TTMSFMXMemoCustomStyler).Description;
    FFilter  := (Source as TTMSFMXMemoCustomStyler).Filter;
    FDefaultExtension  := (Source as TTMSFMXMemoCustomStyler).DefaultExtension;
    FExtensions := (Source as TTMSFMXMemoCustomStyler).Extensions;
    FStylerName := (Source as TTMSFMXMemoCustomStyler).StylerName;
    FBlockStart := (Source as TTMSFMXMemoCustomStyler).BlockStart;
    FBlockEnd := (Source as TTMSFMXMemoCustomStyler).BlockEnd;
    FLineComment := (Source as TTMSFMXMemoCustomStyler).LineComment;
    FLiteral := (Source as TTMSFMXMemoCustomStyler).Literal;
    FMultiCommentLeft := (Source as TTMSFMXMemoCustomStyler).MultiCommentLeft;
    FMultiCommentRight := (Source as TTMSFMXMemoCustomStyler).MultiCommentRight;
    FAllStyles.Assign((Source as TTMSFMXMemoCustomStyler).AllStyles);
    FCommentStyle.Assign((Source as TTMSFMXMemoCustomStyler).CommentStyle);
    FNumberStyle.Assign((Source as TTMSFMXMemoCustomStyler).NumberStyle);
    FURLStyle.Assign((Source as TTMSFMXMemoCustomStyler).URLStyle);
    FListAuto.Assign((Source as TTMSFMXMemoCustomStyler).AutoCompletion);
    FHintParameter.Assign((Source as TTMSFMXMemoCustomStyler).HintParameter);
    FNumericChars := (Source as TTMSFMXMemoCustomStyler).NumericChars;
    FHexIdentifier := (Source as TTMSFMXMemoCustomStyler).HexIdentifier;
    FCustomDraw := (Source as TTMSFMXMemoCustomStyler).CustomDraw;
    FEscapeChar := (Source as TTMSFMXMemoCustomStyler).EscapeChar;
    FHighlightStyle := (Source as TTMSFMXMemoCustomStyler).HighlightStyle;
  end;
end;

constructor TTMSFMXMemoCustomStyler.Create(AOwner: TComponent);
begin
  inherited;

  FAllStyles := TElementStyles.Create(Self);

  FCommentStyle := TCharStyle.Create;
  FURLStyle := TCharStyle.Create;
  FNumberStyle := TCharStyle.Create;

  FListAuto := TStringList.Create;
  FHintParameter := THintParameter.Create;

  FURLStyle.TextColor := TAlphaColorRec.Blue;
  FURLStyle.BkColor := TAlphaColorRec.Null;
  FURLStyle.Style := [TFontStyle.fsUnderline];

  FCommentStyle.TextColor := TAlphaColorRec.Silver;
  FCommentStyle.BkColor := TAlphaColorRec.Null;
  FCommentStyle.Style := [TFontStyle.fsItalic];

  FNumberStyle.TextColor := TAlphaColorRec.Navy;
  FNumberStyle.BkColor := TAlphaColorRec.Null;
  FNumberStyle.Style := [TFontStyle.fsBold];

  FHighlightStyle := TCharStyle.Create;
  FHighlightStyle.TextColor := TAlphaColorRec.White;
  FHighlightStyle.BkColor := TAlphaColorRec.Green;
  FHighlightStyle.Style := [];

  FNumericChars := '+-.0123456789';
  FHexIdentifier := '';
  FLiteral := '';
  FEscapeChar := #255;

  FRegionDefinitions := TRegionDefinitions.Create(self);
end;

destructor TTMSFMXMemoCustomStyler.Destroy;
begin
  FHighlightStyle.Free;
  FlistAuto.Free;
  FNumberStyle.Free;
  FCommentStyle.Free;
  FURLStyle.Free;
  FAllStyles.Free;
  FHintParameter.Free;
  FRegionDefinitions.Free;
  inherited;
end;

function TTMSFMXMemoCustomStyler.HasMultiComment: boolean;
var
  i: integer;
begin
  Result := (FMultiCommentLeft <> '') and (FMultiCommentRight <> '');

  if not Result then
  begin
    for i := 0 to AllStyles.Count - 1 do
    begin
      if AllStyles[i].StyleType = stComment then
      begin
        Result := (AllStyles[i].CommentLeft <> '') and (AllStyles[i].CommentRight <> '');
        if Result then
          break;
      end;
    end;
  end;
end;

procedure TTMSFMXMemoCustomStyler.SetStyles(const Value: TElementStyles);
begin
  FAllStyles.Assign(Value);
end;

procedure TTMSFMXMemoCustomStyler.SetStyle(const Index: integer;
  const Value: TCharStyle);
begin
  case Index of
    1: FCommentStyle.Assign(Value);
    2: FNumberStyle.Assign(Value);
    3: FURLStyle.Assign(Value);
  end;
end;

procedure TTMSFMXMemoCustomStyler.SetHighlightStyle(const Value: TCharStyle);
begin
  FHighlightStyle.Assign(Value);
end;

procedure TTMSFMXMemoCustomStyler.SetlistAuto(const Value: TStringList);
begin
  FListAuto.Assign(Value);
end;

procedure TTMSFMXMemoCustomStyler.SetRegionDefinitions(Value: TRegionDefinitions);
begin
  FRegionDefinitions.Assign(Value);
end;

procedure TTMSFMXMemoCustomStyler.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  if (Operation = opRemove) and (AComponent = FAllStyles.FOwner) then
  begin
    if Assigned(FAllStyles) then
    begin
      FAllStyles.FOwner := nil;
    end;
  end;
  inherited;
end;

function TTMSFMXMemoCustomStyler.PosCommentLeft(s: string;
  var cl: integer): integer;
var
  mres, res,i: integer;

begin
  cl := 0;
  mres := length(s);

  if FMultiCommentLeft <> '' then
  begin
    res := AnsiPos(FMultiCommentLeft, s);
    if res > 0 then
    begin
      cl := Length(FMultiCommentLeft);
      mres := res;
    end;
  end;

  for i := 0 to AllStyles.Count - 1 do
  begin
    if (AllStyles[i].StyleType = stComment) and (AllStyles[i].CommentLeft <> '') then
    begin
      res := AnsiPos(AllStyles[i].CommentLeft, s);
      if (res < mres) and (res > 0) then
      begin
        cl := Length(AllStyles[i].CommentLeft);
        mres := res;
      end;
    end;
  end;

  if cl <> 0 then
    Result := mres
  else
    Result := 0;
end;

function TTMSFMXMemoCustomStyler.PosCommentRight(s: string;
  var cl: integer): integer;
var
  mres,res,i: integer;
begin
  cl := 0;

  mres := length(s);

  if FMultiCommentRight <> '' then
  begin
    res := AnsiPos(FMultiCommentRight, s);
    if res > 0 then
    begin
      cl := Length(FMultiCommentRight);
      mres := res;
    end;
  end;

  for i := 0 to AllStyles.Count - 1 do
  begin
    if (AllStyles[i].StyleType = stComment) and (AllStyles[i].CommentRight <> '') then
    begin
      res := AnsiPos(AllStyles[i].CommentRight, s);
      if (res < mres) and (res > 0) then
      begin
        cl := Length(AllStyles[i].CommentRight);
        mres := res;
      end;
    end;
  end;

  if cl <> 0 then
    Result := mres
  else
    Result := 0;
end;

procedure TTMSFMXMemoCustomStyler.Loaded;
begin
  inherited;
  if not FAllStyles.FModified then
    FAllStyles.Clear;
end;

{ TElementStyles }

function TElementStyles.Add: TElementStyle;
begin
  Result := TElementStyle(inherited Add);
end;

constructor TElementStyles.Create(AOwner: TComponent);
begin
  FModified := False;
  inherited Create(AOwner, CreateItemClass);
end;

function TElementStyles.CreateItemClass: TCollectionItemClass;
begin
  Result := TElementStyle;
end;

function TElementStyles.GetItem(Index: integer): TElementStyle;
begin
  Result := TElementStyle(inherited Items[Index]);
end;

procedure TElementStyles.Init;
begin
  FModified := true;
end;

function TElementStyles.Insert(index: integer): TElementStyle;
begin
  Result := TElementStyle(inherited Insert(Index));
end;

function TElementStyles.IsWordBoundary(ch: char): boolean;
var
  i: integer;
begin
  Result := False;
  for i := 1 to Count do
  begin
    if Items[i - 1].FStyleType = stSymbol then
      if Pos(ch, Items[i - 1].Symbols) > 0 then
      begin
        Result := True;
        Break;
      end;

    if Items[i - 1].FStyleType = stBracket then
      if (ch = Items[i - 1].BracketEnd) or (ch = Items[i - 1].BracketStart)
        then
      begin
        Result := True;
        Break;
      end;
  end;
end;

procedure TElementStyles.SetItem(Index: integer; const Value: TElementStyle);
begin
  inherited Items[Index] := Value;
end;

procedure TElementStyles.Update(Item: TCollectionItem);
begin
  inherited;
end;

{ TElementStyle }

procedure TElementStyle.Changed;
begin
  TElementStyles(GetOwner).Update(Self);
end;

constructor TElementStyle.Create(Collection: TCollection);
begin
  inherited;
  FOwner := (Collection as TElementStyles).FOwner;
  FKeyWords := TStringList.Create;
  FFont := TFont.Create;

  if not Assigned(FOwner) or ((csDesigning in FOwner.ComponentState) and not
    ((csReading in FOwner.Owner.ComponentState) or (csLoading in FOwner.Owner.ComponentState))) then
  begin
    {$IFDEF DELPHI_LLVM}
    FFont.Family := 'Courier';
    {$ELSE}
    FFont.Family := 'Courier New';
    {$ENDIF}
  end;

  FFont.Style := [];
  FBGColor := TAlphaColorRec.Null;
  FFontColor := TAlphaColorRec.Black;
  FFont.Size := 8;
  FStyleType := stKeyword;
  StyleNo := -1;
  FBracketEnd := #0;
  FBracketStart := #0;
end;

destructor TElementStyle.Destroy;
begin
  TElementStyles(GetOwner).FModified := True;
  FKeyWords.Free;
  FFont.Free;
  inherited;
end;

procedure TElementStyle.SetBracketStart(const Value: char);
begin
  if (FBracketStart <> Value) then
  begin
    FBracketStart := Value;
    Changed;
  end;
end;

procedure TElementStyle.SetBracketEnd(const Value: char);
begin
  if (FBracketEnd <> Value) then
  begin
    FBracketEnd := Value;
    Changed;
  end;
end;

procedure TElementStyle.SetSymbols(const Value: string);
begin
  if (FSymbols <> Value) then
  begin
    FSymbols := Value;
    Changed;
  end;
end;

procedure TElementStyle.SetColorbg(const Value: TAlphaColor);
begin
  if (FBGColor <> Value) then
  begin
    FBGColor := Value;
    Changed;
  end;
end;

procedure TElementStyle.SetStyleType(const Value: TStyleType);
begin
  if (FStyleType <> Value) then
  begin
    FStyleType := Value;
    Changed;
  end;
end;

procedure TElementStyle.SetFont(const Value: Tfont);
begin
  FFont.Assign(Value);
  Changed;
end;

procedure TElementStyle.SetKeyWords(const Value: TStringList);
begin
  FKeyWords.Assign(Value);
  Changed;
end;

function TElementStyle.GetDisplayName: string;
begin
  if Info <> '' then
    Result := Info
  else
    Result := inherited GetDisplayName;
end;

procedure TElementStyle.Assign(Source: TPersistent);
begin
  if Source is TElementStyle then
  begin
    FKeyWords.Assign(TElementStyle(Source).KeyWords);
    FFont.Assign(TElementStyle(Source).Font);
    FBGColor := TElementStyle(Source).BGColor;
    FStyleType := TElementStyle(Source).StyleType;
    FBracketStart := TElementStyle(Source).BracketStart;
    FBracketEnd := TElementStyle(Source).BracketEnd;
    FSymbols := TElementStyle(Source).Symbols;
    FInfo := TElementStyle(Source).Info;
    FontColor := TElementStyle(Source).FontColor;
    FCommentLeft := TElementStyle(Source).CommentLeft;
    FCommentRight := TElementStyle(Source).CommentRight;
  end;
end;

{ THintParameter }

procedure THintParameter.Assign(Source: TPersistent);
begin
  if (Source is THintParameter) then
  begin
    FDelimiterchar := (Source as THintParameter).HintCharDelimiter;
    FWritedelimiterchar := (Source as THintParameter).HintCharWriteDelimiter;
    FHintClassDelimiter := (Source as THintParameter).HintClassDelimiter;
    FStartChar := (Source as THintParameter).HintCharStart;
    FEndChar := (Source as THintParameter).HintCharEnd;
    FBkColor := (Source as THintParameter).BkColor;
    FTextColor := (Source as THintParameter).TextColor;
  end;
end;

constructor THintParameter.Create;
begin
  inherited;
  FParameters := TStringList.Create;
  FTextColor := TAlphaColorRec.Black;
  FBkColor := TAlphaColorRec.Yellow;
  FStartchar := '(';
  FEndchar := ')';
  FDelimiterChar := ';';
  FWriteDelimiterChar := ',';
  FHintClassDelimiter := '.';
end;

destructor THintParameter.Destroy;
begin
  FParameters.free;
  inherited;
end;

procedure ThintParameter.SetParameters(const Value: Tstringlist);
begin
  FParameters.Assign(Value);
end;

{ TRegionDefinition }

constructor TRegionDefinition.Create(Collection: TCollection);
begin
  inherited;
  FRegionType := rtClosed;
  FRegionEnd := '';
  FIdentifier := '';
  FRegionStart := '';
end;

procedure TRegionDefinition.Assign(Source: TPersistent);
begin
  if (Source is TRegionDefinition) then
  begin
    FRegionType := (Source as TRegionDefinition).RegionType;
    FRegionEnd := (Source as TRegionDefinition).RegionEnd;
    FIdentifier := (Source as TRegionDefinition).Identifier;
    FRegionStart := (Source as TRegionDefinition).RegionStart;
  end
  else
    inherited;
end;

procedure TRegionDefinition.SetIdentifier(const Value: string);
begin
  FIdentifier := Trim(Value);
end;

procedure TRegionDefinition.SetRegionEnd(const Value: string);
begin
  FRegionEnd := Trim(Value);
end;

procedure TRegionDefinition.SetRegionStart(const Value: string);
begin
  FRegionStart := Trim(Value);
end;

procedure TRegionDefinition.SetRegionType(const Value: TRegionType);
begin
  FRegionType := Value;
end;

function TRegionDefinition.GetDisplayName: string;
begin
  Result := inherited GetDisplayName;
end;

{ TRegionDefinitions }

constructor TRegionDefinitions.Create(Component: TPersistent);
begin
  inherited Create(TRegionDefinition);
  FComponent := Component;
end;

destructor TRegionDefinitions.Destroy;
begin
  inherited;
end;

function TRegionDefinitions.GetItem(Index: Integer): TRegionDefinition;
begin
  Result := TRegionDefinition(inherited Items[Index]);
end;

procedure TRegionDefinitions.SetItem(Index: Integer; Value: TRegionDefinition);
begin
  inherited Items[Index] := Value;
end;

function TRegionDefinitions.GetOwner: TPersistent;
begin
  Result := FComponent;
end;

function TRegionDefinitions.Add: TRegionDefinition;
begin
  Result := TRegionDefinition(inherited Add);
end;

function TRegionDefinitions.Insert(Index: Integer): TRegionDefinition;
begin
  Result := TRegionDefinition(inherited Insert(Index));
end;

{ TCharStyle }

procedure TCharStyle.Assign(Source: TPersistent);
begin
  if (Source is TCharStyle) then
  begin
    TextColor := (Source as TCharStyle).TextColor;
    BkColor := (Source as TCharStyle).BkColor;
    Style := (Source as TCharStyle).Style;
  end;
end;


{ TlineProp }

constructor TLineProp.Create;
begin
  inherited;
  Executable := False;
  BreakPoint := False;
  Wrapped := False;
  Modified := lmUnmodified;
  ImageIndex := -1;
  FErrStart := TIntList.Create;
  FErrLen := TIntList.Create;
  FExpanded := True;
  FLastChildOfParents := 0;
  FHasParent := False;
  FHasChildren := False;
end;

destructor TLineProp.destroy;
begin
  FErrStart.Free;
  FErrLen.Free;
  inherited;
end;

procedure TLineProp.SetExpanded(const Value: Boolean);
begin
  FExpanded := Value;
end;

function TLineProp.GetLastChild: Boolean;
begin
  Result := (FLastChildOfParents > 0);
end;

procedure TLineProp.SetLastChildOfParents(Value: integer);
begin
  if (Value >= 0) then
    FLastChildOfParents := Value;
end;

{ TFMXMemoStrings }

constructor TFMXMemoStrings.Create;
begin
  inherited;
  FLinesProp := TList<TLineProp>.Create;
  FListLengths := TIntList.Create;
end;

destructor TFMXMemoStrings.Destroy;
begin
  ClearLinesProp(False);
  FlinesProp.Free;
  FListLengths.Free;
  inherited;
end;

function TFMXMemoStrings.DoAdd(const S: string): integer;
begin
  Result := TStringList(Self).Add(s);
end;

procedure TFMXMemoStrings.DoInsert(Index: integer; const S: string);
var
  s1: string;
begin
  if Assigned(memo) then
  begin
    s1 := StringReplace(S, #9, stringofchar(#32, memo.tabsize), [rfreplaceall]);
    if index < memo.FTopLine then memo.FbackupTopLine := -1;
    FListLengths.Insert(Index, length(s1));

    inherited Insert(Index, s1);
  end
  else
  begin
    FListLengths.Insert(Index, length(s));
    inherited Insert(Index, s);
  end;
end;

procedure TFMXMemoStrings.Delete(Index: integer);
var
  SaveCurY, SaveCurX: integer;
begin
  if (Index < 0) or (Index > Count - 1) then Exit;

  if FDeleting or (not Assigned(Memo)) or (Assigned(Memo) and Memo.FInternalDelete) then
  begin
    FListLengths.Delete(index);
    inherited;
  end
  else
  begin
    if index < Memo.FTopLine then
      Memo.FbackupTopLine := -1;

    FDeleting := True;

    SaveCurX := Memo.CurX;
    SaveCurY := Memo.CurY;
    if Index < SaveCurY then Dec(SaveCurY);
    if Count = 1 then SaveCurX := 0;

    if Memo.IsUpdating then
      Memo.FCurY := Index
    else
      Memo.CurY := Index;

    Memo.DeleteLine;
    Memo.FCurX := SaveCurX;
    Memo.FCurY := SaveCurY;
    FDeleting := False;
  end;
end;

procedure TFMXMemoStrings.Insert(Index: integer; const S: string);
var
  vp: integer;
  su: string;
begin
  BeginUpdate;
  Memo.ClearSelection;
  su := s;
  while varpos(#13#10,su,vp) > 0 do
  begin
    DoInsert(index, Copy(su, 1, vp - 1));
    System.Delete(su,1,vp + 1);
    inc(index);
  end;
  DoInsert(index, su);
  Memo.curx := Memo.curx;
  Memo.curY := Memo.curY;
  EndUpdate;
end;

procedure TFMXMemoStrings.SaveToFile(const FileName: string);
var
  i: integer;
  tr: string;
begin
  BeginUpdate;
  for i := 0 to Count - 1 do
  begin
    tr := TrimRight(Strings[i]);
    if Strings[i] <> tr then
      Strings[i] := tr;
  end;
  EndUpdate;

  inherited SaveToFile(FileName);

  if Assigned(Memo) then
    Memo.UpdateMemo;
end;

procedure TFMXMemoStrings.SaveToFile(const FileName: string; Encoding : TEncoding );
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    Strings[i] := TrimRight(Strings[i]);

  inherited SaveToFile(FileName, Encoding);
end;

//------------------------------------------------------------------------------

procedure TFMXMemoStrings.LoadFromFile(const FileName: string);
var
  i, len, mx: integer;
  s: string;
begin
  if not Assigned(Memo) then
  begin
    inherited LoadFromFile(FileName);
    Exit;
  end;

  Memo.BlockUpdate;

  with Memo do
  begin
    ClearSelection;
    ClearUndoList;
    CurX := 0;
    CurY := 0;
    FLetRefresh := False;
  end;

  Memo.InternalLines.OnChange := nil;

  inherited LoadFromFile(FileName);

  Memo.FBackupTopLine := -1;
  Memo.CurX := 0;
  Memo.CurY := 0;
  mx := 0;

  Memo.FLetRefresh := false;

  for i := 0 to Memo.InternalLines.Count - 1 do
  begin
    s := Strings[i];
    if pos(#9,s) > 0 then
    begin
      s := StringReplace(s, #9, stringofchar(#32, memo.TabSize), [rfreplaceall]);
      Strings[i] := s;
    end;

    len := Length(s);
    if len > mx then
      mx := len;
  end;
  Memo.FmaxLength := mx;

  Memo.FLetRefresh := true;
  Memo.InternalLines.OnChange := Memo.LinesChanged;

  Memo.FLetRefresh := True;
  Memo.FSetMaxLength := False;
  Memo.FSetMaxLength := True;
  Memo.EndBlockUpdate;
  Memo.InitScrollbars;
end;

procedure TFMXMemoStrings.SaveToStream(Stream: TStream);
begin
  inherited SaveToStream(Stream);

  if Assigned(Memo) then
    Memo.UpdateMemo;
end;

procedure TFMXMemoStrings.LoadFromStream(Stream: TStream);
var
  i, len, mx: integer;
  s: string;

begin
  if not Assigned(Memo) then
  begin
    inherited LoadFromStream(Stream);
    Exit;
  end;

  Memo.BlockUpdate;

  with Memo do
  begin
    ClearSelection;
    ClearUndoList;
    CurX := 0;
    CurY := 0;
    FLetRefresh := False;
  end;

  Memo.InternalLines.OnChange := nil;

  inherited LoadFromStream(Stream);

  Memo.FbackupTopLine := -1;
  Memo.CurX := 0;
  Memo.CurY := 0;
  mx := 0;

  for i := 0 to Memo.InternalLines.Count - 1 do
  begin
    s := Strings[i];
    if pos(#9,s) > 0 then
    begin
      s := StringReplace(s, #9, stringofchar(#32, memo.TabSize), [rfreplaceall]);
      Strings[i] := s;
    end;
    len := Length(s);
    if len > mx then
      mx := len;
  end;

  Memo.FMaxLength := mx;
  Memo.InternalLines.OnChange := Memo.LinesChanged;
  Memo.FLetRefresh := True;
  Memo.EndBlockUpdate;
end;

function TFMXMemoStrings.GetObject(Index: Integer): TObject;
begin
  Result := nil;

  if (Index >= 0) and (Index < Count) then
  begin
    Result := inherited GetObject(Index);
    if Assigned(Result) and (Result is TLineProp) then
      Result := TLineProp(Result).FObject;
  end;
end;

procedure TFMXMemoStrings.PutObject(Index: Integer; AObject: TObject);
var
  P: TObject;
begin
  if (Index >= 0) and (Index < Count) then
  begin
    P := inherited GetObject(Index);
    if Assigned(P) and (P is TLineProp) then
      TLineProp(P).FObject := AObject
    else
      inherited PutObject(Index, AObject);
  end;
end;

procedure TFMXMemoStrings.RestoreObject(Index: Integer; AObject: TObject);
begin
  inherited PutObject(Index, AObject);
end;

function TFMXMemoStrings.Get(Index: Integer): string;
begin
  Result := inherited Get(Index);
end;

procedure TFMXMemoStrings.Put(Index: Integer; const S: string);
var
  sz: string;
begin
  if not Assigned(Memo) then
  begin
    inherited Put(index, s)
  end
  else
  begin
    sz := (StringReplace(s, #9, StringOfChar(#32, memo.Tabsize), [rfreplaceall]));
    if Index < FListLengths.Count then
      FListLengths.Items[Index] := length(sz)
    else
      FListLengths.Add(length(sz));

    inherited Put(Index, sz);

    if Index < Memo.FTopLine then
      Memo.FbackupTopLine := -1;
  end;
end;

function TFMXMemoStrings.CreateProp(Index: integer): TLineProp;
begin
  Result := TLineProp.Create;
  with Result do
    FObject := inherited GetObject(Index);

  inherited PutObject(Index, Result);

  AddLineProp(Result);
end;

procedure TFMXMemoStrings.AddLineProp(p: TObject);
begin
  FLinesProp.Add(Pointer(p));
end;

procedure TFMXMemoStrings.ClearStrings;
begin
  FLinesProp.Clear;
  FListLengths.Clear;
  inherited Clear;
end;

procedure TFMXMemoStrings.Clear;
begin
  if Assigned(Memo) then
  begin
    with Memo do
    begin
      BlockUpdate;
      FSelStartX := 0;
      FSelStartY := 0;
      FSelEndX := 0;
      FSelEndY := 0;
      CurY := 0;
      CurX := 0;
      EndBlockUpdate;
    end;
  end;

  ClearLinesProp;
  FListLengths.Clear;

  inherited;
end;

procedure TFMXMemoStrings.ClearLinesProp(UpdateObject: Boolean = True);
var
  P: TLineProp;
  i: Integer;
begin
  for i := 0 to FLinesProp.Count - 1 do
  begin
    p := FLinesProp[i];

    if Assigned(p) then
      p.Free;
  end;

  FLinesProp.Clear;

  if UpdateObject then
  begin
    for i := 0 to Count - 1 do
    begin
      inherited PutObject(i, nil);
    end;
  end;
end;

function TFMXMemoStrings.GetLineProp(Index: Integer): TLineProp;
var
  P: Tobject;
begin
  if (index < Count) and (Index >= 0) then
  begin
    p := inherited GetObject(Index);
    if p is TLineProp then
      Result := TLineProp(p)
    else
      Result := nil;
  end
  else
    Result := nil;
end;

procedure TFMXMemoStrings.SetLineProp(Index: Integer;
  const Value: TlineProp);
begin
  if (Index < Count) and (Index >= 0) then
    inherited PutObject(index, value)
end;

procedure TFMXMemoStrings.SetTextEx(const Value: string);
begin
  inherited text := value;
end;

function TFMXMemoStrings.GetRealCount: integer;
begin
  if (Text = '') or (Text = #13#10) then
    Result := 0
  else
    Result := inherited Count;
end;

function TFMXMemoStrings.GetTextEx: string;
begin
  Result := inherited Text;
end;

function TFMXMemoStrings.AddObject(const S: string; AObject: TObject): Integer;
var
  P: TLineProp;
begin
  if not FNoObjCreate then
  begin
    P := TLineProp.Create;
    (P as TLineProp).FObject := AObject;
    FLinesProp.Add(p);
    Result := inherited AddObject(S,P);
  end
  else
    Result := inherited AddObject(S,nil);
  FListLengths.Add(length(s));
end;

function TFMXMemoStrings.Add(const S: string): integer;
var
  sz,sc: string;
  p: integer;
begin

  if not Assigned(Memo) then
  begin
    Result := inherited Add(s);
  end
  else
  begin
    sz := StringReplace(s, #9, StringOfChar(#32, memo.tabsize), [rfreplaceall]);
    sz := StringReplace(sz, #10, '', [rfreplaceall]);

    while pos(#13,sz) > 0 do
    begin
      p := pos(#13,sz);
      sc := copy(sz,1,p-1);
      inherited Add(sc);
      sz := Copy(sz,p+1,length(sz));
    end;

    Result := inherited Add(sz);
  end;
end;

procedure TFMXMemoStrings.AddStrings(Strings: TStrings);
var
  i: integer;
begin
  if Assigned(Memo) then
  begin
    Memo.BlockUpdate;
    inherited AddStrings(Strings);
    FListLengths.Clear;
    for i := 1 to Memo.Lines.Count do
      FListLengths.Add(Length(Memo.Lines[i - 1]));

    Memo.EndBlockUpdate;
  end;
end;

procedure TFMXMemoStrings.Assign(Source: TPersistent);
var
  i: Integer;
begin
  if Source is TStrings then
  begin
    if Assigned(Memo) then
    begin
      Memo.Clear;
      Memo.FBackupTopLine := -1;
      Memo.SetLines(TFMXMemoStrings(Source));
      FListLengths.Clear;
      for i := 1 to TFMXMemoStrings(Source).Count do
        FListLengths.Add(Length(TFMXMemoStrings(Source)[i - 1]));
      Memo.SetMaxLength;
    end
    else
    begin
      inherited Assign(Source);
      inherited AddStrings(Source as TStrings);
    end;
  end
  else
    inherited Assign(Source);
end;

procedure TFMXMemoStrings.AssignEx(Source: TPersistent);
var
  i: integer;
begin
  if Source is TStrings then
  begin
    if Assigned(Memo) then
    begin
      try
        Memo.InternalLines.FNoObjCreate := true;
        Memo.FBackupTopLine := -1;

        Memo.InternalLines.ClearStrings;
        Memo.InternalLines.AddStrings(TFMXMemoStrings(Source));

        FListLengths.Clear;
        for i := 1 to TFMXMemoStrings(Source).Count do
          FListLengths.Add(Length(TFMXMemoStrings(Source)[i - 1]));
        Memo.SetMaxLength;
      finally
        Memo.InternalLines.FNoObjCreate := false;
      end;
    end
    else
      inherited Assign(Source);
  end
  else
    inherited Assign(Source);
end;

constructor TUndo.Create(ACurX0, ACurY0, ACurX, ACurY: integer; AText: string);
begin
  inherited Create;
  FUndoCurX0 := ACurX0;
  FUndoCurY0 := ACurY0;
  FUndoCurX := ACurX;
  FUndoCurY := ACurY;
  FUndoText := AText;
  FLinkedUndo := false;
end;

procedure TUndo.Undo;
begin
  if Assigned(Memo) then
  begin
    with Memo do
    begin
      CurY := FUndoCurY;
      CurX := FUndoCurX;
      PerformUndo;
      CurY := FUndoCurY0;
      CurX := FUndoCurX0;
    end;
  end;
end;

procedure TUndo.Redo;
begin
  if Assigned(Memo) then
  begin
    with Memo do
    begin
      CurY := FUndoCurY0;
      CurX := FUndoCurX0;
      PerformRedo;
      CurY := FUndoCurY;
      CurX := FUndoCurX;
    end;
  end;
end;

function TUndo.Append(NewUndo: TUndo): boolean;
begin
  Result := False;
end;

procedure TOverwriteCharUndo.PerformUndo;
var
  currline: string;
  redo:string;
begin
  Memo.BlockUpdate;
  redo := FUndoText;
  CurrLine := Memo.InternalLines[FUndoCurY];
  {$IFDEF DELPHI_LLVM}
  CurrLine[FUndoCurX] := FUndoText[0];
  {$ELSE}
  CurrLine[FUndoCurX + 1] := FUndoText[1];
  {$ENDIF}
  Memo.InternalLines[FUndoCurY] := CurrLine;
  FUndoText := redo;
  Memo.EndBlockUpdate;
end;

procedure TOverwriteCharUndo.PerformRedo;
var
  currline: string;
  redo:string;
begin
  Memo.BlockUpdate;
  redo := FUndoText;
  CurrLine := Memo.InternalLines[Memo.CurY];
  {$IFDEF DELPHI_LLVM}
  CurrLine[Memo.CurX] := FUndoText[0];
  {$ELSE}
  CurrLine[Memo.CurX + 1] := FUndoText[1];
  {$ENDIF}
  Memo.InternalLines[Memo.CurY] := CurrLine;
  FUndoText := redo;
  Memo.EndBlockUpdate;
end;

function TNewLineUndo.Append(NewUndo: TUndo): boolean;
begin
  Result := False;
  if not ((NewUndo is TNewLineUndo) and
    (NewUndo.UndoCurX0 = FUndoCurX) and
    (NewUndo.UndoCurY0 = FUndoCurY)) then Exit;
  FUndoText := FUndoText + NewUndo.FUndoText;
  FUndoCurX := NewUndo.UndoCurX;
  FUndoCurY := NewUndo.UndoCurY;
  Result := True;
end;

procedure TNewLineUndo.PerformUndo;
var
  i: Integer;
  CurrLine: string;
begin
  {$IFDEF DELPHI_LLVM}
  for i := Length(FUndoText) - 1 downto 0 do
  begin
    CurrLine := Memo.InternalLines[Memo.CurY];
    if ((FUndoText[i] = #13) and (Memo.CurX = 0)) or ((Length(CurrLine) > 0) and
      (FUndoText[i] = CurrLine[Memo.CurX - 1])) then
      Memo.BackSpace;
  end;
  {$ELSE}
  for i := Length(FUndoText) downto 1 do
  begin
    CurrLine := Memo.InternalLines[Memo.CurY];
    if ((FUndoText[i] = #13) and (Memo.CurX = 0)) or ((Length(CurrLine) > 0) and
      (FUndoText[i] = CurrLine[Memo.CurX])) then
      Memo.BackSpace;
  end;
  {$ENDIF}
end;

procedure TNewLineUndo.PerformRedo;
var
  i: Integer;
begin
  with Memo do
  begin
    {$IFDEF DELPHI_LLVM}
    for i := 0 to Length(FUndoText) - 1 do
    {$ELSE}
    for i := 1 to Length(FUndoText) do
    {$ENDIF}
    begin
      if FUndoText[i] = #13 then
        NewLine
      else
        InsertChar(FUndoText[i]);
    end;
  end;
end;

procedure TInsertCharUndo.PerformUndo;
var
  i: Integer;
  CurrLine: string;
begin
  {$IFDEF DELPHI_LLVM}
  for i := Length(FUndoText) - 1 downto 0 do
  begin
    CurrLine := Memo.InternalLines[Memo.CurY];
    if ((FUndoText[i] = #13) and (Memo.CurX = 0)) or ((Length(CurrLine) > 0) and
      (FUndoText[i] = CurrLine[Memo.CurX - 1])) then
      Memo.BackSpace;
  end;
  {$ELSE}
  for i := Length(FUndoText) downto 1 do
  begin
    CurrLine := Memo.InternalLines[Memo.CurY];
    if ((FUndoText[i] = #13) and (Memo.CurX = 0)) or ((Length(CurrLine) > 0) and
      (FUndoText[i] = CurrLine[Memo.CurX])) then
      Memo.BackSpace;
  end;
  {$ENDIF}
end;

procedure TInsertCharUndo.PerformRedo;
var
  i: Integer;
begin
  with Memo do
  begin
    {$IFDEF DELPHI_LLVM}
    for i := 0 to Length(FUndoText) - 1 do
    {$ELSE}
    for i := 1 to Length(FUndoText) do
    {$ENDIF}
    begin
      if FUndoText[i] = #13 then
        NewLine
      else
        InsertChar(FUndoText[i]);
    end;
  end;
end;

function TInsertCharUndo.Append(NewUndo: TUndo): boolean;
begin
  Result := False;
  if not ((NewUndo is TInsertCharUndo) and
    (NewUndo.UndoCurX0 = FUndoCurX) and
    (NewUndo.UndoCurY0 = FUndoCurY)) then Exit;
  FUndoText := FUndoText + NewUndo.FUndoText;
  FUndoCurX := NewUndo.UndoCurX;
  FUndoCurY := NewUndo.UndoCurY;
  Result := True;
end;

procedure TDeleteCharUndo.PerformUndo;
var
  i: integer;
begin
  with Memo do
  begin
    BlockUpdate;
    {$IFDEF DELPHI_LLVM}
    for i := 0 to Length(FUndoText) - 1 do
    {$ELSE}
    for i := 1 to Length(FUndoText) do
    {$ENDIF}
    begin
      if not FIsBackspace then
      begin
        Memo.CurY := FUndoCurY0;
        Memo.CurX := FUndoCurX0;
      end;
      if FUndoText[i] = #13 then
        NewLine
      else
      begin
        InsertChar(FUndoText[i]);
      end;
    end;
    EndBlockUpdate;
  end;
end;

procedure TDeleteCharUndo.PerformRedo;
var
  i: integer;
begin
  with Memo do
  begin
    {$IFDEF DELPHI_LLVM}
    for i := 0 to Length(FUndoText) - 1 do
    {$ELSE}
    for i := 1 to Length(FUndoText) do
    {$ENDIF}
    begin
      if FIsBackspace then
        BackSpace
      else
        DeleteChar(-1, -1);
    end;
  end;
end;

function TDeleteCharUndo.Append(NewUndo: TUndo): boolean;
begin
  Result := False;
  if not ((NewUndo is TDeleteCharUndo) and
    (NewUndo.UndoCurX0 = FUndoCurX) and
    (NewUndo.UndoCurY0 = FUndoCurY)) then Exit;
  if TDeleteCharUndo(NewUndo).FIsBackspace <> FIsBackspace then Exit;
  FUndoText := NewUndo.FUndoText + FUndoText;
  FUndoCurX := NewUndo.UndoCurX;
  FUndoCurY := NewUndo.UndoCurY;
  Result := True;
end;

procedure TDeleteLineUndo.PerformUndo;
begin
  with Memo do
  begin
    ClearSelection;
    SetSelText(PChar(FUndoText + #13#10));
  end;
end;

procedure TDeleteLineUndo.PerformRedo;
begin
  Memo.DeleteLine;
end;

procedure TDeleteBufUndo.PerformUndo;
begin
  with Memo do
  begin
    ClearSelection;

    SetSelText(PChar(FUndoText));

    SetFocus;

    FSelStartX := FUndoSelStartX;
    FSelStartY := FUndoSelStartY;
    FSelEndX := FUndoSelEndX;
    FSelEndY := FUndoSelEndY;
    SelectionChanged;
  end;
end;

procedure TDeleteBufUndo.PerformRedo;
begin
  with Memo do
  begin
    FSelStartX := FUndoSelStartX;
    FSelStartY := FUndoSelStartY;
    FSelEndX := FUndoSelEndX;
    FSelEndY := FUndoSelEndY;
    DeleteSelectionInt;
    SelectionChanged;
  end;
end;

constructor TInsertOverwriteUndo.Create(AInsertMode: boolean);
begin
  inherited Create(0,0,0,0,'');
  FInsertMode := AInsertMode;
end;

procedure TInsertOverwriteUndo.PerformRedo;
begin
  Memo.Overwrite := not FInsertMode;
end;

procedure TInsertOverwriteUndo.PerformUndo;
begin
  Memo.Overwrite := FInsertMode;
end;

constructor TIndentUndo.Create(SelFrom, SelTo, Indent: integer);
begin
  inherited Create(0,0,0,0,'');
  SelRowFrom := SelFrom;
  SelRowTo := SelTo;
  FIndent := Indent;
end;

procedure TPasteUndo.PerformUndo;
begin
  with Memo do
  begin
    FSelStartX := FUndoCurX0;
    FSelStartY := FUndoCurY0;
    FSelEndX := FUndoCurX;
    FSelEndY := FUndoCurY;
    DeleteSelectionInt;
  end;
end;

constructor TPasteUndo.Create(ACurX0, ACurY0, ACurX, ACurY: integer;
  AText: string; IsLinked: boolean);
begin
  inherited Create(ACurX0, ACurY0, ACurX, ACurY, AText);
  FLinkedUndo := IsLinked;
end;

procedure TPasteUndo.PerformRedo;
begin
  with Memo do
  begin
    ClearSelection;
    SetSelText(PChar(FUndoText));
  end;
end;

{ TCorrectUndo }

constructor TCorrectUndo.Create(LineNo: integer; UndoLine: string);
begin
  inherited Create(-1,-1,-1,-1,'');
  FUndoCurY := LineNo;
  FUndoText := UndoLine;
end;

procedure TCorrectUndo.PerformRedo;
begin
  PerformUndo;
end;

procedure TCorrectUndo.PerformUndo;
var
  s: string;
begin
  with Memo do
  begin
    s := Lines[CurY];
    Lines[CurY] := FUndoText;
    FUndoText := s;
  end;
end;

constructor TAdvUndoList.Create;
begin
  inherited;
  FPos := 0;
  FIsPerforming := False;
  FLimit := 100;
end;

destructor TAdvUndoList.Destroy;
begin
  Clear;
  inherited;
end;

function TAdvUndoList.Add(AItem: TUndo): integer;
begin
  Result := -1;
  if FIsPerforming or ((Count > 0) and Items[0].Append(AItem)) then
  begin
    AItem.Free;
    Exit;
  end;

  AItem.Memo := Self.Memo;
  if FPos > 0 then
  begin
    while FPos > 0 do
    begin
      Delete(0);
      Dec(FPos);
    end;
  end;

  Insert(0, AItem);
  if Count > FLimit then
    Delete(Count - 1);

  Memo.UndoChange;
  Result := 0;
end;

procedure TAdvUndoList.Clear;
begin
  while (Count > 0) do
    Delete(0);

  FPos := 0;
  if Assigned(Memo) then
    if not (csDestroying in Memo.ComponentState) then
      Memo.UndoChange;
end;

procedure TAdvUndoList.Delete(Index: integer);
begin
  {$IFNDEF DELPHI_LLVM}
  TUndo(Items[Index]).Free;
  {$ENDIF}
  inherited;
end;

procedure TAdvUndoList.Undo;
var
  OldAutoIndent: boolean;
begin
  if FPos < Count then
  begin
    OldAutoIndent := Memo.AutoIndent;
    Memo.AutoIndent := False;
    FIsPerforming := True;
    Items[FPos].Undo;

    if Items[FPos].FLinkedUndo then
    begin
      Inc(FPos);
      Items[FPos].Undo;
    end;

    Inc(FPos);
    FIsPerforming := False;
    Memo.AutoIndent := OldAutoIndent;
    Memo.UndoChange;
  end;
end;

procedure TAdvUndoList.Redo;
var
  OldAutoIndent: boolean;
begin
  if FPos > 0 then
  begin
    OldAutoIndent := Memo.AutoIndent;
    Memo.AutoIndent := False;
    FIsPerforming := True;

    Dec(FPos);
    Items[FPos].Redo;

    if (FPos > 0) and Items[FPos - 1].FLinkedUndo then
    begin
      Dec(FPos);
      Items[FPos].Redo;
    end;


    FIsPerforming := False;
    Memo.AutoIndent := OldAutoIndent;
    Memo.UndoChange;
  end;
end;

procedure TAdvUndoList.SetLimit(Value: integer);
begin
  if FLimit <> Value then
  begin
    if Value <= 0 then Value := 10;
    if Value > 0 then Value := 100;
    FLimit := Value;
    Clear;
  end;
end;

{ TAutoCompletion }

procedure TAutoCompletion.Assign(Source: TPersistent);
begin
  if (Source is TAutoCompletion) then
  begin
    FActive := (Source as TAutoCompletion).Active;
    FHeight := (Source as TAutoCompletion).Height;
    FSizeDropDown := (Source as TAutoCompletion).SizeDropDown;
    FStartToken := (Source as TAutoCompletion).StartToken;
    FWidth := (Source as TAutoCompletion).Width;
    FMaxWidth := (Source as TAutoCompletion).MaxWidth;
    FAutoWidth := (Source as TAutoCompletion).AutoWidth;
    FFromFirstChar := (Source as TAutoCompletion).FromFirstChar;
    FAutoCompletionCount := (Source as TAutoCompletion).AutoCompletionCount;
  end;
end;

constructor TAutoCompletion.Create(AOwner: TTMSFMXMemo);
begin
  inherited Create;
  FOwner := AOwner;
  FHeight := 100;
  FWidth := 200;
  FActive := True;
  FSizeDropDown := True;
  FMaxWidth := 0;
  FStartToken := '.';
  FAutoCompletionCount := 8;
  FAutoWidth := true;
  FFromFirstChar := true;
end;

destructor TAutoCompletion.Destroy;
begin
  inherited;
end;

{ TMemoComboListBox }

function TMemoComboListBox.AutoAdaptWidth: Single;
var
  T: TText;
  NewWidth, MinWidth: Single;
  O: TFMXObject;
  S: String;
  Item: TListBoxItem;
begin
  Result := 0;
  MinWidth := 0;
  T := nil;
  try
    T := TText.Create(nil);
    T.AutoSize := True;
    T.WordWrap := False;
    if Items.Count > 0 then
    begin
      Item := ListItems[0];
      Item.ApplyStyleLookup;
      O := Item.FindStyleResource('text');
      if O is TText then
        T.Font.Assign(TText(O).Font);
      NewWidth := 0;
      for S in Items do
      begin
        T.Text := S;
        T.AutoSize := False;
        T.AutoSize := True;
        if O <> nil then
          NewWidth := T.Width+TControl(O).Margins.Left+TControl(O).Margins.Right;
        if NewWidth > MinWidth then
          MinWidth := NewWidth;
      end;
    end;

    O := Content;
    if O is TControl then
      Result := Width-TControl(O).Width+MinWidth;

  finally
    T.Free;
  end;
end;


{ TTMSFMXMemoAction }

destructor TTMSFMXMemoAction.Destroy;
begin
  if Assigned(FMemo) then
    FMemo.RemoveFreeNotification(Self);
  inherited;
end;

function TTMSFMXMemoAction.HandlesTarget(Target: TObject): Boolean;
begin
  Result := Supported and Assigned(Memo);
end;

procedure TTMSFMXMemoAction.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (AComponent = Memo) then
    Memo := nil;
end;

procedure TTMSFMXMemoAction.UpdateTarget(Target: TObject);
begin
  if Assigned(Memo) and ((Self is TTMSFMXMemoCut) or (Self is TTMSFMXMemoCopy)) then
    Enabled := Memo.SelLength > 0;
end;

procedure TTMSFMXMemoAction.SetMemo(Value: TTMSFMXMemo);
begin
  if Value <> FMemo then
  begin
    FMemo := Value;
    if Value <> nil then
      Value.FreeNotification(Self);
  end;
end;

{ TTMSFMXMemoCopy }

procedure TTMSFMXMemoCopy.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.CopyToClipboard;
end;

{ TTMSFMXMemoCut }

procedure TTMSFMXMemoCut.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.CutToClipboard;
end;

{ TTMSFMXMemoPaste }

procedure TTMSFMXMemoPaste.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.PasteFromClipboard;
end;

procedure TTMSFMXMemoPaste.UpdateTarget(Target: TObject);
var
  ClipService: IFMXClipboardService;
begin
  Enabled := False;
  if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
    Enabled := (ClipService.GetClipboard.Kind = TTypeKind.tkUString);
end;

{ TTMSFMXSelectAll }

procedure TTMSFMXMemoSelectAll.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.SelectAll;
end;

procedure TTMSFMXMemoSelectAll.UpdateTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Enabled := Length(Memo.Lines.Text) > 0
  else
    Enabled := False;
end;

{ TTMSFMXMemoEditUndo }

procedure TTMSFMXMemoUndo.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.Undo;
end;

procedure TTMSFMXMemoUndo.UpdateTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Enabled := Memo.CanUndo
  else
    Enabled := False;
end;

{ TTMSFMXMemoRedo }

procedure TTMSFMXMemoRedo.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.Redo;
end;

procedure TTMSFMXMemoRedo.UpdateTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Enabled := Memo.CanRedo
  else
    Enabled := False;
end;

{ TTMSFMXMemoDelete }

procedure TTMSFMXMemoDelete.ExecuteTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Memo.DeleteSelection;
end;

procedure TTMSFMXMemoDelete.UpdateTarget(Target: TObject);
begin
  if Assigned(Memo) then
    Enabled := Memo.SelLength > 0
  else
    Enabled := False;
end;

{ TTMSFMXGutter }

procedure TTMSFMXGutter.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXGutter) then
  begin
    FFont.Assign((Source as TTMSFMXGutter).Font);
    FDigitCount := (Source as TTMSFMXGutter).DigitCount;
    FNumberSuffix := (Source as TTMSFMXGutter).NumberSuffix;
    FLineNumberAt := (Source as TTMSFMXGutter).LineNumberAt;
    FLineNumberStart := (Source as TTMSFMXGutter).LineNumberStart;
    FShowLeadingZeros := (Source as TTMSFMXGutter).ShowLeadingZeros;
    FShowLineNumbers := (Source as TTMSFMXGutter).ShowLineNumbers;
  end;
end;

procedure TTMSFMXGutter.Changed;
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

constructor TTMSFMXGutter.Create(AOwner: TTMSFMXMemo);
begin
  inherited Create;
  FOwner := AOwner;
  FFont := TFont.Create;
  FFont.OnChanged := FontChanged;

  if (csDesigning in FOwner.ComponentState) and not
    ((csReading in FOwner.Owner.ComponentState) or (csLoading in FOwner.Owner.ComponentState)) then
  begin
    {$IFDEF DELPHI_LLVM}
    FFont.Family := 'Courier';
    {$ELSE}
    FFont.Family := 'Courier New';
    {$ENDIF}
    FFont.Size := 12;
  end;

  FShowLineNumbers := True;
  FShowLeadingZeros := false;
  FDigitCount := 4;
  FLineNumberAt := 1;
  FLineNumberStart := 1;
end;

destructor TTMSFMXGutter.Destroy;
begin
  FFont.Free;
  inherited;
end;

procedure TTMSFMXGutter.FontChanged(Sender: TObject);
begin
  Changed;
end;

procedure TTMSFMXGutter.SetDigitCount(const Value: integer);
begin
  if (FDigitCount <> Value) then
  begin
    FDigitCount := Value;
    Changed;
  end;
end;

procedure TTMSFMXGutter.SetFont(const Value: TFont);
begin
  FFont := Value;
end;

procedure TTMSFMXGutter.SetLineNumberAt(const Value: Integer);
begin
  if (FLineNumberAt <> Value) and (Value > 0) then
  begin
    FLineNumberAt := Value;
    Changed;
  end;
end;

procedure TTMSFMXGutter.SetLineNumberStart(const Value: Integer);
begin
  if (FLineNumberStart <> Value) then
  begin
    FLineNumberStart := Value;
    Changed;
  end;
end;

procedure TTMSFMXGutter.SetNumberSuffix(const Value: string);
begin
  if (FNumberSuffix <> Value) then
  begin
    FNumberSuffix := Value;
    Changed;
  end;
end;

procedure TTMSFMXGutter.SetShowLeadingZeros(const Value: boolean);
begin
  if (FShowLeadingZeros <> Value) then
  begin
    FShowLeadingZeros := Value;
    Changed;
  end;
end;

procedure TTMSFMXGutter.SetShowLineNumbers(const Value: Boolean);
begin
  if (FShowLineNumbers <> Value) then
  begin
    FShowLineNumbers := Value;
    Changed;
  end;
end;

{ TTMSFMXActiveLineSettings }

procedure TTMSFMXActiveLineSettings.Assign(Source: TPersistent);
begin
  if (Source is TTMSFMXActiveLineSettings) then
  begin
    FActiveLineAtCursor := (Source as TTMSFMXActiveLineSettings).ActiveLineAtCursor;
    FShowActiveLine := (Source as TTMSFMXActiveLineSettings).ShowActiveLine;
    FShowActiveLineIndicator := (Source as TTMSFMXActiveLineSettings).ShowActiveLineIndicator;
  end;
end;

constructor TTMSFMXActiveLineSettings.Create(AOwner: TTMSFMXMemo);
begin
  inherited Create;
  FMemo := AOwner;
  FActiveLineAtCursor := true;
  FShowActiveLine := false;
  FShowActiveLineIndicator := false;
end;

procedure TTMSFMXActiveLineSettings.SetActiveLineAtCursor(const Value: Boolean);
begin
  if (Value <> FActiveLineAtCursor) then
  begin
    FActiveLineAtCursor := Value;
    FMemo.Repaint;
  end;
end;

procedure TTMSFMXActiveLineSettings.SetShowActiveLine(const Value: boolean);
begin
  if (Value <> FShowActiveLine) then
  begin
    FShowActiveLine := Value;
    FMemo.Repaint;
  end;
end;

procedure TTMSFMXActiveLineSettings.SetShowActiveLineIndicator(
  const Value: boolean);
begin
  if (Value <> FShowActiveLineIndicator) then
  begin
    FShowActiveLineIndicator := Value;
    FMemo.Repaint;
  end;
end;


procedure TIndentUndo.PerformRedo;
begin
  Memo.BlockIndent(SelRowFrom, SelRowTo, Indent, False);
end;

procedure TIndentUndo.PerformUndo;
begin
  Memo.BlockIndent(SelRowFrom, SelRowTo, -Indent, False);
end;

initialization
  GlobalUseDX10Software := True;

end.
