{********************************************************************}
{                                                                    }
{ written by TMS Software                                            }
{            copyright © 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.TMSEdit;

interface

{$I TMSDEFS.INC}

uses
  {$IFNDEF IOS}
  {$IFNDEF MACOS}
  {$IFNDEF ANDROID}
  Windows,
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}
  Classes, FMX.Edit, FMX.Types, Character, Types, FMX.Objects, FMX.Controls, UITypes, FMX.ListBox,
  FMX.Forms, Math, FMX.TMSBitmap, FMX.TMSBaseControl, FMX.TMSPopup, FMX.StdCtrls, UIConsts, FMX.Styles
  {$IFDEF DELPHIXE5_LVL}
  , FMX.Graphics
  {$ENDIF}
  ;

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

  // version history
  // v1.0.0.0 : first release
  // v1.0.0.1 : Fixed : Issue with dropdown dblclick
  //          : Fixed : Issue with dropdown access violation
  // v1.0.1.0 : New : OnButtonClick event
  // v1.5.0.0 : New : support for XE4
  // v1.5.0.1 : Fixed : Issue with Form Key handling
  // v1.5.0.2 : Fixed : Issue with lookup list on Mac
  // v1.5.1.0 : New : Width and Height properties on lookup list
  //          : Fixed : Issue with lookup autosize calculation
  // v1.5.1.1 : Fixed : Issue with LiveBindings edit link
  // v1.5.1.2 : Fixed : Issue with scrollbox and applystyle empty text initialization
  // v1.5.1.3 : Fixed : Issue with displaylist on XE5
  // v1.5.1.4 : Improved : Signed numeric not being handled in GetInt/SetInt

const
  LOOKUPITEMHEIGHT = 12;

type
  TTMSEditType = (etString, etNumeric, etSignedNumeric, etFloat, etSignedFloat, etUppercase, etMixedCase, etLowerCase,
    etMoney, etHex, etAlphaNumeric, etValidChars);

  TPrecisionDisplay = (pdNormal, pdShortest);

  TTMSFMXEdit = class;

  TLookupSettings = class(TPersistent)
  private
    FDisplayList: TStringList;
    FValueList: TStringList;
    FDisplayCount: Integer;
    FColor: TAlphaColor;
    FEnabled: Boolean;
    FNumChars: Integer;
    FCaseSensitive: Boolean;
    FHistory: Boolean;
    FMulti: Boolean;
    FSeparator: char;
    FAcceptOnTab: Boolean;
    FWidth: Integer;
    FHeight: Integer;
    procedure SetDisplayList(const Value: TStringList);
    procedure SetValueList(const Value: TStringList);
    procedure SetNumChars(const Value: Integer);
    procedure SetHeight(const Value: Integer);
    procedure SetWidth(const Value: Integer);
  protected
    property AcceptOnTab: boolean read FAcceptOnTab write FAcceptOnTab default False;
    property Color: TAlphaColor read FColor write FColor default claWhite;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property CaseSensitive: Boolean read FCaseSensitive write FCaseSensitive default False;
    property DisplayCount: Integer read FDisplayCount write FDisplayCount default 4;
    property DisplayList: TStringList read FDisplayList write SetDisplayList;
    property Enabled: Boolean read FEnabled write FEnabled default False;
    property History: Boolean read FHistory write FHistory default False;
    property NumChars: Integer read FNumChars write SetNumChars default 2;
    property ValueList: TStringList read FValueList write SetValueList;
    property Multi: Boolean read FMulti write FMulti default False;
    property Separator: char read FSeparator write FSeparator;
    property Width: Integer read FWidth write SetWidth default 0;
    property Height: Integer read FHeight write SetHeight default 0;
  end;

  TListBoxTab = class(TListBox)
  private
    FHandleTab: boolean;
    FColor: TAlphaColor;
    FMouseCaptured: Boolean;
    FEdit: TTMSFMXEdit;
    procedure SetColor(const Value: TAlphaColor);
  protected
    function GetBackGround: TRectangle;
    procedure UpdateBkgColor;
    procedure SetCapture;
    procedure ReleaseCaptureEx;
    procedure CloseLookup;
    property Edit: TTMSFMXEdit read FEdit write FEdit;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; 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;
  published
    property Color: TAlphaColor read FColor write SetColor;
    property HandleTab: boolean read FHandleTab write FHandleTab;
  end;

  {$IFDEF DELPHI_LLVM}
  { TListHintWindow }
  TListWindow = class(TTMSFMXPopup)
  {$ELSE}
  TListWindow = class(TCustomForm)
  {$ENDIF}
  private
    FListControl: TCustomListBox;
  protected
    {$IFNDEF DELPHI_LLVM}
    procedure SetActive(const Value: Boolean); override;
    {$ENDIF}
  published
    property ListControl: TCustomListBox read FListControl write FListControl;
  end;

  TLookupSelectEvent = procedure(Sender: TObject; var Value: string) of object;
  TLookupIndexSelectEvent = procedure(Sender: TObject; Index: Integer; var Value: string) of object;

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXEdit = class(TEdit)
  private
    FEditType: TTMSEditType;
    FMaxLength: integer;
    FValidChars: string;
    FEmptyTextCtrl: TText;
    FEmptyText: string;
    FLookupList: TListWindow;
    FLookupListBox: TListboxTab;
    FLookup: TLookupSettings;
    FOnLookupSelect: TLookupSelectEvent;
    FOnLookupIndexSelect: TLookupIndexSelectEvent;
    FSigned: Boolean;
    FPrefix: string;
    FSuffix: string;
    FIsModified: Boolean;
    FLengthLimit: smallint;
    FFullTextSearch: boolean;
    FAutoThousandSeparator: Boolean;
    FPrecision: smallint;
    FPrecisionDisplay: TPrecisionDisplay;
    FBlockChange: Boolean;
    FAllowNumericNullValue: Boolean;
    FAutoComplete: Boolean;
    FOnCloseWithEscape: TNotifyEvent;
    FCanEditObserve: Boolean;
    function GetVersion: String;
    function GetVersionNr: Integer;
    procedure SetVersion(const Value: String);
    procedure ListKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);
    procedure ListMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure SetEmptyText(const Value: string);
    procedure CloseLookup;
    procedure DoneLookup;
    procedure UpdateLookup;
    procedure UpdateAutoComplete;
    procedure SetLookup(const Value: TLookupSettings);
    function AllowMin(ch: char): boolean;
    function FixedLength(s: string): Integer;
    procedure SetPrefix(const Value: string);
    procedure SetSuffix(const Value: string);
    procedure SetModified(const Value: Boolean);
    function DecimalPos: Integer;
    procedure AutoSeparators;
    function GetFloat: double;
    procedure SetFloat(const Value: double);
    procedure SetAutoThousandSeparator(const Value: Boolean);
    procedure SetPrecision(const Value: smallint);
    procedure SetPrecisionDisplay(const Value: TPrecisionDisplay);
    function EStrToFloat(s: string): extended;
    function GetInt: Integer;
    procedure SetInt(const Value: Integer);
    procedure SetEditType(const Value: TTMSEditType);
    function IsLookupListVisible: Boolean;
  protected
    function CanObserve(const ID: Integer): Boolean; override;
    property OnCloseWithEscape: TNotifyEvent read FOnCloseWithEscape write FOnCloseWithEscape;
    function GetDefaultStyleLookupName: string; override;
    procedure SetText(const Value: string); override;
    procedure InsertText(const AText: string);
    function GetStyleObject: TFMXObject; override;
    procedure ApplyStyle; override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Single); override;
    procedure KeyDown(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;
    procedure KeyUp(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;
    procedure DoEnter; override;
    procedure DoExit; override;

    //--- TODO: Recheck before publish
    property Signed: Boolean read FSigned write FSigned default False;
    property Prefix: string read FPrefix write SetPrefix;
    property Suffix: string read FSuffix write SetSuffix;
  public
    function LookupListbox: TListBoxTab;
    function LookupList: TListWindow;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property Modified: Boolean read FIsModified write SetModified;
    property FloatValue: double read GetFloat write SetFloat;
    property IntValue: Integer read GetInt write SetInt;
    property CanEditObserve: Boolean read FCanEditObserve write FCanEditObserve;
  published
    property Version: String read GetVersion write SetVersion;
    property AutoComplete: Boolean read FAutoComplete write FAutoComplete default False;
    property EditType: TTMSEditType read FEditType write SetEditType default etString;
    property EmptyText: string read FEmptyText write SetEmptyText;
    property Lookup: TLookupSettings read FLookup write SetLookup;
    property MaxLength: integer read FMaxLength write FMaxLength default 0;
    property ValidChars: string read FValidChars write FValidChars;
    property AllowNumericNullValue: Boolean read FAllowNumericNullValue write FAllowNumericNullValue default False;
    property AutoThousandSeparator: Boolean read FAutoThousandSeparator write SetAutoThousandSeparator default True;
    property FullTextSearch: boolean read FFullTextSearch write FFullTextSearch default false;
    property LengthLimit: smallint read FLengthLimit write FLengthLimit default 0;
    property Precision: smallint read FPrecision write SetPrecision default 0;
    property PrecisionDisplay: TPrecisionDisplay read FPrecisionDisplay write SetPrecisionDisplay default pdNormal;
    property OnLookupSelect: TLookupSelectEvent read FOnLookupSelect write FOnLookupSelect;
    property OnLookupIndexSelect: TLookupIndexSelectEvent read FOnLookupIndexSelect write FOnLookupIndexSelect;
  end;

  TTMSFMXEditPopup = class(TTMSFMXPopup)
  private
   FEdit: TTMSFMXEdit;
  protected
    procedure DialogKey(var Key: Word; Shift: TShiftState); override;
  end;

  [ComponentPlatformsAttribute(TMSPlatforms)]
  TTMSFMXEditBtn = class(TTMSFMXEdit)
  private
    FBlockDropDown: Boolean;
    FPopup: TTMSFMXEditPopup;
    FButton: TButton;
    FPopupControl: TControl;
    FBitmap: TBitmap;
    FOnButtonClick: TNotifyEvent;
    procedure SetBitmap(const Value: TBitmap);
    function GetButton: TButton;
    function GetButtonBitmap: TTMSFMXBitmap;
  protected
    procedure KeyDown(var Key: Word; var KeyChar: WideChar; Shift: TShiftState); override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure PopupKeyDown(Sender: TObject; var Key: Word; var KeyChar: WideChar; Shift: TShiftState);
    procedure ButtonClick(Sender: TObject);
    procedure ButtonDblClick(Sender: TObject);
    function GetStyleObject: TFMXObject; override;
    procedure ApplyStyle; override;
    procedure AssignEvents;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure DropDown; virtual;
    property Popup: TTMSFMXEditPopup read FPopup write FPopup;
    property Button: TButton read GetButton;
    property ButtonBitmap: TTMSFMXBitmap read GetButtonBitmap;
  published
    property Bitmap: TBitmap read FBitmap write SetBitmap;
    property PopupControl: TControl read FPopupControl write FPopupControl;
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;


implementation

uses
  SysUtils, StrUtils, Variants,
  FMX.Platform, FMX.Layouts
  {$IFDEF MACOS}
  {$IFNDEF IOS}
  ,FMX.Platform.Mac, MacApi.AppKit, MacApi.CocoaTypes, MAcApi.Foundation
  {$ENDIF}
  {$ENDIF}
  ;

{$IFDEF MACOS}
{$IFNDEF IOS}
procedure ShowWindow(const AForm: TCommonCustomForm);
var
  NSWin: NSWindow;
  frame: NSRect;
  AutoReleasePool: NSAutoReleasePool;
begin
  AutoReleasePool := TNSAutoreleasePool.Create;
  try
    NSWin := WindowHandleToPlatform(AForm.Handle).Wnd;
    NSWin.orderFront(TNSApplication.OCClass.sharedApplication);
    if AForm = Application.MainForm then
      NSWin.makeMainWindow;
    frame := NSWin.frame;
      AForm.SetBounds(round(frame.origin.x),
        round(TNSScreen.Wrap(TNSScreen.OCClass.mainScreen).frame.size.height - frame.origin.y - frame.size.height),
        round(frame.size.width), round(frame.size.height))
  finally
    AutoReleasePool.release;
  end;
end;
{$ENDIF}
{$ENDIF}

type
  TAutoType = (atNumeric, atFloat, atString, atDate, atTime, atHex);

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

function CheckTerminator(ch: char): boolean;
begin
  {$IFDEF DELPHIXE6_LVL}
  Result := ch.IsInArray([' ', ',', '.', '/', '\', ':', '*', '$', '-', '(', ')', '[', ']', '"','''','&']);
  {$ELSE}
  Result := CharInSet(ch, [' ', ',', '.', '/', '\', ':', '*', '$', '-', '(', ')', '[', ']', '"','''','&']);
  {$ENDIF}
end;

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

function ShiftCase(AName: string): string;
var
  I, L: Integer;
  NewName: string;
  First: Boolean;
  lcase: string;
begin
  First := true;
  NewName := AName;
  L := Length(AName);

  lcase := lowercase(aname);

  for I := 1 to L do
  begin
    if CheckTerminator(NewName[I]) then
      First := true
    else
      if First then
      begin
        NewName[I] := AName[I].ToUpper;
        First := false;
      end
      else
        NewName[I] := AName[I].ToLower;

    if (Copy(NewName, 1, I) = 'Mc') or (Copy(NewName, 1, I) = 'Mac') or
      ((Pos(' Mc', NewName) = I - 2) and (I > 2)) or
      ((I > L - 3) and ((Copy(NewName, I - 1, 2) = ' I') or
      (Copy(NewName, I - 2, 3) = ' II'))) then
      First := true;
  end;
  ShiftCase := NewName;
end;

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

function IsType(s: string): TAutoType;
var
  i: Integer;
  isI, isF, isH: Boolean;
  th, de, mi: Integer;

begin
  Result := atString;

  isI := true;
  isF := true;
  isH := true;

  if s = '' then
  begin
    isI := false;
    isF := false;
    isH := false;
  end;

  th := -1; de := 0; mi := 0;

  for i := 1 to Length(s) do
  begin
    {$IFDEF DELPHIXE6_LVL}
    if not s[i].IsNumber then isI := false;
    if not (s[i].IsNumber or s[i].isInArray(['.',','])) then isF := false;
    if not (s[i].IsNumber or s[i].IsLetter) then isH := false;
    {$ELSE}
    if not (CharInSet(s[i], ['0'..'9'])) then isI := false;
    if not (CharInSet(s[i], ['0'..'9','.',','])) then isF := false;
    if not (CharInSet(s[i], ['0'..'9','a'..'f','A'..'F'])) then isH := false;
    {$ENDIF}

    if (s[i] = FormatSettings.ThousandSeparator) and (i - th < 3) then isF := false;

    if s[i] = FormatSettings.ThousandSeparator then th := i;
    if s[i] = FormatSettings.DecimalSeparator then inc(de);
    if s[i] = '-' then inc(mi);
  end;

  if isH and not isI then
    Result := atHex;

  if isI then
    Result := atNumeric
  else
  begin
    if isF then
      Result := atFloat;
  end;

  if (mi > 1) or (de > 1) then
    Result := atString;
end;

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

function StripThousandSep(s: string): string;
begin
  while (Pos(FormatSettings.ThousandSeparator, s) > 0) do
    Delete(s, Pos(FormatSettings.ThousandSeparator, s), 1);
  Result := s;
end;

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

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

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

function HexToInt(s: string): Integer;
var
  i: Integer;
  r, m: Integer;

  function CharVal(c: char): Integer;
  begin
    Result := 0;
    if ((c >= '0') and (c <= '9')) then Result := ord(c) - ord('0');
    if ((c >= 'A') and (c <= 'F')) then Result := ord(c) - ord('A') + 10;
    if ((c >= 'a') and (c <= 'f')) then Result := ord(c) - ord('a') + 10;
  end;

begin
  r := 0;
  m := 1;
  for i := Length(s) downto 1 do
  begin
    r := r + m * CharVal(s[i]);
    m := m shl 4;
  end;
  Result := r;
end;

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

function ValStr(s: string): Integer;
var
  err: Integer;
begin
  val(s, result, err);
end;

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

function CleanString(s: string): string;
var
  i: Integer;
begin
  i := pos(#13, s);
  if i > 0 then
    delete(s, i, 1);
  i := pos(#10, s);
  if i > 0 then
    delete(s, i, 1);
  i := pos(#11, s);
  if i > 0 then
    delete(s, i, 1);

  Result := s;
end;

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

{ TTMSFMXEdit }

function TTMSFMXEdit.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 TTMSFMXEdit.GetVersionNr: integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

constructor TTMSFMXEdit.Create(AOwner: TComponent);
begin
  inherited;
  FCanEditObserve := True;
  FEmptyTextCtrl := nil;
  FAutoThousandSeparator := True;
  FLengthLimit := 0;
  FAutoComplete := False;

  FLookupList := nil;
  FLookupListBox := nil;
  FLookup := TLookupSettings.Create;
end;

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

destructor TTMSFMXEdit.Destroy;
begin
  FLookup.Free;
  if Assigned(FLookupList) then
  begin
    FLookupList.Free;
    FLookupList := nil;
  end;
  inherited;
end;

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

procedure TTMSFMXEdit.DoEnter;
begin
  inherited;
  if Assigned(FEmptyTextCtrl) then
    FEmptyTextCtrl.Visible := false;
end;

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

procedure TTMSFMXEdit.DoExit;
var
  TT: string;
  OldModified: Boolean;
begin
  inherited;
  if Assigned(FEmptyTextCtrl) and (Text = '') and (EmptyText <> '') then
    FEmptyTextCtrl.Visible := true;

  if (csLoading in ComponentState) then
    Exit;

  if IsLookupListVisible then
    FLookupList.Hide;

  if (FPrecision > 0) and (EditType in [etFloat, etSignedFloat, etMoney]) then
  begin
    if (self.Text <> '') or not FAllowNumericNullValue then
    begin
      // update for precision
      OldModified := Modified;
      Floatvalue := self.Floatvalue;
      Modified := OldModified;
    end;
  end;

  if (EditType in [etNumeric]) and (Self.Text = '') and not FAllowNumericNullValue then
    Text := '0';

  if (EditType in [etFloat, etSignedFloat, etMoney]) and (Self.Text = '') and not FAllowNumericNullValue  then
    Floatvalue := 0.0;

  TT := Trim(Text);
  if FLookup.Enabled and FLookup.History and (TT <> '') then
  begin
    if FLookup.DisplayList.IndexOf(TT) = -1 then
      FLookup.DisplayList.Add(TT);
  end;
end;

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

procedure TTMSFMXEdit.InsertText(const AText: string);
var
  TmpS: string;
begin
  if ReadOnly then
    Exit;

  TmpS := Text;
  Delete(TmpS, SelStart + 1, SelLength);
  Insert(AText, TmpS, SelStart + 1);
  if (MaxLength <= 0) or (Length(TmpS) <= MaxLength) then
  begin
    Text := TmpS;
    FNeedChange := True;
    CaretPosition := SelStart + Length(AText);
  end;
  SelLength := 0;
end;

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

function TTMSFMXEdit.IsLookupListVisible: Boolean;
begin
  Result := Assigned(FLookupList) and FLookupList.Visible;
end;

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

procedure TTMSFMXEdit.SetText(const Value: string);
var
  fmt, neg: string;
  f: extended;
  Val: string;
begin
  Val := Value;
  if (value = '') then
  begin
    if not AllowNumericNullValue then
    begin
    case FEditType of
      etFloat, etSignedFloat: if not (IsType(value) in [atFloat, atNumeric]) then val := '0';
      etMoney: if not (IsType(value) in [atFloat, atNumeric]) then val := '0';
      etHex: if not (IsType(value) in [atHex, atNumeric]) then val := '0';
      etNumeric: if not (IsType(value) in [atNumeric]) then val := '0';
    end;
    end;

    if Assigned(FEmptyTextCtrl) and (EmptyText <> '') then
      FEmptyTextCtrl.Visible := true;
  end
  else
  begin
    if Assigned(FEmptyTextCtrl) and (EmptyText <> '') then
      FEmptyTextCtrl.Visible := false;
  end;

  if PrecisionDisplay = pdNormal then
  begin
    if ((FPrecision > 0) or (FEditType = etMoney)) and (val <> '') then
    begin
      if (FEditType in [etMoney]) then
      begin
        if (Pos('-', val) > 0) then neg := '-' else neg := '';
        if AutoThousandSeparator then
          fmt := '%.' + IntToStr(FPrecision) + 'n'
        else
          fmt := '%.' + IntToStr(FPrecision) + 'f';

        Val := Format(fmt, [EStrToFloat(Val)]);
      end;

      if (FEditType in [etFloat, etSignedFloat]) then
      begin
        fmt := '%.' + inttostr(FPrecision) + 'f';
        f := EStrToFloat(val);
        Val := Format(fmt, [f]);
      end;
    end;
  end
  else
  begin
    if (FEditType in [etFloat, etSignedFloat, etMoney]) then
    begin
      f := EStrToFloat(Val);
      Val := Format('%g', [f]);
    end;
  end;

  if (FEditType in [etHex]) then
    Val := AnsiUpperCase(val);

  inherited SetText(FPrefix + Val + FSuffix);

  SetModified(False);
end;

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

end;

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

procedure TTMSFMXEdit.ApplyStyle;
var
  c: TFmxObject;
begin
  inherited;

  if Assigned(FEmptyTextCtrl) then
  begin
    FEmptyTextCtrl.Text := FEmptyText;
    {$IFDEF DELPHIXE6_LVL}
    FEmptyTextCtrl.HorzTextAlign := TTextAlign.Leading;
    FEmptyTextCtrl.Align := TAlignLayout.Client;
    {$ELSE}
    FEmptyTextCtrl.HorzTextAlign := TTextAlign.taLeading;
    FEmptyTextCtrl.Align := TAlignLayout.alClient;
    {$ENDIF}
    FEmptyTextCtrl.Color := claSilver;
    FEmptyTextCtrl.HitTest := false;
    FEmptyTextCtrl.CanFocus := false;
    FEmptyTextCtrl.Visible := not IsFocused and (FEmptyText <> '') and (Text = '');
  end;

  c := FindStyleResource('content');
  if Assigned(c) and (c is TControl) then
    {$IFDEF DELPHIXE6_LVL}
    (c as TControl).Align := TAlignLayout.Client;
    {$ELSE}
    (c as TControl).Align := TAlignLayout.alClient;
    {$ENDIF}
end;

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

function TTMSFMXEdit.GetDefaultStyleLookupName: string;
begin
  Result := 'Editstyle';
end;

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

function TTMSFMXEdit.GetStyleObject: TFMXObject;
var
  T,E: TFmxObject;
  //strm: TStringStream;
begin
  Result := inherited GetStyleObject;
  if Assigned(Result) then
  begin
    T := Result.FindStyleResource('content');
    E := Result.FindStyleResource('emptytext');

    if Assigned(T) then
    begin
      if not Assigned(E) then
      begin
        FEmptyTextCtrl := TText.Create(Self);
        FEmptyTextCtrl.StyleName := 'emptytext';
        T.InsertObject(T.ChildrenCount, FEmptyTextCtrl);
        FEmptyTextCtrl.Width := 83;
        FEmptyTextCtrl.Height := 16;
      end
      else
        FEmptyTextCtrl := TText(E);
    end;

    if not (csDesigning in ComponentState) then
    begin
      if Assigned(FLookupList) then
      begin
        FLookupList.Free;
        FLookupList := nil;
      end;

      if not Assigned(FLookupList) then
      begin
        {$IFDEF DELPHI_LLVM}
        FLookupList := TListWindow.Create(Self);
        {$ELSE}
        FLookupList := TListWindow.CreateNew(Self);
        {$ENDIF}
        {$IFNDEF DELPHIXE5_LVL}
        FLookupList.ShowActivated := False;
        FLookupList.StaysOpen := False;
        {$ELSE}
        {$IFNDEF DELPHI_LLVM}
        {$IFDEF DELPHIXE6_LVL}
        FLookupList.FormStyle := TFormStyle.Popup;
        {$ELSE}
        FLookupList.FormStyle := TFormStyle.fsPopup;
        {$ENDIF}
        {$ENDIF}
        {$ENDIF}
        {$IFNDEF DELPHI_LLVM}
        {$IFDEF DELPHIXE6_LVL}
        FLookupList.BorderStyle := TFmxFormBorderStyle.None;
        {$ELSE}
        FLookupList.BorderStyle := TFmxFormBorderStyle.bsNone;
        {$ENDIF}
        FLookupList.Transparency := True;
        {$ELSE}
        FLookupList.Transparent := True;
        {$ENDIF}
        {$IFDEF DELPHIXE6_LVL}
        FLookupList.Fill.Kind := TBrushKind.None;
        {$ELSE}
        FLookupList.Fill.Kind := TBrushKind.bkNone;
        {$ENDIF}
      end;

      E := Result.FindStyleResource('lookuplistbox');
      if not Assigned(E) then
      begin
        FLookupListbox := TListBoxTab.Create(FLookupList);
        FLookupListbox.StyleName := 'lookuplistbox';
        Result.InsertObject(Result.ChildrenCount, FLookupListBox);
        with FLookupListBox do
        begin
          Parent := Self;
          {$IFDEF DELPHIXE6_LVL}
          Align := TAlignLayout.Client;
          {$ELSE}
          Align := TAlignLayout.alClient;
          {$ENDIF}
          ItemHeight := LOOKUPITEMHEIGHT;
          CanFocus := True;
          TabOrder := 0;
          Visible := False;
          OnKeyUp := ListKeyUp;
          OnMouseUp := ListMouseUp;
        end;
        if Assigned(FLookupList) then
          FLookupList.ListControl := FLookupListBox;
      end;
    end;

    {
    // todo: del it
    strm := TStringStream.Create;
    Result.SaveToStream(strm);
    strm.SaveToFile('abcR.txt');
    }
  end;
end;

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

function TTMSFMXEdit.FixedLength(s: string): Integer;
var
  i: Integer;
begin
  s := StripThousandSep(s);
  i := Pos(FormatSettings.decimalseparator, s);
  if (i > 0) then Result := i else Result := Length(s) + 1;

  if Signed and (EditType in [etFloat, etSignedFloat, etNumeric, etMoney]) and (pos('-',s) > 0) then
    Result := Result - 1;
end;

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

function TTMSFMXEdit.AllowMin(ch: char): boolean;
begin
  Result := Signed and (EditType in [etFloat, etSignedFloat, etNumeric, etMoney]) and (ch = '-');
end;

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

procedure TTMSFMXEdit.KeyDown(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
var
  s,cl: string;
  allowclip: boolean;
  at: TAutoType;
  ismod: Boolean;
  d: Integer;
  ClipService: IFMXClipboardService;

  procedure EatKey;
  begin
    KeyChar := #0;
    Key := 0;
  end;

begin
  if not Assigned(FLookupListBox) then
  begin
    inherited;
    Exit;
  end;

  s := KeyChar;

  if (Key = vkMenu) then
  begin
    CloseLookup;
  end;

  if (Key = vkBack) and (FPrefix <> '') then
    if (SelStart <= Length(FPrefix)) and (SelLength = 0) then Exit;

  if (FLengthLimit > 0) and (FixedLength(self.Text) > FLengthLimit) and
    (SelLength = 0) and (SelStart < DecimalPos) and (Key <> vkBack) and (Key <> ord(FormatSettings.decimalseparator)) and not AllowMin(chr(Key)) then Exit;

  if (Key in [vkHOME, vkEND, vkTAB, vkLEFT, vkRIGHT, vkPRIOR, vkNEXT, vkDelete, vkBack]) then
  begin
    if (Key = vkBack) then
    begin
      s := self.Text;

      if SelLength = 0 then
        delete(s, SelStart - Length(fprefix), 1)
      else
        delete(s, Max(1, SelStart - Length(fprefix)), SelLength);

      ismod := (s <> Text) or Modified;
      Modified := ismod;

      inherited;

      if (EditType = etMoney) then
        AutoSeparators;

      if (Text = '') and ((s <> '') or ismod) then
        Change;

      Modified := ismod;

      UpdateLookup;
      Exit;
    end
    else
    begin
      if (Key in [vkHOME, vkEND, vkTAB, vkLEFT, vkRIGHT, vkPRIOR, vkNEXT]) then
      begin
        if AutoComplete and (SelLength > 0) and IsLookupListVisible then
          CloseLookup;
      end;

      inherited;
    end;

    Exit;
  end;

  if not IsLookupListVisible then
  begin
    if (Key = vkUp) or (Key = vkDown)  then
    begin
      //DoKeyDown(Msg);
      //msg.Result := 1;
      //Exit;
    end;
  end;

  if (Key = vkReturn) and IsLookupListVisible then
  begin
    DoneLookup;
    Exit;
  end;

  if (Key in [vkTab, vkReturn, vkEscape, vkF1, vkF2, vkF3, vkF5, vkF6,
    vkF7, vkF8, vkF9, vkF10, vkF11, vkF12]) {and IsCtrl} then
  begin
    inherited;
    Exit;
  end;

  if (key = vkHome) and IsLookupListVisible then
  begin
    FLookupListBox.ItemIndex := 0;
    Exit;
  end;

  if (Key = vkEnd) and IsLookupListVisible then
  begin
    FLookupListBox.ItemIndex := FLookupListBox.Items.Count - 1;
    Exit;
  end;

  if (Key = vkNext) and IsLookupListVisible then
  begin
    if FLookupListBox.ItemIndex + FLookup.DisplayCount < FLookupListBox.Items.Count then
      FLookupListBox.ItemIndex := FLookupListBox.ItemIndex + FLookup.DisplayCount
    else
      FLookupListBox.ItemIndex := FLookupListBox.Items.Count - 1;
    Exit;
  end;

  if (Key = vkPrior) and IsLookupListVisible then
  begin
    if FLookupListBox.ItemIndex > FLookup.DisplayCount then
      FLookupListBox.ItemIndex := FLookupListBox.ItemIndex - FLookup.DisplayCount
    else
      FLookupListBox.ItemIndex := 0;
    Exit;
  end;

  if (Key = vkUp) and IsLookupListVisible then
  begin
    if FLookupListBox.ItemIndex > 0 then
      FLookupListBox.ItemIndex := FLookupListBox.ItemIndex - 1;
    Exit;
  end;

  if (Key = vkDown) and IsLookupListVisible then
  begin
    if FLookupListBox.ItemIndex + 1 < FLookupListBox.Items.Count then
      FLookupListBox.ItemIndex := FLookupListBox.ItemIndex + 1;
    Exit;
  end;

  if (Key = vkEscape) and IsLookupListVisible then
  begin
    CloseLookup;
    Exit;
  end;

  if (Key = vkDelete) and (SelStart >= Length(FPrefix)) then
  begin
    s := self.text;
    if SelLength = 0 then
      Delete(s, SelStart - Length(fprefix) + 1, 1)
    else
      Delete(s, SelStart - Length(fprefix) + 1, SelLength);

    if (lengthlimit > 0) and (fixedLength(s) - 1 > lengthlimit) then
    begin
      EatKey;
      exit;
    end;
    SetModified(true);
  end;

  if (MaxLength > 0) and (SelLength = 0) and (Length(Text) >= MaxLength) then
    Exit;

  if (fPrefix <> '') and (SelStart < Length(fPrefix)) then
    SelStart := Length(fPrefix);

  if (Shift = [ssCtrl]) and (KeyChar = ^V) then
  begin
    if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(ClipService)) then
      cl := ClipService.GetClipboard.AsString;

    at := IsType(cl);

    allowclip := true;

    case FEditType of
      etNumeric: allowclip := at = atNumeric;
      etSignedNumeric: allowclip := at = atNumeric;
      etFloat, etSignedFloat: allowclip := at = atFloat;
      etUppercase: cl := Uppercase(cl);
      etMixedCase: cl := Shiftcase(cl);
      etLowerCase: cl := Lowercase(cl);
      etMoney: allowclip := at = atFloat;
      etHex: allowclip := at in [atHex, atNumeric];
    end;

    if Allowclip then
      InsertText(cl);
    Exit;
  end;

  case FEditType of
    etString:
      begin
      end;
    etNumeric:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not KeyChar.IsNumber then
        {$ELSE}
        if not (CharInSet(KeyChar, ['0'..'9'])) then
        {$ENDIF}
          EatKey;
      end;
    etSignedNumeric:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not ((KeyChar.IsNumber) or KeyChar.IsInArray(['-'])) then
        {$ELSE}
        if not (CharInSet(KeyChar, ['0'..'9','-'])) then
        {$ENDIF}
          EatKey;
      end;
    etFloat:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not ((KeyChar.IsNumber) or (KeyChar = FormatSettings.DecimalSeparator)) then
        {$ELSE}
        if not ((CharInSet(KeyChar, ['0'..'9'])) or (KeyChar = FormatSettings.DecimalSeparator)) then
        {$ENDIF}
          EatKey;
      end;
    etSignedFloat:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not (((KeyChar.IsNumber) or KeyChar.IsInArray(['-'])) or (KeyChar = FormatSettings.DecimalSeparator)) then
        {$ELSE}
        if not ((CharInSet(KeyChar, ['0'..'9','-'])) or (KeyChar = FormatSettings.DecimalSeparator)) then
        {$ENDIF}
          EatKey;
      end;
    etUppercase:
      begin
        s := Uppercase(s);
        KeyChar := s[1];
        Key := ord(KeyChar);
      end;
    etLowerCase:
      begin
        s := LowerCase(s);
        KeyChar := s[1];
        Key := ord(KeyChar);
      end;
    etMoney:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not (((KeyChar.IsNumber) or KeyChar.IsInArray(['-'])) or (KeyChar = FormatSettings.DecimalSeparator) or
        {$ELSE}
        if not ((CharInSet(KeyChar, ['0'..'9','-'])) or (KeyChar = FormatSettings.DecimalSeparator) or
        {$ENDIF}
         (KeyChar = FormatSettings.ThousandSeparator)) then
          EatKey;
      end;
    etHex:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not (KeyChar.IsNumber or KeyChar.IsLetter) then
        {$ELSE}
        if not (CharInSet(KeyChar, ['0'..'9','a'..'f','A'..'F'])) then
        {$ENDIF}
          EatKey;
      end;
    etAlphaNumeric:
      begin
        {$IFDEF DELPHIXE6_LVL}
        if not (KeyChar.IsNumber or KeyChar.IsLetter) then
        {$ELSE}
        if not (CharInSet(KeyChar, ['0'..'9','a'..'z','A'..'Z'])) then
        {$ENDIF}
          EatKey;
      end;
    etValidChars:
      begin
        if (FValidChars <> '') then
          if Pos(UpperCase(s), Uppercase(FValidChars)) = 0 then
            EatKey;
      end;
  end;

  s := self.Text;
  inherited;
  if Assigned(FEmptyTextCtrl) then
    FEmptyTextCtrl.Visible := (EmptyText <> '') and (Text = '');

  if (FEditType in [etFloat, etSignedFloat, etMoney]) then
  begin
    d := Length(Text) - Length(s);
    if (d > 1) then
      CaretPosition := CaretPosition + d;
  end;

  UpdateLookup;
end;

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

procedure TTMSFMXEdit.KeyUp(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
var
  oldSelStart: integer;
begin
  inherited;

  if (Key = vkTab) and IsLookupListVisible and (Lookup.AcceptOnTab) then
    DoneLookup;

  if EditType = etMixedCase then
  begin
    oldSelStart := SelStart;
    Text := ShiftCase(Text);
    SelStart := oldSelStart;
  end;

  if (KeyCHar <> '') and (KeyCHar <> #8) then
    UpdateAutoComplete;
end;

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

procedure TTMSFMXEdit.ListKeyUp(Sender: TObject; var Key: Word;
  var KeyChar: Char; Shift: TShiftState);
begin
  if Key = vkReturn then
    DoneLookup;

  if (Key = vkTab) and (Lookup.AcceptOnTab) then
    DoneLookup;
end;

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

procedure TTMSFMXEdit.ListMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  DoneLookup;
end;

function TTMSFMXEdit.LookupList: TListWindow;
begin
  Result := FLookupList;
end;

function TTMSFMXEdit.LookupListbox: TListBoxTab;
begin
  Result := FLookupListBox;
end;

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

procedure TTMSFMXEdit.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
end;

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

procedure TTMSFMXEdit.MouseMove(Shift: TShiftState; X, Y: Single);
begin
  inherited;

end;

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

procedure TTMSFMXEdit.SetEditType(const Value: TTMSEditType);
var
  at: TAutoType;
begin
  if FEditType <> Value then
  begin
    FEditType := Value;

    if (csLoading in ComponentState) then
      Exit;

    if (Text <> '') or (not AllowNumericNullValue) then
    begin
      at := IsType(self.Text);
      case FEditType of
        etHex: if not (at in [atNumeric, atHex]) then self.IntValue := 0;
        etNumeric: if (at <> atNumeric) then self.IntValue := 0;
        etFloat, etSignedFloat, etMoney: if not (at in [atFloat, atNumeric]) then self.FloatValue := 0.0;
      end;
    end;

    if (csDesigning in ComponentState) and not(csLoading in ComponentState) and ((FEditType = etFloat) or (FEditType = etSignedFloat)) and (Precision = 0) then
      Precision := 2;
  end;
end;

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

procedure TTMSFMXEdit.SetEmptyText(const Value: string);
begin
  FEmptyText := Value;

  if Assigned(FEmptyTextCtrl) then
    FEmptyTextCtrl.Text := Value;
end;

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

function TTMSFMXEdit.CanObserve(const ID: Integer): Boolean;
begin
  Result := inherited CanObserve(ID);
  Result := Result and CanEditObserve;
end;

procedure TTMSFMXEdit.CloseLookup;
begin
  Self.SetFocus;
  if Assigned(FLookupList) then
  begin
    FLookupList.Hide;
    FLookupListBox.Visible := False;
    FLookupList.RemoveObject(FLookupListBox);
    FLookupListBox.Parent := Self;
  end;
end;

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

procedure TTMSFMXEdit.DoneLookup;
var
  idx, vp: Integer;
  NewValue: string;
  LookupText, NewText: string;
begin
  if not Assigned(FLookupListBox) then
    Exit;

  if (FlookupListBox.ItemIndex <> -1) then
  begin
    idx := Integer(FLookupListBox.Items.Objects[FlookupListBox.ItemIndex]);

    if (idx >= 0) and (idx < FLookup.ValueList.Count) then
      NewValue := FLookup.ValueList.Strings[idx]
    else
      NewValue := FLookupListbox.Items[FLookupListBox.ItemIndex];

    if Assigned(FOnLookupSelect) then
      FOnLookupSelect(Self, NewValue);

    if Assigned(FOnlookupIndexSelect) then
      FOnLookupIndexSelect(Self, idx, NewValue);

    if FLookup.Multi then
    begin
      NewValue := NewValue + FLookup.Separator;
      LookupText := Text; // get current text value & strip till last lookup part
      NewText := '';
      while VarPos(FLookup.Separator, LookupText, vp) > 0 do
      begin
        NewText := NewText + Copy(LookupText, 1, vp);
        Delete(LookupText, 1, vp);
      end;
      Text := NewText + NewValue;

    end
    else
      Text := NewValue;

    if FLookup.Multi then
      SelStart := length(Text)
    else
      SelectAll;


    CloseLookup;
  end;
end;

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

procedure TTMSFMXEdit.SetLookup(const Value: TLookupSettings);
begin
  FLookup.Assign(Value);
end;

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

procedure TTMSFMXEdit.UpdateLookup;
var
  pf: TPointF;
  pt: TPoint;
  i, cnt: Integer;
  mw, mh, tw, vp: Integer;
  LookupText: string;
  lx,ly: Integer;
  ismatch: Boolean;
begin
  if not FLookup.Enabled or not Assigned(FLookupListBox) then
    Exit;

  //tp := 0;

  if FLookup.Multi then
  begin
    LookupText := Text;
    while VarPos(FLookup.Separator, LookupText, vp) > 0 do
    begin
      //tp := tp + vp;
      Delete(LookupText, 1, vp);
    end;
  end
  else
    LookupText := Text;

  if Length(LookupText) >= FLookup.NumChars then
  begin
    pf := LocalToScreen(PointF(0, 0));
    pt := Point(Round(pf.X), Round(pf.Y));

    if Assigned(FLookupList) then
    begin
      FLookupList.Free;
      FLookupList := nil;
    end;

    if not Assigned(FLookupList) then
    begin
      {$IFDEF DELPHI_LLVM}
      FLookupList := TListWindow.Create(Self);
      {$ELSE}
      FLookupList := TListWindow.CreateNew(Self);
      {$ENDIF}

      {$IFNDEF DELPHIXE5_LVL}
      {$IFNDEF DELPHI_LLVM}
      FLookupList.TopMost := True;
      {$ENDIF}
      FLookupList.ShowActivated := False;
      FLookupList.StaysOpen := False;
      {$ENDIF}

      {$IFNDEF DELPHI_LLVM}
      {$IFDEF DELPHIXE6_LVL}
      FLookupList.BorderStyle := TFmxFormBorderStyle.None;
      {$ELSE}
      FLookupList.BorderStyle := TFmxFormBorderStyle.bsNone;
      {$ENDIF}
      FLookupList.Transparency := True;
      {$ELSE}
      FLookupList.Transparent := True;
      {$ENDIF}

      {$IFDEF DELPHIXE6_LVL}
      FLookupList.Fill.Kind := TBrushKind.None;
      {$ELSE}
      FLookupList.Fill.Kind := TBrushKind.bkNone;
      {$ENDIF}

      FLookupListBox := TListBoxTab.Create(Self);
      with FLookupListBox do
      begin
        Parent := Self;
        {$IFDEF DELPHIXE6_LVL}
        Align := TAlignLayout.Client;
        {$ELSE}
        Align := TAlignLayout.alClient;
        {$ENDIF}
        ItemHeight := LOOKUPITEMHEIGHT;
        CanFocus := True;
        TabOrder := 0;
        Visible := False;
        OnKeyUp := ListKeyUp;
        OnMouseUp := ListMouseUp;
        FLookupList.ListControl := FLookupListBox;
      end;
    end;

    FLookupList.ListControl := FLookupListBox;
    FLookupList.Fill.Color := FLookup.Color;
    {$IFDEF DELPHIXE6_LVL}
    FLookupList.Fill.Kind := TBrushKind.None;
    {$ELSE}
    FLookupList.Fill.Kind := TBrushKind.bkNone;
    {$ENDIF}

    FLookupListbox.Color := FLookup.Color;
    FLookupListBox.HandleTab := Lookup.AcceptOnTab;

    mh := (12 * FLookup.DisplayCount) + 4;

    ly := pt.Y + Round(Height) - 6;

    lx := pt.X + 8;


    FLookupList.Fill.Color := claWhite;

    cnt := 0;
    FLookupListBox.Items.Clear;

    for i := 1 to FLookup.DisplayList.Count do
    begin
      if FLookup.CaseSensitive then
      begin
        if Pos(LookupText, FLookup.FDisplayList.Strings[i - 1]) = 1 then
        begin
          FLookupListbox.Items.AddObject(CleanString(FLookup.FDisplayList.Strings[i - 1]), TObject(i - 1));
          inc(cnt);
        end;
      end
      else
      begin
        vp := Pos(AnsiUppercase(LookupText), AnsiUppercase(FLookup.FDisplayList.Strings[i - 1]));

        if FullTextSearch then
          ismatch := vp > 0
        else
          ismatch := vp = 1;

        if ismatch then
        begin
          FLookupListbox.Items.Add{Object}(CleanString(FLookup.FDisplayList.Strings[i - 1]){, TObject(i - 1)});
          inc(cnt);
        end;
      end;
    end;

    if FLookup.FDisplayList.Count > 0 then
      FLookupListBox.ItemIndex := 0;

    if cnt < FLookup.DisplayCount then
      mh := (cnt * LOOKUPITEMHEIGHT) + 4;

    FLookupListBox.Sorted := True;
    mw := 50;
    FLookupList.Width := 0;

    if cnt > 0 then
    begin
      for i := 1 to cnt do
      begin
        tw := Round(FLookupList.Canvas.TextWidth(FLookupListBox.Items[i - 1]));

        if tw > mw then
          mw := tw;
      end;

      mw := mw + 8;

      if cnt > FLookup.DisplayCount then
        mw := mw + 18;//GetSystemMetrics(SM_CXHSCROLL);
    end;

    if (FLookup.Width > 0) then
      mw := FLookup.Width;

    if (FLookup.Height > 0) then
      mh := FLookup.Height;

    if (cnt > 0) then
    begin
      FLookupList.Top := ly;
      FLookupList.Left := lx;
      {$IFNDEF DELPHIXE5_LVL}
      {$IFNDEF DELPHI_LLVM}
      FLookupList.TopMost := true;
      {$ENDIF}
      {$ENDIF}

      {$IFDEF DELPHIXE5_LVL}
      {$IFNDEF DELPHI_LLVM}
      {$IFDEF DELPHIXE6_LVL}
      FLookupList.FormStyle := TFormStyle.Popup;
      {$ELSE}
      FLookupList.FormStyle := TFormStyle.fsPopup;
      {$ENDIF}
      {$ENDIF}
      {$ENDIF}

      FLookupList.InsertObject(FLookupList.ChildrenCount, FLookupListBox);
      FLookupListBox.Parent := FLookupList;
      FLookupListBox.Visible := True;
      {$IFDEF MACOS}
        {$IFNDEF IOS}
        ShowWindow(FLookupList);
        {$ELSE}
        FLookupList.IsOpen := True;
        {$ENDIF}
      {$ELSE}
        {$IFNDEF DELPHI_LLVM}
        FLookupList.Show;
        {$ELSE}
        FLookupList.IsOpen := True;
        {$ENDIF}
      {$ENDIF}
      FLookupList.SetBounds(lx, ly, mw, mh);
    end
    else
    begin
      {$IFNDEF DELPHI_LLVM}
      FLookupList.Hide;
      {$ELSE}
      FLookupList.IsOpen := False;
      {$ENDIf}
    end;
  end
  else
  begin
    if IsLookupListVisible then
    begin
      FLookupList.Visible := False;
      FLookupList.Parent := nil;
    end;
  end;
end;

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

procedure TTMSFMXEdit.UpdateAutoComplete;
var
  s, ms: string;
  i, vp: Integer;
begin
  if not AutoComplete then
    Exit;

  s := Text;
  ms := '';
  if Length(s) >= 1 then
  begin
    for i := 1 to FLookup.DisplayList.Count do
    begin
      if FLookup.CaseSensitive then
      begin
        if Pos(s, FLookup.FDisplayList.Strings[i - 1]) = 1 then
        begin
          ms := FLookup.FDisplayList.Strings[i - 1];
          Break;
        end;
      end
      else
      begin
        vp := Pos(AnsiUppercase(s), AnsiUppercase(FLookup.FDisplayList.Strings[i - 1]));
        if (vp = 1) then
        begin
          ms := FLookup.FDisplayList.Strings[i - 1];
          Break;
        end;
      end;
    end;

    if (ms <> '') then
    begin
      Text := CleanString(ms);
      SelStart := Length(s);
      SelLength := Length(Text) - SelStart;
    end;
  end;
end;

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

procedure TTMSFMXEdit.SetPrefix(const Value: string);
begin
  FPrefix := Value;
end;

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

procedure TTMSFMXEdit.SetSuffix(const Value: string);
begin
  FSuffix := Value;
end;

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

procedure TTMSFMXEdit.SetModified(const Value: Boolean);
begin
  if csLoading in ComponentState then
    Exit;

  if ReadOnly then
    Exit;

  FIsModified := value;
end;

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

function TTMSFMXEdit.DecimalPos: Integer;
var
  i: Integer;
begin
  i := Pos(FormatSettings.decimalseparator, self.text);
  if (i = 0) then Result := Length(fprefix) + Length(self.text) + Length(fSuffix) + 1
  else Result := Length(fPrefix) + i;
end;

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

procedure TTMSFMXEdit.AutoSeparators;
var
  s, si, neg: string;
  d: Double;
  Diffl, OldSelStart, OldPrec: Integer;

begin
  s := self.Text;
  Diffl := Length(s);

  OldSelStart := SelStart;

  if (s = '') then
    Exit;

  if (Pos('-', s) = 1) then
  begin
    Delete(s, 1, 1);
    neg := '-';
  end
  else
    neg := '';

  if (Pos(FormatSettings.DecimalSeparator, s) > 0) then
    s := Copy(s, Pos(FormatSettings.DecimalSeparator, s), 255)
  else
    s := '';

  d := Trunc(Abs(self.FloatValue));

  if FAutoThousandSeparator then
    si := Format('%n', [d])
  else
    si := Format('%f', [d]);

  si := Copy(si, 1, Pos(FormatSettings.decimalseparator, si) - 1);

  OldPrec := FPrecision;
  FPrecision := 0;

  FBlockChange := (Text <> FPrefix + neg + si + s + FSuffix);

  if FBlockChange then
    inherited Text := FPrefix + neg + si + s + fSuffix;

  FBlockChange := false;

  FPrecision := OldPrec;

  Diffl := Length(self.Text) - Diffl;

  SelStart := OldSelStart + Diffl;
  SelLength := 0;
end;

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

procedure TTMSFMXEdit.SetAutoThousandSeparator(const Value: Boolean);
begin
  FAutoThousandSeparator := Value;
  if FEditType in [etMoney, etFloat, etSignedFloat] then AutoSeparators;
end;

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

procedure TTMSFMXEdit.SetFloat(const Value: double);
var
  s:string;
begin
  case FEditType of
    etHex: self.Text := IntToHex(trunc(value), 0);
    etNumeric:
      if (FPrecision >= 0) then
        self.Text := Format('%.' + inttostr(FPrecision) + 'n', [value])
      else
        self.Text := Format('%g', [Value]);
    etFloat, etSignedFloat, etString:
      if (FPrecision >= 0) then
      begin
        s := Format('%.' + inttostr(FPrecision) + 'f', [value]);
        self.Text := s;
      end
      else
        self.Text := Format('%g', [Value]);
    etMoney:
      begin
        if (FPrecision >= 0) then
          self.Text := Format('%.' + inttostr(FPrecision) + 'f', [value]) else self.Text := Format('%g', [Value]);
        AutoSeparators;
      end;
  end;
  SetModified(True);
end;

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

function TTMSFMXEdit.GetFloat: double;
var
  s: string;
  d: double;
  e: integer;
begin
  Result := 0;
  case FEditType of
    etHex: if self.Text <> '' then Result := HexToInt(self.Text);
    etString:
      begin
        val(self.Text, d, e);
        Result := d;
      end;
    etNumeric, etFloat, etSignedFloat:
      if (self.Text <> '') then
      begin
        s := self.Text;
        if (s = '-') then
          Result := 0
        else
          Result := EStrToFloat(s);
      end;
    etMoney:
      if self.Text <> '' then
      begin
        s := StripThousandSep(self.Text);
        if (Pos(FormatSettings.Decimalseparator, s) = Length(s)) then Delete(s, Pos(FormatSettings.DecimalSeparator, s), 1);
        if (s = '') or (s = '-') then Result := 0 else
          Result := EStrToFloat(s);
      end;
  end;
end;

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

procedure TTMSFMXEdit.SetPrecision(const Value: smallint);
var
  at: TAutoType;
begin
  if (FPrecision <> value) and (editType in [etFloat, etSignedFloat, etMoney, etString]) then
  begin
    FPrecision := Value;
    if (Text <> '') or (not AllowNumericNullValue) then
    begin
      at := IsType(self.text);
      if (at in [atFloat, atNumeric]) then
        FloatValue := FloatValue
      else
        FloatValue := 0.0;
    end;
  end;
end;

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

procedure TTMSFMXEdit.SetPrecisionDisplay(const Value: TPrecisionDisplay);
begin
  if (FPrecisionDisplay <> Value) then
  begin
    FPrecisionDisplay := Value;
    FloatValue := FloatValue;
  end;
end;

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

function TTMSFMXEdit.EStrToFloat(s: string): extended;
begin
  Result := 0;

  if Pos(FormatSettings.ThousandSeparator, s) > 0 then
    s := StripThousandSep(s);

  if (FPrecision > 0) and (Length(s) > FPrecision) then
    if s[Length(s) - FPrecision] = FormatSettings.Thousandseparator then
      s[Length(s) - FPrecision] := FormatSettings.Decimalseparator;
  try
    TryStrToFloat(s, Result);
  except
    Result := 0;
  end;
end;

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

function TTMSFMXEdit.GetInt: Integer;
begin
  Result := 0;
  case FEditType of
    etHex: if (self.Text <> '') then Result := HexToInt(self.Text);
    etSignedNumeric, etNumeric, etSignedFloat, etFloat: Result := ValStr(self.Text);
    etMoney: Result := ValStr(StripThousandSep(self.Text));
  end;
end;

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

procedure TTMSFMXEdit.SetInt(const Value: Integer);
begin
  case FEditType of
    etHex: self.Text := IntToHex(value, 0);
    etSignedNumeric, etNumeric, etSignedFloat, etFloat: self.Text := Inttostr(value);
    etMoney:
      begin
        self.Text := IntToStr(value);
        AutoSeparators;
      end;
  end;
  SetModified(True);
end;

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

{ TTMSFMXEditBtn }

procedure TTMSFMXEditBtn.ApplyStyle;
var
  b: TButton;
  I: Integer;
begin
  inherited;
  AssignEvents;
  b := Getbutton;
  if Assigned(b) then
  begin
    for I := 0 to b.ChildrenCount - 1 do
    begin
      if (b.Children[I] is TTMSFMXBitmap) then
      begin
        if b.Children[I].StyleName = 'buttonbitmap' then
          (b.Children[I] as TTMSFMXBitmap).Bitmap.Assign(Bitmap);
      end;
    end;
  end;
end;

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

function TTMSFMXEditBtn.GetButton: TButton;
begin
  Result := FindStyleResource('button') as TButton;
end;

function TTMSFMXEditBtn.GetButtonBitmap: TTMSFMXBitmap;
var
  b: TButton;
begin
  Result := nil;
  b := GetButton;
  if Assigned(b) then
    Result := FindStyleResource('buttonbitmap') as TTMSFMXBitmap;
end;

function TTMSFMXEditBtn.GetStyleObject: TFMXObject;
var
  B,C: TFmxObject;
  bm: TTMSFMXBitmap;
begin
  Result := inherited;

  B := nil;
  C := nil;
  if Assigned(Result) then
  begin
    B := Result.FindStyleResource('button');
    C := Result.FindStyleResource('content');
  end;

  if Assigned(Result) then
  begin
    if not Assigned(B) then
    begin
      FButton := TButton.Create(Self);
      FButton.CanFocus := False;
      FButton.StyleName := 'button';
      FButton.Width := 16;
      {$IFDEF DELPHIXE6_LVL}
      (C as TLayout).Align := TAlignLayout.Client;
      FButton.Align := TAlignLayout.Right;
      {$ELSE}
      (C as TLayout).Align := TAlignLayout.alClient;
      FButton.Align := TAlignLayout.alRight;
      {$ENDIF}

      bm := TTMSFMXBitmap.Create(FButton);
      bm.HitTest := False;
      bm.Margins.Left := 2;
      bm.Margins.Top := 2;
      bm.Margins.Right := 2;
      bm.Margins.Bottom := 2;
      bm.StyleName := 'buttonbitmap';
      {$IFDEF DELPHIXE6_LVL}
      bm.Align := TAlignLayout.Client;
      {$ELSE}
      bm.Align := TAlignLayout.alClient;
      {$ENDIF}
      FButton.InsertObject(FButton.ChildrenCount, bm);

      Result.InsertObject(Result.ChildrenCount, FButton);
    end
    else
      FButton := TButton(B);
  end;
end;

procedure TTMSFMXEditBtn.KeyDown(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
begin
  inherited;
  if Key = vkF4 then
  begin
    DropDown;
    Exit;
  end;
end;

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

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

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

procedure TTMSFMXEditBtn.PopupKeyDown(Sender: TObject; var Key: Word;
  var KeyChar: WideChar; Shift: TShiftState);
begin
  case Key of
    vkReturn:
    begin
      DropDown;
    end;
  end;
end;

procedure TTMSFMXEditBtn.SetBitmap(const Value: TBitmap);
begin
  FBitmap.Assign(Value);
  NeedStyleLookup;
  Repaint;
end;

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

procedure TTMSFMXEditBtn.AssignEvents;
var
  b: TControl;
begin
  b := FindStyleResource('button') as TControl;
  if Assigned(b) then
  begin
    b.OnClick := ButtonClick;
    b.OnDblClick := ButtonDblClick;
  end;
end;

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

procedure TTMSFMXEditBtn.ButtonClick(Sender: TObject);
begin
  if not FBlockDropDown then
    DropDown;

  FBlockDropDown := False;

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

procedure TTMSFMXEditBtn.ButtonDblClick(Sender: TObject);
begin
  FBlockDropDown := True;
end;

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

constructor TTMSFMXEditBtn.Create(AOwner: TComponent);
begin
  inherited;
  CanFocus := True;
  AutoCapture := True;
  FBitmap := TBitmap.Create(0, 0);
  FPopup := TTMSFMXEditPopup.Create(Self);
  FPopup.ParentControl := Self;
  FPopup.Transparent := True;
  FPopup.FEdit := Self;
  FPopup.StyleLookup := 'tmsfmxeditbtnpopupstyle';
  FPopup.PlacementTarget := Self;
  FPopup.StaysOpen := False;
  FPopup.Stored := False;
//  FPopup.Parent := Self;
  FPopup.Locked := True;
  FPopup.ShowActivated := True;
  FPopup.DragWithParent := True;
//  FPopup.DesignVisible := True;  RAID 283593
  FPopup.DesignVisible:= False;
  FPopup.Width := 240;
  FPopup.Height := 160;
  SetAcceptsControls(False);
end;

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

destructor TTMSFMXEditBtn.Destroy;
begin
  FBitmap.Free;
  FPopup.Free;
  inherited;
end;

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

procedure TTMSFMXEditBtn.DropDown;
begin
  if Assigned(PopupControl) then
  begin
    if not FPopup.IsOpen then
    begin
      FPopup.Width := PopupControl.Width;
      FPopup.Height := PopupControl.Height;
      {$IFDEF DELPHIXE6_LVL}
      FPopup.Placement := TPlacement.Bottom;
      PopupControl.Align := TAlignLayout.Client;
      {$ELSE}
      FPopup.Placement := TPlacement.plBottom;
      PopupControl.Align := TAlignLayout.alClient;
      {$ENDIF}
      PopupControl.Parent := FPopup;
      PopupControl.Visible := True;
      FPopup.IsOpen := True;
      PopupControl.SetFocus;
    end
    else
    begin
      FPopup.IsOpen := False;
      PopupControl.Visible := False;
      SetFocus;
    end;
  end;
end;

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

{ TLookupSettings }

procedure TLookupSettings.Assign(Source: TPersistent);
begin
  if (Source is TLookupSettings) then
  begin
    FAcceptOnTab := (Source as TLookupSettings).AcceptOnTab;
    FCaseSensitive := (Source as TLookupSettings).CaseSensitive;
    FColor := (Source as TLookupSettings).Color;
    FDisplayCount := (Source as TLookupSettings).DisplayCount;
    FDisplayList.Assign((Source as TLookupSettings).DisplayList);
    FEnabled := (Source as TLookupSettings).Enabled;
    FHistory := (Source as TLookupSettings).History;
    FNumChars := (Source as TLookupSettings).NumChars;
    FValueList.Assign((Source as TLookupSettings).ValueList);
    FMulti := (Source as TLookupSettings).Multi;
    FSeparator := (Source as TLookupSettings).Separator;
    FWidth := (Source as TLookupSettings).Width;
    FHeight := (Source as TLookupSettings).Height;
  end;
end;

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

constructor TLookupSettings.Create;
begin
  inherited Create;
  FWidth := 0;
  FHeight := 0;
  FDisplayList := TStringList.Create;
  FDisplayList.Sorted := true;
  FDisplayList.Duplicates :=  dupIgnore;
  FValueList := TStringList.Create;
  FColor := claWhite;
  FDisplayCount := 4;
  FNumChars := 2;
  FEnabled := False;
  FSeparator := ';';
end;

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

destructor TLookupSettings.Destroy;
begin
  FValueList.Free;
  FDisplayList.Free;
  inherited;
end;

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

procedure TLookupSettings.SetDisplayList(const Value: TStringList);
begin
  FDisplayList.Assign(Value);
end;

procedure TLookupSettings.SetHeight(const Value: Integer);
begin
  FHeight := Value;
end;

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

procedure TLookupSettings.SetNumChars(const Value: Integer);
begin
  if Value > 0 then
    FNumChars := Value
end;

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

procedure TLookupSettings.SetValueList(const Value: TStringList);
begin
  FValueList.Assign(Value);
end;

procedure TLookupSettings.SetWidth(const Value: Integer);
begin
  FWidth := Value;
end;

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

{$IFNDEF DELPHI_LLVM}
procedure TListWindow.SetActive(const Value: Boolean);
begin
  inherited;

  if not Active then
    Hide
  else
    if FListControl.CanFocus and not (csDestroying in FListControl.ComponentState) then
      FListControl.SetFocus;
end;
{$ENDIF}

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

{ TListBoxTab }

constructor TListBoxTab.Create(AOwner: TComponent);
begin
  inherited;
  FColor := claWhite;
end;

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

destructor TListBoxTab.Destroy;
begin

  inherited;
end;

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

function TListBoxTab.GetBackGround: TRectangle;
var
  C: TFmxObject;
begin
  Result := nil;
  C := FindStyleResource('background');
  if Assigned(C) and (C is TRectangle) then
    Result := C as TRectangle;
end;

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

procedure TListBoxTab.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
end;

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

procedure TListBoxTab.MouseMove(Shift: TShiftState; X, Y: Single);
begin
  inherited;

end;

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

procedure TListBoxTab.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  Y: Single);
begin
  inherited;
  if FMouseCaptured then
  begin
    ReleaseCapture;
    CloseLookup;
    FMouseCaptured := False;
  end;
end;

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

procedure TListBoxTab.ReleaseCaptureEx;
begin
  if FMouseCaptured then
  begin
    ReleaseCapture;
    FMouseCaptured := False;
  end;
end;

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

procedure TListBoxTab.SetCapture;
begin
  if FMouseCaptured then
    Exit;
  FMouseCaptured := True;
  Capture;
end;

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

procedure TListBoxTab.CloseLookup;
begin
  if Assigned(FEdit) then
    FEdit.CloseLookup;
end;

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

procedure TListBoxTab.SetColor(const Value: TAlphaColor);
begin
  if (FColor <> Value) then
  begin
    FColor := Value;
    UpdateBkgColor;
  end;
end;

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

procedure TListBoxTab.UpdateBkgColor;
var
  bg: TRectangle;
begin
  bg := GetBackGround;
  if Assigned(bg) then
    bg.Fill.Color := FColor;
end;

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

{ TTMSFMXEditPopup }

procedure TTMSFMXEditPopup.DialogKey(var Key: Word; Shift: TShiftState);
var
  b: Boolean;
begin
  b := Key = vkEscape;
  inherited;
  if b then
  begin
    if Assigned(FEdit) then
    begin
      if Assigned(FEdit.OnCloseWithEscape) then
        FEdit.OnCloseWithEscape(Self);
    end;
  end;
end;

initialization
  RegisterFmxClasses([TTMSFMXEdit, TTMSFMXEditBtn]);


end.
